【学习记录】Week12(二):House of Pig——Tcache 与 FSOP 的终极复合利用
写在前面在上一篇中我们掌握了 House of Botcake成功在现代 glibc (2.29) 中制造了堆重叠并实现了 Tcache Poisoning。然而在 glibc 2.34 中官方彻底移除了__free_hook和__malloc_hook。这就意味着即便我们通过 Tcache Poisoning 拿到了任意地址写的原语也面临着“写到哪、写什么才能劫持控制流”的困境。今天我们将学习现代 CTF 中的另一大杀器——House of Pig。它将 Tcache 的分配能力与 IO_FILE (FSOP) 完美融合在无 Hook 的时代为我们撕开通往 Shell 的突破口。 目录时代背景Hook 没落与 FSOP 的崛起核心跳板_IO_str_overflow源码剖析House of Pig 利用流程与数学推导实战进阶与 House of Botcake 的经典组合拳总结与下篇预告1. 时代背景Hook 没落与 FSOP 的崛起在 glibc 2.34 之前堆漏洞利用的终点往往是覆盖__free_hook为system然后free(/bin/sh)。随着 Hook 被移除攻击者必须寻找新的“控制流劫持点”。此时FSOP (File Stream Oriented Programming)成为了主流。FSOP 的核心是伪造IO_FILE_plus结构体并通过某种方式触发 glibc 的 IO 流刷新机制如调用exit()或触发malloc_printerr。在刷新过程中glibc 会遍历_IO_list_all链表调用每个 FILE 结构体虚表中的overflow函数。然而glibc 2.24 引入了 vtable 地址范围检查我们不能再随意伪造一个虚表指针指向栈上的 shellcode。我们必须寻找合法 vtable中的危险函数。House of Pig 利用的正是_IO_str_jumps这个合法虚表中的_IO_str_overflow函数。2. 核心跳板_IO_str_overflow源码剖析_IO_str_overflow是 glibc 中处理字符串流缓冲区溢出的函数。当写入的数据超过当前缓冲区大小时它会尝试分配一个更大的缓冲区。其核心 C 代码逻辑简化后如下int _IO_str_overflow (_IO_FILE *fp, int c) { int flush_only c EOF; size_t pos; if (fp-_flags _IO_NO_WRITES) // 必须可写 return flush_only ? 0 : EOF; // 计算当前写入位置 pos fp-_IO_write_ptr - fp-_IO_write_base; if (pos (size_t) (_IO_blen (fp) flush_only)) { if (fp-_flags _IO_USER_BUF) /* not allowed to enlarge */ return EOF; else { // 核心逻辑分配新缓冲区 char *new_buf; char *old_buf fp-_IO_buf_base; // 旧缓冲区 size_t old_blen _IO_blen (fp); // 旧缓冲区长度 // 计算新长度通常是 old_blen * 2 100 size_t new_size 2 * old_blen 100; // 【漏洞点 1】调用 malloc大小受我们控制 new_buf malloc (new_size); if (new_buf NULL) { // 分配失败的处理... } // 复制旧数据到新缓冲区 memcpy (new_buf, old_buf, old_blen); // 【漏洞点 2】调用 free参数受我们控制 if (old_buf ! NULL) free (old_buf); // 更新 FILE 结构体的指针... } } // ... }关键发现在这个函数中malloc的参数new_size由_IO_buf_end - _IO_buf_base计算得出而free的参数old_buf就是_IO_buf_base。如果我们能伪造一个IO_FILE_plus结构体并让其vtable指向_IO_str_jumps我们就能控制malloc的大小和free的参数3. House of Pig 利用流程与数学推导House of Pig 的核心思想是利用 Tcache Poisoning 劫持malloc的分配流使得_IO_str_overflow中的malloc(new_size)返回我们想要的目标地址如栈地址或_IO_list_all本身然后再利用free(old_buf)触发执行。3.1 构造伪造的 FILE 结构体为了顺利走到malloc和free我们需要精心设置IO_FILE_plus的字段_flags: 设置为0(可写且绕过_IO_USER_BUF检查)。_IO_write_base: 设置为0。_IO_write_ptr: 设置为1(保证pos _IO_blen(fp)触发扩容逻辑)。_IO_buf_base: 设置为/bin/sh\x00的地址(作为free的参数如果是覆盖__free_hook时代的变体则用此法在无 Hook 时代通常设为旧缓冲区地址配合栈迁移)。_IO_buf_end: 设置为0。这样old_blen 0 - _IO_buf_basenew_size 2 * (-_IO_buf_base) 100。vtable: 指向_IO_str_jumps。3.2 结合 Tcache 的劫持逻辑假设我们已经通过 House of Botcake 拿到了 Tcache Poisoning 能力并且计算出了new_size落在某个 Tcache bin 的范围内例如 0x60。我们在堆上伪造上述的IO_FILE_plus结构体。利用 Tcache Poisoning将 Tcache[0x60] 的next指针指向目标地址例如存放返回地址的栈地址。利用 Largebin Attack 或 Tcache Poisoning 将_IO_list_all覆盖为我们伪造的堆块地址。调用exit(0)触发_IO_flush_all_lockp。glibc 遍历到我们伪造的 FILE 结构体调用_IO_str_overflow。函数内部执行malloc(new_size)。因为 Tcache[0x60] 被投毒这次malloc直接返回了栈地址接着执行memcpy(new_buf, old_buf, old_blen)。我们将精心构造的 ROP 链作为old_buf长度作为old_blen直接将 ROP 链写入了栈上随后函数返回栈展开直接执行我们的 ROP 链如system(/bin/sh)或setcontext。准备: House of Botcake 获取 Tcache 投毒能力伪造 IO_FILE_plus 结构体vtable_IO_str_jumps计算 new_size 并投毒对应 Tcache bin使 next 指向栈地址劫持 _IO_list_all 指向伪造结构体触发 exit 触发 FSOP调用 _IO_str_overflowmalloc new_size从 Tcache 取回栈地址memcpy 将 ROP 链写入栈地址函数返回执行 ROP 链Getshell4. 实战进阶与 House of Botcake 的经典组合拳在真实的 glibc 2.31~2.35 CTF 题目中House of Botcake 和 House of Pig 往往是相伴而生的。一个完整的利用链如下漏洞触发利用 UAF 或 Off-by-one。堆重叠制造使用House of Botcake让 chunk A 同时在 Unsorted Bin 和 Tcache 中。信息泄露通过 Unsorted Bin 中的 A 泄露 Libc 基址通过 Tcache 中的 A 泄露堆地址用于绕过 Safe-Linking。原语获取修改 A 的 Tcachenext指针实现任意地址分配Tcache Poisoning。结构体伪造利用任意地址分配在堆上布置伪造的IO_FILE_plus结构体。劫持与触发 (House of Pig)再次利用 Tcache Poisoning 修改_IO_list_all或利用 Unsorted Bin Attack 修改_IO_list_all。调用exit触发 FSOP。控制流劫持_IO_str_overflow内部的malloc取回栈地址memcpy写入 ROP 链完成栈迁移或直接执行system。这套组合拳完美规避了 Double Free 检查、Safe-Linking 以及 Hook 移除带来的障碍是现代堆利用的“标准答案”。5. 总结与下篇预告5.1 核心知识点总结FSOP 是现代替代方案在__free_hook被移除后伪造 IO_FILE 结构体并触发overflow是劫持控制流的核心路径。_IO_str_overflow是跳板这个合法虚表函数内部包含malloc和free调用其参数受 FILE 结构体字段控制。Tcache 与 FSOP 的结合House of Pig 的精髓在于利用 Tcache Poisoning 控制_IO_str_overflow中malloc的返回地址将数据如 ROP 链精准写入目标地址如栈帧实现“任意地址写任意值”并触发执行。5.2 下篇预告在掌握了 Botcake 和 Pig 这两大现代基石后下一篇我们将转向另一个高频考点House of Corrosionglobal_max_fast 利用。我们将探讨如何通过破坏global_max_fast这个全局变量将原本只能管理小块内存的 Fastbin 机制扩展到全尺寸从而实现对更大范围内存如stdout或_IO_list_all的强力劫持。结语House of Pig 展示了现代漏洞利用的典型范式——不再追求单点突破而是将多种机制Tcache 的分配能力 IO_FILE 的执行路径串联起来形成一条从内存破坏到代码执行的完整通道。掌握这种“组合思维”是进阶高阶 PWN 选手的关键。