【学习记录】Week13(四):stdout/stderr 独立攻击面深度剖析与全周总结 📅 2026/7/4 16:07:04 写在前面欢迎来到 Week13 的收官之战在前三篇中我们系统梳理了_IO_FILE的结构布局、调用链、任意读写原语构造以及结合exit机制的各路劫持手法。这些技术大多围绕“伪造一个全新的 FILE 结构体并挂入_IO_list_all”展开。然而在实战中libc 数据段里已经躺着三个现成的 FILE 结构体stdin,stdout,stderr。今天我们将聚焦如何将这些现有流作为独立攻击面并探讨当程序无法正常退出时如何利用堆报错强行触发 IO 流刷新。最后我们将对本周的 IO_FILE 理论体系进行全面总结。 目录独立攻击面为什么是stdout和stderrstdout半盲劫持无_IO_list_all修改的泄露与执行stderr与malloc_printerr的联动强制触发 FSOPglibc IO 防御机制演进图谱 (2.23 ~ 2.35)Week 13 全局总结与下期预告1. 独立攻击面为什么是stdout和stderr伪造 FILE 结构体并劫持_IO_list_all虽然威力巨大但往往需要一次“任意地址写堆地址”的原语如 Largebin Attack 或 House of Corrosion。在某些题目限制下我们可能只拥有“任意地址写固定值”或“部分溢出”的能力。此时直接篡改 libc 中的stdout(_IO_2_1_stdout_) 或stderr(_IO_2_1_stderr_) 就成了更优解。位置固定它们都在 libc 数据段地址可通过 Libc 基址直接推算。已在链表中它们天然挂在_IO_list_all的头部无需修改链表指针只需破坏其内部字段即可。高频调用程序只要执行puts或printf就会直接访问stdout只要触发堆错误就会访问stderr。2.stdout半盲劫持无_IO_list_all修改的泄露与执行2.1 经典信息泄露 (glibc 2.23~2.27)在早期版本中stdout的_flags通常是0xfbad2887。其内部指针的排布通常是_IO_write_base_IO_write_ptr_IO_write_end。如果存在单字节溢出Off-by-one或部分写漏洞将_IO_write_base的最低位覆盖为\x00会导致_IO_write_base向前扩展覆盖到 libc 数据段中前面的内存通常是_IO_2_1_stdin_的内容或函数指针。当程序下次调用printf(...)时glibc 会把从_IO_write_base到_IO_write_ptr之间的所有数据当作缓冲区输出从而无脑泄露 Libc 内部地址。2.2 执行流劫持 (结合 House of Apple)在高版本2.34中我们依然可以打stdout。通过 Tcache Poisoning 或其他任意写手段直接分配到stdout所在的内存。不修改_chain也不修改_IO_list_all直接把stdout的vtable改为_IO_wfile_jumps。伪造其_wide_data指针指向堆块并在堆上布置system或 ROP 链。当程序下一次执行puts或退出时对stdout调用overflow直接触发 House of Apple 链。优势这种手法极其隐蔽不需要大动干戈地修改全局指针只在局部做文章。3.stderr与malloc_printerr的联动强制触发 FSOP在 CTF 中经常遇到一种尴尬的情况我们已经布置好了伪造的 FILE 结构体并劫持了_IO_list_all但程序既没有调用exit也没有return而是卡在一个死循环里。FSOP 无法触发怎么办答案是自己创造错误利用 glibc 的报错机制3.1malloc_printerr调用链当 glibc 的堆管理器检测到堆元数据被破坏如double free、size校验失败、unlink检查失败等会调用malloc_printerr打印错误信息并终止程序。其内部调用链大致为malloc_printerr-__libc_message-abort-_IO_flush_all_lockp3.2 实战应用主动触发崩溃我们利用漏洞劫持_IO_list_all布置好 House of Apple 链。我们故意破坏堆块的size字段或者制造一个必定会被 glibc 检测到的 Double Free。下次调用malloc或free时glibc 触发malloc_printerr。在abort阶段glibc 会尝试调用stderr打印报错信息。如果我们劫持了stderr直接触发执行即使没劫持stderr_IO_flush_all_lockp也会被调用遍历被我们篡改的_IO_list_all触发伪造结构的overflow。核心思维在堆漏洞利用中“触发崩溃”往往不是终点而是我们主动按下的一枚“引爆按钮”。4. glibc IO 防御机制演进图谱 (2.23 ~ 2.35)为了方便大家记忆和对照我们将本周涉及的所有防御机制演进与绕过思路总结为下图glibc 演进与攻防博弈伪造任意 vtable滥用合法 vtable 内部逻辑转向嵌套结构体盲区结合 Largebin/Corrosion 劫持 _IO_list_all结合 exit/tls_dtor_list 多路径House of Orange (直接 system)glibc 2.23: 无校验时代glibc 2.24: vtable 范围检查House of Pig (_IO_str_jumps)glibc 2.29~2.31: 封堵 str_overflow, Tcache KeyHouse of Apple (_IO_wfile_jumps)glibc 2.34: 移除 Hook, 强化 PTR_MANGLE现代 FSOP 标准链glibc 2.35: 检查内部指针一致性综合复合利用5. Week 13 全局总结5.1 核心知识点回顾本周我们彻底解剖了_IO_FILE机制这是现代 PWN 的灵魂。核心要点如下结构即攻击面_IO_FILE中的_chain构成链表_IO_buf_base/end控制缓冲区vtable实现多态。每一部分都能被转化为漏洞原语链表劫持、任意读写、控制流转移。触发路径多样化FSOP 不再只依赖exitmalloc_printerr造成的abort是更强大的强制触发点。绕过思维的根本转变从“伪造非法数据骗过系统”转变为“使用合法数据结构合法 vtable通过操纵其内部复杂的逻辑跳板将控制流导向未校验的角落如_wide_vtable”。复合利用现代题目往往是“堆漏洞制造原语 (Botcake) - 全局指针劫持 - IO 结构体伪造 - 强制触发”的流水线作业。5.2 进阶建议源码追踪用 GDB 跟踪一次_IO_wfile_overflow的完整执行流观察_wide_data是如何被解引用的。偏移记忆熟记stdout在常见 libc 版本中的偏移以及_IO_FILE_plus中vtable(0xd8) 和_chain(0x68) 的偏移这是实战中拼手速的关键。结语IO_FILE 的学习曲线是陡峭的但它的回报也是丰厚的。掌握了它你就掌握了在 glibc 2.34 无 Hook 时代生存的王牌。当你能熟练地在脑中构建出_IO_FILE_plus的立体结构并清晰地看到数据流如何通过虚表流转时所有的防护机制在你眼中都将只是透明的壁垒。