【学习记录】Week11(二):House of 系列精讲—— 无 free 时代的破局与堆块合并的艺术

📅 2026/7/3 23:29:04
【学习记录】Week11(二):House of 系列精讲—— 无 free 时代的破局与堆块合并的艺术
写在前面在上一篇中我们学习了 House of Spirit、Force 和 Lore掌握了伪造堆块、劫持 Top Chunk 和 Smallbin 的基本法。今天我们将挑战 House 系列中更具实战意义、难度也更高的三种技术House of Einherjar、House of Rabbit 与 House of Orange。这些技术常常出现在 glibc 2.23-2.27 的高质量 CTF 题目中特别是当题目限制了free函数或者只提供了极小范围的溢出时它们将是破局的关键。 目录House of Einherjar断链重生的 Off-by-null 合并术House of Rabbit移花接木的 Fastbin 合并术House of Orange无 free 时代的 IO_FILE 劫持鼻祖总结与 Week 11 展望1. House of Einherjar断链重生的 Off-by-null 合并术1.1 漏洞原理House of Einherjar 的核心在于利用Off-by-null单字节 NULL 溢出强行触发堆块的后向合并。在 glibc 中当一个 chunk 被释放且非 fastbin/tcache 大小时堆管理器会检查其前一个 chunk通过PREV_INUSE位判断。如果PREV_INUSE位为 0说明前一个 chunk 处于空闲状态glibc 会将当前 chunk 与前一个 chunk合并以减少内存碎片。如果我们能通过溢出将目标 chunk 的size字段最低位覆盖为\x00清零PREV_INUSE位并伪造目标 chunk 的prev_size字段我们就能欺骗 glibc让它以为目标 chunk 的前面相距prev_size字节处存在一个巨大的、空闲的 chunk。随后触发freeglibc 就会执行 Unlink 操作将这个伪造的“前一个 chunk”从链表中摘除并合并。1.2 利用条件Off-by-null 漏洞能覆盖相邻 chunk 的size最低字节为\x00。可控的 prev_size能伪造目标 chunk 的prev_size使得target_addr - prev_size指向我们控制的 Fake Chunk。绕过 Unlink 检查伪造的 Fake Chunk 必须满足FD-bk P BK-fd P已知指针绕过法。触发 free能够释放目标 chunk。1.3 利用流程与数学布局假设我们要在地址 A 处伪造一个 Fake Chunk已知全局指针ptr指向 A。目标 chunk B 紧随其后。构造 Fake Chunk (A)设置size为一个不被 fastbin/tcache 管理的大小如 0x90设置fd ptr - 0x18bk ptr - 0x10。计算 prev_size假设 B 的地址为addr_B。我们需要设置 B 的prev_size addr_B - addr_A。触发 Off-by-null溢出覆盖 B 的size最低字节为\x00。释放 Bglibc 检查PREV_INUSE为 0读取prev_size找到 Fake Chunk A。对 A 执行 Unlink合并 A 和 B。结果ptr被修改为ptr - 0x18合并后的大 chunk 包含了 B 的区域造成堆重叠后续可利用 Tcache Poisoning 等手法获取 Shell。1. 在已知指针处构造 Fake Chunk满足 Unlink 检查2. 计算偏移设置目标 Chunk B 的 prev_size3. 触发 Off-by-null覆盖 B 的 size 低位为 \x004. 释放 Chunk B5. glibc 触发后向合并对 Fake Chunk 执行 Unlink6. 合并产生堆重叠全局指针被篡改7. 任意地址写 - Getshell2. House of Rabbit移花接木的 Fastbin 合并术2.1 漏洞原理House of Rabbit 利用的是 glibc 在将 fastbin 中的 chunk 放入 unsorted bin 时的malloc_consolidate机制。当程序申请一个较大的内存大于 fastbin 最大值时或者调用malloc_trim时glibc 会遍历 fastbin 链表将里面的 chunk 逐个取出进行相邻 chunk 的合并并放入 unsorted bin。合并逻辑会检查 fastbin chunk 的前后相邻 chunk 是否空闲。如果空闲则合并。如果我们通过漏洞如堆溢出修改了 fastbin 链表中某个 chunk 的size字段使其变小或者修改了其相邻 chunk 的prev_size就能在malloc_consolidate过程中制造出堆块重叠。2.2 利用场景这种技术通常用于绕过 fastbin 大小限制或在无 free 的情况下制造重叠。典型利用步骤缩小 size 法分配三个连续的 chunk A (0x31), B (0x31), C (0x91)。释放 A 进入 fastbin。利用溢出修改 A 的size从0x31变为0x21。触发malloc_consolidate例如申请一个 large chunk。glibc 取出 A (size0x21)认为它的下一个 chunk 是A_addr 0x20这正好落在了原本属于 B 的头部如果 B 此时处于 inuse 状态glibc 会认为合并失败将 A(0x21) 放入 unsorted bin。但此时A 和 B 在逻辑上产生了错位。后续如果再次分配可以从 unsorted bin 切割出 A而 C 的区域依然存在造成严重重叠。2.3 限制与注意高版本 glibc 对 fastbin 的size检查变严修改 size 必须保证仍在 fastbin 范围内且对齐。需要精确控制相邻 chunk 的状态防止在 consolidate 时触发corrupted size vs. prev_size的崩溃。3. House of Orange无 free 时代的 IO_FILE 劫持鼻祖3.1 漏洞原理House of Orange 是堆漏洞利用史上的里程碑。它解决了两个难题程序没有 free 函数如何释放堆块以及如何在没有控制函数指针的情况下劫持控制流第一阶段无 free 释放堆块 (Top Chunk 劫持)当程序没有 free 时我们只能通过malloc来操作堆。如果通过溢出修改 Top Chunk 的 size使其变得很小但必须满足对齐和剩余空间大于 MINSIZE 等条件下一次申请一个大于该 size 的内存时glibc 会认为 Top Chunk 不够用触发sysmalloc。sysmalloc会将旧的 Top Chunk 放入 Unsorted Bin相当于执行了 free并向操作系统申请新的页作为新的 Top Chunk。这样我们就成功在无 free 的情况下获得了 Unsorted Bin 中的 chunk可以用来泄露 libc 地址。第二阶段FSOP (File Stream Oriented Programming) 劫持控制流在 glibc 2.23 中当malloc遇到错误如堆块校验失败时会调用malloc_printerr-__libc_message-abort-_IO_flush_all_lockp。这个刷新 IO 流的操作会遍历_IO_list_all链表。如果我们能通过 Unsorted Bin Attack 将_IO_list_all修改为main_arena 88即 Unsorted Bin 的链表头并将一个伪造的 IO_FILE 结构体链接进去当程序再次触发 malloc 错误时就会调用我们伪造的 IO_FILE 里的虚表函数通常是overflow函数从而劫持控制流执行system(/bin/sh)。3.2 利用流程图flowchart TD subgraph 第一阶段泄露与伪造 A[1. 溢出修改 Top Chunk size] -- B[2. malloc 触发 sysmalloc] B -- C[3. 旧 Top Chunk 进入 Unsorted Bin] C -- D[4. 泄露 Libc 地址与堆地址] D -- E[5. 在 Unsorted Bin 构造 Fake IO_FILE] end subgraph 第二阶段触发与劫持 F[6. Unsorted Bin Attackbr覆盖 _IO_list_all main_arena88] -- G[7. 故意触发 malloc 错误br如破坏链表] G -- H[8. 调用 abort - _IO_flush_all_lockp] H -- I[9. 遍历到 Fake IO_FILE] I -- J[10. 调用伪造的 vtable-__overflow] J -- K[11. 执行 system(/bin/sh)] end E -- F3.3 现代演变在 glibc 2.24 中引入了_IO_str_jumps虚表检查和对 IO_FILE vtable 的范围限制传统的 House of Orange 失效。但其核心思想演变出了House of Orange 2.0 (利用_IO_str_overflow)、House of Roman等利用 IO_FILE 结构的新技术。理解 Orange 是掌握所有高版本 IO_FILE 攻击的基础。4. 总结与 Week 11 展望4.1 核心知识点总结House of Einherjar利用 Off-by-null 清除PREV_INUSE位伪造prev_size触发后向合并制造堆重叠并绕过 Unlink 检查。House of Rabbit利用malloc_consolidate合并 fastbin 的机制通过修改 fastbin chunk 的 size 制造错位重叠。House of Orange无 free 环境下通过破坏 Top Chunk 触发sysmalloc释放堆块结合 Unsorted Bin Attack 劫持_IO_list_all利用 FSOP 调用伪造虚表获取 Shell。4.2 防御与缓解glibc 2.29引入了严格的prev_size一致性检查使得 Einherjar 的触发条件变得极其苛刻。glibc 2.24引入了 vtable 范围检查封堵了原版 House of Orange 的虚表劫持。安全编码杜绝 NULL 字节溢出特别是strncpy的误用严格控制堆溢出边界。4.3 展望本周我们深入探讨了 House of 系列的经典技术。从中我们可以看到堆漏洞利用的本质是“通过破坏堆元数据欺骗堆管理器执行非预期的操作”。随着 glibc 防御机制的升级这些老技术或被修补或演化出新的绕过姿势。在未来的学习中我们将遇到更多结合 IO_FILE、Tcache Stashing Unlink 等新型攻击链。最终结论House of 系列是堆漏洞利用的艺术结晶。掌握它们不仅能让你在面对古董级 CTF 题目时游刃有余更能让你深刻理解 ptmalloc2 的底层架构为研究高版本堆利用打下坚实的理论基础。参考文献The Malloc Maleficarum - Phantasmal PhantasmagoriaCTF Wiki - Heap Exploitation: House of Einherjar Orangeglibc malloc.c 源码分析