House-of-Corrosion 一种新的堆利用技巧
字数 2776 2025-08-24 20:49:22
House-of-Corrosion 堆利用技术详解
1. 技术概述
House-of-Corrosion 是一种针对 glibc 2.27 和 glibc 2.29 的堆利用技术,主要利用 UAF 漏洞和堆风水技术,通过修改 global_max_fast 变量并篡改 stderr 文件流对象来获取 shell。
2. 前提条件
- 需要一个 UAF 漏洞
- 可以分配较大的堆块(size <= 0x3b00)
- 不需要任何内存泄露
- 需要爆破 4bit 地址(ASLR 情况下)
3. 核心原语
3.1 任意写原语
通过 unsortedbin attack 修改 global_max_fast 后,可以按以下公式计算特定大小的堆块来修改目标地址:
chunk_size = (delta * 2) + 0x20
其中 delta 是目标地址与 fastbinY 的偏移量。
操作步骤:
- 预先分配特定大小的堆块 A
- 释放 A
- 通过 UAF 修改 A 的内容
- 重新分配 A,完成目标地址的修改
3.2 Transplant 原语(数据转移)
用于将源地址的数据转移到目标地址:
- 预先分配两个大小相同(dst_size)的堆块 A 和 B
- 释放 B 和 A
- 通过 UAF 部分改写 A 的 fd 指针指向自身(类似 double free)
- 重新分配 A 并篡改其 size 为 src_size
- 释放 A
- 恢复 A 的 size 为 dst_size
- 再次分配 dst_size,完成数据转移
计算公式:
src_size = ((src_addr - fastbinY) * 2) + 0x20
dst_size = ((dst_addr - fastbinY) * 2) + 0x20
4. glibc 2.27 利用步骤
4.1 堆风水准备
- 释放一个 chunk 到 large bin
- 通过 UAF 篡改 size 中的
NO_MAIN_ARENA标志位为 1
4.2 unsortedbin attack 修改 global_max_fast
- 释放一个 chunk 到 unsortedbin
- 修改其 bk 指针指向
global_max_fast - 0x10 - 重新分配该 chunk,完成
global_max_fast的修改
4.3 伪造 unsortedbin chunk
- 释放特定大小的堆块,使其将堆地址写入
global_max_fast - 伪造 chunk 的 size 字段,确保
NON_MAIN_ARENA置为 1 - 确保 bk 指向可写区域
4.4 篡改 stderr 结构体
- 使用 Transplant 原语将 libc 地址(如
__default_morecore)转移到stderr->_IO_buf_end - 使用写原语修改
_IO_buf_base,使_IO_buf_base + _IO_buf_end = one_gadget - 将
_flags置为 0(绕过_IO_str_overflow检查) - 将
_IO_write_ptr置为 0x7fffffff(确保_IO_write_ptr - _IO_write_base > _IO_buf_base + _IO_buf_end) - 将 stdout 的
_mode置为 0(防止干扰) - 将
stderr+0xe0(stdout 的_flags位置)写为 call rax gadget - 部分覆写 vtable 使其指向
_IO_str_jumps - 0x10
4.5 再次修改 global_max_fast
将 global_max_fast 改为合适的大小
4.6 触发 shell
- 分配一个 size 大于
global_max_fast的 chunk - 当 unsortedbin 放入 largebin 时,会检查
NON_MAIN_ARENA标志位 - 触发断言,调用 stderr
- 最终调用
_IO_str_overflow()并执行我们的 gadget 获取 shell
5. glibc 2.29 利用(条件苛刻)
5.1 修改 global_max_fast
使用 tcache dup 替代 unsortedbin attack:
- 分配三个 chunk A(0x10), B(0x10), C(0x420)
- 释放 C 使其进入 largebin
- 释放 B 和 A
- 通过 UAF 修改 A 的 fd 指向 C
- 修改 C 的 fd 指向
global_max_fast - 重新分配完成修改
5.2 修改 stderr 结构体
- 将 vtable 覆盖为堆地址
- 将
_flags改为 "/bin/sh" - 使用 Transplant 将
DW.ref.__gcc_personality_v0的 libc 地址转移到伪造的 vtable 的 sync 位置 - 部分覆写该地址为 gadget(如
add rsi, r8; jmp rsi)
5.3 关闭 libio vtable 保护
- 通过 free fastbin chunk 往
_rtld_global._dl_nns填入堆地址 - 使用 Transplant 将
_rtld_global._dl_ns[0]._ns_loaded移到_rtld_global._dl_ns[1]._ns_loaded - 将
_rtld_global._dl_ns[0]._ns_loaded置为 0 - 修改 libc 的 link_map 中
l_ns为 1 - 调整
l_addr使l_addr + wcpcpy offset + 0x26 = system_addr
5.4 触发 shell
- 准备
NON_MAIN_ARENA置为 1 的 unsortedbin - 当其落入 largebin 时触发 assert
- 调用
__sync位置的 gadget(add rsi, r8; jmp rsi) - 此时 rsi 为 system 地址,rdi 为
_flags地址("/bin/sh")
6. 关键点总结
- 必须能够控制
global_max_fast的值 - 需要精确计算堆块大小与目标地址的关系
- stderr 结构体的篡改是关键
- glibc 2.29 需要知道 libc 和 ld.so 之间的偏移
- 需要能够分配和释放大尺寸堆块
- 利用过程涉及多次堆风水操作
7. 防御措施
- 及时修复 UAF 漏洞
- 使用最新版本的 glibc
- 启用所有安全机制(ASLR, RELRO, Stack Canary 等)
- 限制堆分配的大小和频率
该技术展示了如何通过精心构造的堆操作来绕过现代 glibc 的保护机制,虽然条件较为苛刻,但为堆利用提供了新的思路。