“堆漏洞”是二进制安全中非常核心的一类漏洞,主要出现在程序动态分配内存(如 malloc/free)的管理环节。与栈漏洞相比,堆漏洞利用方式更灵活,但门槛也更高。
下面从原理、常见类型、利用手法、防御与学习路径几个方面来梳理。
1. 堆的基本概念
程序运行时,堆用于动态分配内存。以 Linux 下的 glibc 为例:
-
malloc:申请内存,返回用户指针。
-
free:释放内存。
-
chunk:glibc 管理堆的最小单位,包含用户数据和头部元数据(size、prev_size 等)。
-
bin:被释放的 chunk 会进入不同的空闲链表,如 fastbin、smallbin、unsortedbin、largebin、tcache。
堆漏洞的本质,通常是程序对堆内存的使用超出了预期边界,或破坏了堆管理器的元数据,从而让攻击者能控制程序执行流或读写任意内存。
2. 常见堆漏洞类型
2.1 堆溢出(Heap Overflow)
向堆块写入的数据超过其申请大小,覆盖相邻 chunk 的头部或数据。
-
可覆盖相邻 chunk 的 size 字段,制造 overlap。
-
可覆盖 fd/bk 指针,配合 unlink 实现任意地址写。
2.2 Use-After-Free(UAF)
释放后继续使用指针。
-
若释放的 chunk 被重新分配,原指针可操作新 chunk 的数据。
-
常用于劫持函数指针、篡改虚表等。
2.3 Double Free
同一块内存被释放两次。
-
会让同一个 chunk 进入 bin 两次,后续分配可返回两个相同指针。
-
结合 tcache/fastbin 可实现任意地址分配。
2.4 Off-by-One / Off-by-Null
写入时多写一个字节或少写一个字节。
-
常用来修改相邻 chunk 的 size 字段的最低字节,制造 chunk overlap。
-
经典如 poison null byte。
2.5 Unlink 攻击
利用 free 时对相邻空闲 chunk 的合并操作。
-
伪造 fd/bk 指针,使 unlink 宏执行时把任意地址写入目标。
-
现代 glibc 有检查:fd->bk == p && bk->fd == p。
2.6 House of 系列
针对不同 glibc 版本和 bin 机制的利用技巧:
-
House of Spirit
-
House of Force
-
House of Einherjar
-
House of Orange
-
House of Lore
-
House of Rabbit
-
House of Husk 等
2.7 Tcache 相关漏洞
glibc 2.26 引入 tcache 后,出现大量新利用方式:
-
tcache poisoning:篡改 next 指针,实现任意地址分配。
-
tcache dup:double free 绕过检查。
-
tcache stashing unlink attack 等。
3. 典型利用流程
一个完整的堆利用通常包括:
信息泄露:泄露 libc 地址、堆地址、栈地址。
控制堆布局:通过申请/释放塑造 chunk 分布。
破坏元数据:溢出、UAF、double free 等。
任意地址写:篡改 fd/bk/next,让 malloc 返回目标地址。
劫持执行流:改写 __free_hook、__malloc_hook、exit 函数指针、虚表等。
获取 shell:写入 /bin/sh 或 one_gadget。
4. 防御机制
现代系统有多层防护:
-
ASLR:地址随机化。
-
PIE:代码段随机化。
-
NX:不可执行栈/堆。
-
Canary:栈保护。
-
Safe Unlink:检查 fd/bk。
-
tcache 保护:double free 检查、key 字段。
-
Checksec:常用工具查看二进制启用的保护。
绕过这些保护正是堆利用的核心挑战。


