【学习记录】Week12(一):House of Botcake——glibc 2.29+ 时代的堆重叠王者
写在前面:在 glibc 2.29 版本中,官方为 Tcache 引入了
key字段,用于检测并阻止经典的 Double Free 攻击。这一改动曾让许多习惯于利用 Tcache Double Free 制造堆重叠的选手极不适应。然而,攻防博弈从未停止,House of Botcake应运而生。它巧妙地利用了 Tcache 与 Unsorted Bin 之间的状态差异,在不破坏任何校验机制的前提下,完美实现了 Double Free 并制造堆重叠,成为了现代 CTF (glibc 2.29 ~ 2.35) 必考的“基础设施”级技术。
📑 目录
- 背景回顾:Tcache Key 机制与 Double Free 的困局
- 核心破局:House of Botcake 的状态错位思想
- 详细的堆布局与利用流程推演
- 实战进阶:结合 Safe-Linking 的完整利用链
- 总结与下篇预告
1. 背景回顾:Tcache Key 机制与 Double Free 的困局
在 glibc 2.27 中,Tcache 没有任何 Double Free 检查,攻击者可以连续两次free同一个 chunk,使其在 Tcache 链表中形成环(A -> A),随后两次malloc即可实现任意地址写。
为了修补这个漏洞,glibc 2.29 在tcache_entry结构体中新增了key字段:
typedef struct tcache_entry { struct tcache_entry *next; struct tcache_perthread_struct *key; // 新增字段 } tcache_entry;当一个 chunk 被放入 Tcache 时,堆管理器会在key字段写入一个特定的值(当前 Tcache 结构体的地址)。在执行free时,如果发现该 chunk 的key等于这个特定值,就会遍历当前的 Tcache 链表检查是否已存在该 chunk,如果存在则触发free(): double free detected in tcache 2报错。
这一机制直接封死了在 Tcache 中的直接 Double Free。
2. 核心破局:House of Botcake 的状态错位思想
House of Botcake 的核心思想非常精妙:如果一个 chunk 不在 Tcache 里,而在 Unsorted Bin 里,那么它的key字段就没有被设置。此时我们再次free它,它就会顺利进入 Tcache!
这样一来,这个 chunk 就同时存在于 Unsorted Bin(双向链表)和 Tcache(单链表)中。
- 在 Unsorted Bin 中:它的
fd和bk指针指向main_arena中的地址,我们可以利用这一点泄露 Libc 基址。 - 在 Tcache 中:我们可以通过 UAF 修改它的
next指针,实现Tcache Poisoning,进而申请到任意地址。
这就是 House of Botcake 的本质:利用不同 Bin 机制的盲区,实现状态的“双栖”。
3. 详细的堆布局与利用流程推演
假设题目环境为 glibc 2.31,存在 UAF 漏洞,且我们目标是利用大小为0x100的 chunk 制造重叠。
步骤一:填满 Tcache 并送入 Unsorted Bin
- 分配 9 个大小为
0x100的 chunk:A, B, C, D, E, F, G, H, I。(I用于防止后续释放时与 Top Chunk 合并)。 - 依次释放
A到G。此时 Tcache[0x100]被填满(7个)。 - 释放
H。因为 Tcache 已满,H进入Unsorted Bin。- 此时
H的fd和bk指向main_arena+96。 - 关键点:
H的key字段没有被设置!
- 此时
步骤二:清空 Tcache 制造错位
- 连续调用 7 次
malloc(0xf8),将 Tcache[0x100]清空。 - 再次释放
G。此时 Tcache[0x100] 为空,G顺利进入Tcache。
步骤三:触发 Double Free(核心操作)
- 再次释放
H。- glibc 检查
H的key字段,发现它并不等于 Tcache 结构体地址(因为它之前进的是 Unsorted Bin)。 - 检查通过!
H被放入Tcache。 - 此时,
H同时存在于 Unsorted Bin 和 Tcache 中! - Tcache 链表状态:
head -> H -> G -> NULL
- glibc 检查
步骤四:信息泄露与投毒
- 泄露 Libc:利用 UAF 漏洞读取
H的fd指针。由于H同时在 Unsorted Bin,它的fd指针存储着main_arena的地址。成功计算出 Libc 基址。 - Tcache Poisoning:
- 计算目标地址(如
__free_hook)。 - 利用 UAF 向
H写入数据,覆盖其在 Tcache 中的next指针为target_addr。 - *(注:如果 glibc >= 2.32,需结合 Safe-Linking 机制,用泄露的堆地址异或目标地址)*。
- 计算目标地址(如
步骤五:分配获取目标地址
malloc(0xf8)-> 返回H。malloc(0xf8)-> 返回G。malloc(0xf8)->返回target_addr!实现任意地址写。
1. 填满 Tcache, 释放 H 进 Unsorted Bin
H 无 key
2. 清空 Tcache
3. 释放 G 进 Tcache
4. 再次释放 H
绕过 key 检查, H 进 Tcache
5. H 同时在 Tcache 和 Unsorted Bin
6. UAF 读 H 的 fd
泄露 Libc
7. UAF 写 H 的 next 指针
实施 Tcache Poisoning
8. 连续 Malloc 取出 H 和目标地址
4. 实战进阶:结合 Safe-Linking 的完整利用链
在 glibc 2.32+ 环境中,Tcache 引入了 Safe-Linking 机制,next指针变成了(chunk_addr >> 12) ^ next_ptr。House of Botcake 如何与之适配?
- 泄露堆地址:在步骤 4 中,当我们向 Tcache 释放
G时,H(此时还在 Unsorted Bin)的fd指针被改写为 Tcache 加密后的G的地址。通过 UAF 读取这个值,由于此时H是链表头,next_ptr为 0,所以加密后的值就是(H_addr >> 12) ^ 0 = H_addr >> 12。我们成功泄露了堆地址的 key! - 加密目标地址:在步骤 8 中,我们写入的
next不能再是裸的target_addr,而必须是(H_addr >> 12) ^ target_addr。
由此可见,House of Botcake 不仅能泄露 Libc,还能顺带泄露堆地址,简直是专为绕过现代防护量身定制的利器。
5. 总结与下篇预告
5.1 核心知识点总结
- 本质:利用 Unsorted Bin 中的 chunk 缺失 Tcache
key字段的特性,进行跨 Bin 的 Double Free。 - 效果:让一个 chunk 同时存在于 Tcache 和 Unsorted Bin,实现 Libc 泄露与 Tcache Poisoning 的双管齐下。
- 地位:在 glibc 2.29+ 环境中,只要存在 UAF 且无其他严苛限制,House of Botcake 几乎是制造堆重叠的首选方案。
5.2 下篇预告
在掌握了 House of Botcake 制造重叠和泄露的能力后,下一篇我们将迎来本周的重头戏之一(1.5天重点):House of Pig。
在 glibc 2.34 移除__free_hook等传统劫持点后,House of Pig 将教我们如何将 Tcache Poisoning 与 IO_FILE (FSOP) 完美结合,在失去 Hook 的时代依然能稳定 Getshell。
结语:House of Botcake 完美诠释了“魔高一尺,道高一丈”的另一面——防御机制的缝隙总是存在的。掌握它,你就拿到了通往现代堆利用大门的钥匙。
