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 的偏移量。

操作步骤:

  1. 预先分配特定大小的堆块 A
  2. 释放 A
  3. 通过 UAF 修改 A 的内容
  4. 重新分配 A,完成目标地址的修改

3.2 Transplant 原语(数据转移)

用于将源地址的数据转移到目标地址:

  1. 预先分配两个大小相同(dst_size)的堆块 A 和 B
  2. 释放 B 和 A
  3. 通过 UAF 部分改写 A 的 fd 指针指向自身(类似 double free)
  4. 重新分配 A 并篡改其 size 为 src_size
  5. 释放 A
  6. 恢复 A 的 size 为 dst_size
  7. 再次分配 dst_size,完成数据转移

计算公式:

src_size = ((src_addr - fastbinY) * 2) + 0x20
dst_size = ((dst_addr - fastbinY) * 2) + 0x20

4. glibc 2.27 利用步骤

4.1 堆风水准备

  1. 释放一个 chunk 到 large bin
  2. 通过 UAF 篡改 size 中的 NO_MAIN_ARENA 标志位为 1

4.2 unsortedbin attack 修改 global_max_fast

  1. 释放一个 chunk 到 unsortedbin
  2. 修改其 bk 指针指向 global_max_fast - 0x10
  3. 重新分配该 chunk,完成 global_max_fast 的修改

4.3 伪造 unsortedbin chunk

  1. 释放特定大小的堆块,使其将堆地址写入 global_max_fast
  2. 伪造 chunk 的 size 字段,确保 NON_MAIN_ARENA 置为 1
  3. 确保 bk 指向可写区域

4.4 篡改 stderr 结构体

  1. 使用 Transplant 原语将 libc 地址(如 __default_morecore)转移到 stderr->_IO_buf_end
  2. 使用写原语修改 _IO_buf_base,使 _IO_buf_base + _IO_buf_end = one_gadget
  3. _flags 置为 0(绕过 _IO_str_overflow 检查)
  4. _IO_write_ptr 置为 0x7fffffff(确保 _IO_write_ptr - _IO_write_base > _IO_buf_base + _IO_buf_end
  5. 将 stdout 的 _mode 置为 0(防止干扰)
  6. stderr+0xe0(stdout 的 _flags 位置)写为 call rax gadget
  7. 部分覆写 vtable 使其指向 _IO_str_jumps - 0x10

4.5 再次修改 global_max_fast

global_max_fast 改为合适的大小

4.6 触发 shell

  1. 分配一个 size 大于 global_max_fast 的 chunk
  2. 当 unsortedbin 放入 largebin 时,会检查 NON_MAIN_ARENA 标志位
  3. 触发断言,调用 stderr
  4. 最终调用 _IO_str_overflow() 并执行我们的 gadget 获取 shell

5. glibc 2.29 利用(条件苛刻)

5.1 修改 global_max_fast

使用 tcache dup 替代 unsortedbin attack:

  1. 分配三个 chunk A(0x10), B(0x10), C(0x420)
  2. 释放 C 使其进入 largebin
  3. 释放 B 和 A
  4. 通过 UAF 修改 A 的 fd 指向 C
  5. 修改 C 的 fd 指向 global_max_fast
  6. 重新分配完成修改

5.2 修改 stderr 结构体

  1. 将 vtable 覆盖为堆地址
  2. _flags 改为 "/bin/sh"
  3. 使用 Transplant 将 DW.ref.__gcc_personality_v0 的 libc 地址转移到伪造的 vtable 的 sync 位置
  4. 部分覆写该地址为 gadget(如 add rsi, r8; jmp rsi

5.3 关闭 libio vtable 保护

  1. 通过 free fastbin chunk 往 _rtld_global._dl_nns 填入堆地址
  2. 使用 Transplant 将 _rtld_global._dl_ns[0]._ns_loaded 移到 _rtld_global._dl_ns[1]._ns_loaded
  3. _rtld_global._dl_ns[0]._ns_loaded 置为 0
  4. 修改 libc 的 link_map 中 l_ns 为 1
  5. 调整 l_addr 使 l_addr + wcpcpy offset + 0x26 = system_addr

5.4 触发 shell

  1. 准备 NON_MAIN_ARENA 置为 1 的 unsortedbin
  2. 当其落入 largebin 时触发 assert
  3. 调用 __sync 位置的 gadget(add rsi, r8; jmp rsi
  4. 此时 rsi 为 system 地址,rdi 为 _flags 地址("/bin/sh")

6. 关键点总结

  1. 必须能够控制 global_max_fast 的值
  2. 需要精确计算堆块大小与目标地址的关系
  3. stderr 结构体的篡改是关键
  4. glibc 2.29 需要知道 libc 和 ld.so 之间的偏移
  5. 需要能够分配和释放大尺寸堆块
  6. 利用过程涉及多次堆风水操作

7. 防御措施

  1. 及时修复 UAF 漏洞
  2. 使用最新版本的 glibc
  3. 启用所有安全机制(ASLR, RELRO, Stack Canary 等)
  4. 限制堆分配的大小和频率

该技术展示了如何通过精心构造的堆操作来绕过现代 glibc 的保护机制,虽然条件较为苛刻,但为堆利用提供了新的思路。

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 后,可以按以下公式计算特定大小的堆块来修改目标地址: 其中 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,完成数据转移 计算公式: 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 的保护机制,虽然条件较为苛刻,但为堆利用提供了新的思路。