写在前面:在 Week11 的前两篇中,我们复盘了传统的 House of 系列(Spirit, Force, Lore, Einherjar, Rabbit, Orange)。随着 glibc 版本的迭代,传统的技术或被修补,或难以适配现代环境。今天,我们正式进入现代 CTF 高频考点的硬核环节。根据最新的 Week11 规划,本篇将用最高优先级、最大的篇幅,深度剖析House of Botcake与House of Pig这两项在现代堆漏洞利用中出场率极高的技术。
📑 目录
- 现代堆利用背景:为什么是 Botcake 和 Pig?
- House of Botcake:glibc 2.29+ 的重叠制造机
- House of Pig:Tcache 与 FSOP 的终极复合利用
- 综合实战思维:从 UAF 到 Shell 的现代路径
- 总结与下篇预告
1. 现代堆利用背景:为什么是 Botcake 和 Pig?
在 glibc 2.29 引入 Tcache Key 遏制了简单的 Double Free,并在后续版本中加强了prev_size一致性检查与 Safe-Linking 机制后,传统的利用链遭遇了毁灭性打击。现代 CTF PWN 题(特别是 glibc 2.31~2.35 环境)的解题思路发生了根本性转变:
- 如何制造堆重叠?->House of Botcake成为了绝对的主流答案。它巧妙地利用了 Tcache 和 Unsorted Bin 的释放顺序差异,在不破坏任何元数据校验的前提下,完美制造堆块重叠。
- 如何劫持控制流?-> 纯堆上的 Hook(如
__free_hook)在 glibc 2.34 中被彻底移除。于是,攻击重心转移到了IO_FILE (FSOP)。House of Pig作为 Tcache 与 FSOP 的集大成者,成为了获取 Shell 的王牌技术。
2. House of Botcake:glibc 2.29+ 的重叠制造机
2.1 核心思想
House of Botcake 的目标是让同一个 chunk 同时存在于 Tcache 和 Unsorted Bin 中。一旦达成这个目标,我们就可以通过 Unsorted Bin 中的指针泄露 Libc 地址,同时通过 Tcache 修改该 chunk 的next指针,实现 Tcache Poisoning。
2.2 利用条件
- 存在 UAF 漏洞,或者能够对已释放的 chunk 进行读写。
- 能够控制 chunk 的分配和释放顺序。
- Tcache 未满(通常需要 7 个 chunk 来填满一个 Tcache bin)。
2.3 详细利用步骤
假设我们要利用大小为0x100的 chunk。
阶段一:填满 Tcache 并释放进 Unsorted Bin
- 分配 9 个大小为
0x100的 chunk:A, B, C, D, E, F, G, H, I(I用于防止与 Top Chunk 合并)。 - 依次释放
A到G,填满 Tcache[0x100]。 - 释放
H。因为 Tcache 已满,H进入Unsorted Bin。此时H的fd和bk指向main_arena。
阶段二:制造双重存在
4. 分配一个 chunk(或调用malloc7 次),清空 Tcache[0x100]。
5. 再次释放G。此时 Tcache[0x100] 为空,G进入Tcache。
6.关键步骤:再次释放H。此时 Tcache[0x100] 已经有一个G,且未满。H会进入Tcache。
* *等等,glibc 2.29 不是有 Key 检查吗?*
*这就是 Botcake 的精妙之处:H之前是在 Unsorted Bin 中的,它的内存区域并没有被写入 Tcache Key(Key 是在进入 Tcache 时写入的)。当我们再次free(H)时,glibc 检查H->key,发现它不等于 Tcache 结构体地址,因此绕过了 Double Free 检查!
* 此时,H既在 Tcache 链表中(Tcache -> H -> G),又在 Unsorted Bin 中(H仍然与前面的 chunk 连在一起,或者就是独立在 Unsorted Bin)。
阶段三:泄露与投毒
7.泄露 Libc:利用 UAF 读取H的fd指针(此时在 Unsorted Bin 状态下指向main_arena),计算出 Libc 基址。
8.Tcache Poisoning:利用 UAF 修改H在 Tcache 中的next指针,将其指向目标地址(如__free_hook或栈上的返回地址)。
9. 连续malloc两次:第一次返回H,第二次返回目标地址,实现任意地址写。
1. 填满 Tcache, 释放 H 进 Unsorted Bin
2. 清空 Tcache
3. 释放 G 进 Tcache
4. 再次释放 H
由于 H 无 Key, 绕过 Double Free 检查
H 进入 Tcache
5. 此时 H 同时在 Tcache 和 Unsorted Bin
6. UAF 读取 H 的 fd 泄露 Libc
7. UAF 修改 H 的 Tcache next 指针
8. 连续 Malloc 两次, 获取目标地址控制权
3. House of Pig:Tcache 与 FSOP 的终极复合利用
3.1 核心思想
House of Pig 是针对 glibc 2.34+ 移除 Hook 函数后的利用难题而设计的。它结合了Tcache Poisoning和IO_FILE (FSOP)技术。
其核心在于:利用 Tcache Poisoning 劫持分配流,将一个伪造的IO_FILE_plus结构体分配到_IO_list_all附近或可控内存中,然后通过触发exit()或malloc_printerr,执行 IO 流刷新操作,最终调用伪造的 IO 虚表(通常是_IO_str_jumps或_IO_wstrn_jumps)中的函数,实现控制流劫持(如system("/bin/sh")或栈迁移 ROP)。
3.2 利用条件
- 拥有 Tcache Poisoning 能力(通常通过 House of Botcake 或 UAF 实现)。
- 能够泄露 Libc 地址和堆地址(用于伪造 IO 结构和计算 Tcache 异或密钥)。
- 程序存在正常的退出路径(
exit或return)或能触发堆错误。
3.3 详细利用步骤
阶段一:前置原语准备
通过 House of Botcake 或其他手段,实现:
- 泄露 Libc 基址。
- 泄露堆地址(用于绕过 glibc 2.32+ 的 Safe-Linking)。
- 拥有 Tcache Poisoning 能力,可以申请任意地址。
阶段二:伪造 IO_FILE 结构
在堆上布置一个伪造的IO_FILE_plus结构体。关键字段设置如下:
_flags: 设置为0或特定值以绕过检查。_IO_read_ptr,_IO_read_end等:通常置零。_IO_write_base: 设置为0。_IO_write_ptr: 设置为1(使得_IO_write_ptr > _IO_write_base,触发overflow逻辑)。_chain: 指向下一个 IO 结构(如果是单结构可置零)。vtable: 指向_IO_str_jumps或_IO_wstrn_jumps(合法 vtable,绕过 vtable 范围检查)。- 关键利用点:在
_IO_str_overflow函数中,存在如下逻辑:
// 伪代码 new_buf = malloc(new_size); memcpy(new_buf, old_buf, old_len); free(old_buf);我们可以通过控制_IO_buf_base(old_buf) 和_IO_buf_end来控制new_size和old_buf。
阶段三:劫持 _IO_list_all 与触发
- 利用 Tcache Poisoning,将
malloc的目标指向_IO_list_all(或利用 Largebin Attack 等将伪造的堆块地址写入_IO_list_all)。 - 修改
_IO_list_all的值,使其指向我们在堆上伪造的IO_FILE_plus结构体。 - 触发 FSOP:调用
exit()。exit会调用_IO_flush_all_lockp,遍历_IO_list_all链表,对每个 FILE 结构调用overflow。 - 由于我们伪造的
vtable是_IO_str_jumps,最终会调用_IO_str_overflow。 - 在
_IO_str_overflow内部,会执行malloc和free。此时,我们已经控制了 Tcache,可以让这里的malloc返回栈地址,然后通过memcpy将 ROP 链写入栈上,或者直接利用system("/bin/sh")完成利用。
House of Pig 的变体:在最新的 CTF 中,由于
_IO_str_overflow的检查也变严,衍生出了利用_IO_wfile_overflow的House of Apple系列和利用_IO_obstack_jumps的技术,但其核心架构(Tcache 劫持 + 伪造 IO 结构 + 触发刷新)依然是 House of Pig 奠定的。
4. 综合实战思维:从 UAF 到 Shell 的现代路径
在现代 CTF 中,一道题往往是多种技术的组合。以下是一个典型的现代堆题解题思维流:
漏洞点: UAF / Off-by-one
利用 House of Botcake
制造堆重叠
信息泄露
Libc Base & Heap Base
绕过 Safe-Linking
构造 Tcache Poisoning
控制流劫持
由于无 Hook, 转向 IO_FILE
利用 House of Pig
伪造 IO_FILE + 劫持 _IO_list_all
触发 exit/abort
执行伪造虚表
Getshell / ROP
这个流程图完美概括了目前 2.31~2.35 版本 glibc 堆题的通用解法。Botcake 负责“破局”制造原语,Pig(或 Apple)负责“收网”拿下控制流。
5. 总结与下篇预告
5.1 核心知识点总结
- House of Botcake:利用 Unsorted Bin 中的 chunk 没有 Tcache Key 的特性,二次释放使其同时存在于 Tcache 和 Unsorted Bin,完美绕过 glibc 2.29 的 Double Free 检查,是制造堆重叠的首选。
- House of Pig:在 glibc 移除 Hook 的背景下,将 Tcache Poisoning 与 FSOP 结合。通过伪造
IO_FILE结构和合法 vtable,利用exit触发 IO 流刷新,在_IO_str_overflow等函数中劫持执行流。 - 技术组合:现代堆题不再是单一技术的单打独斗,而是“堆管理机制破坏 (Botcake) -> 信息泄露 -> 数据结构伪造 (Pig/IO_FILE)”的复合攻击。
5.2 下篇预告
在下一篇 Week11(四)中,我们将继续探索现代 House 系列的其他重要成员:
- House of Apple:针对
_IO_wfile_overflow的新一代 FSOP 技术。 - House of Tangerine:2023 年高频出现的利用手法。
- House of Corrosion:利用
global_max_fast扩展 Fastbin 范围的强力攻击。 - 综合练习:UAF + tcache poisoning 实战分析。
最终结论:House of Botcake 和 House of Pig 是现代 CTF 选手必须熟练掌握的“左右互搏”术。前者是矛,撕开现代 glibc 的严密防线获取原语;后者是盾,在失去传统劫持点后提供可靠的控制流接管方案。掌握它们,你就掌握了现代堆利用的命脉。