这道题挺奇特,不过 pwn 题奇奇怪怪的多得是……
截取一段程序输出:
is currently stored in a stack variable, and you will have to figure out how to copy it into the .bss.
There are two options: do a leak (using one printf) followed by a write (using a second printf), or use a dynamic padding size, using the * format character, in combination with %n, in a single printf, to copy memory. Since this level only gives you a single printf() call, you will likely need to use the latter. Check the printf man page (in category 3:man 3 printf) for documentation on *.
丢给豆包翻译了下:
该值目前存储在一个栈变量中,你需要设法将其复制到.bss 段中。
有两种实现思路:一是先通过一次 printf 完成数据泄露(leak),再通过第二次 printf 完成数据写入(write);二是利用 * 格式符指定动态填充长度,结合 %n 格式符,在单次 printf 调用中完成内存数据的复制。由于本关卡仅提供一次 printf () 调用机会,你大概率需要采用后一种方法。可查阅 printf 的 3 号手册页(执行命令:man 3 printf),获取关于 * 格式符的官方说明。
这就牵扯到 printf 的 * 格式符。
printf 中的 *
在 printf 这一类格式化输出函数里,* 的含义是:该字段宽度/精度不在格式字符串里写死,而是从参数列表里再取一个 int。
宽度
|
|
可见 printf 从第第二个参数:5 取出来作宽度,然后将 20 写入%*d,,可见输出确实宽度就是 5。
精度
|
|

可见此时精度为 5,且进行了四舍五入。
位置参数
|
|
以上两者是等价的。
在 printf 中,可以通过位置参数 n$ 择取栈上的参数用作宽度标识符。
对于 pwn 来说,可以通过 * 获取填充量并配合 %n 写入地址。
当然还可以用 .* 来控制 %s 读取的长度防止崩溃。
wp
pwn.college 的 Level6.0,题目描述:
Use a format string exploit to copy a value and overwrite a global variable
查看保护:
|
|
和 level5.0 一样。
题目输出上文已经讲过,本题实际就是利用 * 将获取栈上的随机值,通过修改宽度的长度写入到 .bss 地址。
上 ida:
直接进 func()
|
|
v3 为 64 位整形数组。
|
|
这里往 v3[67] 写入 长度为 3 字节的随机数。
|
|
向 v3[21] + 6 写数据,注意这里有偏移量,后续要通过这个偏移量计算随机数位置。
|
|
这里也很显然随机数存放在 v3[67] 里。
|
|
所以这里说明我们要写入 .bss 的地址为 0x404128 。
首先找出 printf 的偏移量:

|
|
这里表明偏移量在 28 位置,而且我们前面有填充了 2 字节,这和前面 read 的时候 +6 有关。
这里可以认为我们填充完写入位置在 v3[22] 且偏移量为 28,而随机在 v3[67] 处。
由 67-22+28=73 得到随机数在偏移量为 73 的位置。
所以我们可以写出 “%*73c” 来获得宽度为随机数的字符串,将该长度写入 0x404128 即可达到目的。
后面补上 “%N$n” 这里 N 还要计算,原本偏移量为 28 要加上 2 个填充量,N 应该是两位数目前有:
|
|
长度为 13 再加上,5 个填充值长度为 18,去掉 2 个填充量。最后可得地址偏移量应该为 28 + 16/8 = 30.
所以 payload 应该为:
|
|
完整 exp:
|
|
