PWN学习之路 - 字符串格式化漏洞总结
一、函数原型
int printf(const char format,…)*
常规用法
1 | char str[10]; |
另外一种用法
1 | char str[10]; |
第一种用法没有错误,但是在第二种方法中没有规定format参数,用户便可以通过构造str来实现内存的任意读写。
printf函数的格式化字符串常见的有
%d,%f,%c,%s,%x(16进制数,没有0x),%p(16进制数,有0x)等。
%n ,它的功能是将%n之前打印出来的字符个数,赋值给一个变量。
%hn,%hhn,%lln,分别为写入目标空间2字节,1字节,8字节。
注意是对应参数(这个参数是指针)的对应的地址开始起几个字节。不要觉得%lln,取的是8个字节的指针,%n取的就是4个字节的指针,取的是多少字节的指针只跟程序的位数有关,如果是32位的程序,%n取的就是4字节指针,64位取的就是8字节指针,这是因为不同位数的程序,每个参数对应的字节数是不同的。
二、原理分析
以一段代码为例
1 | #include<stdio.h> |
![运行结果](/2019/04/06/PWN%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF-%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%BC%8F%E6%B4%9E%E6%80%BB%E7%BB%93/1.png)
第七个参数就是AAAA在栈中的位置
![输入前](/2019/04/06/PWN%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF-%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%BC%8F%E6%B4%9E%E6%80%BB%E7%BB%93/before.png)
![输入后](/2019/04/06/PWN%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF-%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%BC%8F%E6%B4%9E%E6%80%BB%E7%BB%93/behind.png)
PS:
linux下直接读取第七个参数的方法:
%< number >$x 是直接读取第number个位置的参数,同样可以用在%n,%d等等。
但是需要注意64位程序,前6个参数是存在寄存器中的,从第7个参数开始才会出现在栈中,所以栈中从格式化串开始的第一个,应该是%7$n**。
三、读取内存
输入的字符串的前4个字节如果是一个有效的字符串的首地址,就可以用%s将其打印出来,做到任意内存读取。如果不是有效的字符串,会出现段错误。
四、修改内存
局部变量保存在栈中
前提需要关闭PIE保护,不然局部变量的地址是随机的。
例如前四个字节地址在0xABCDEFGH
构造字符串如下
1 | `printf "\xGH\xEF\xCD\xAB"`%08x%08x%08x%08x%08x%08x%08n |
五、实战
CGfsb
在IDA中源码如下
![源码](/2019/04/06/PWN%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF-%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%BC%8F%E6%B4%9E%E6%80%BB%E7%BB%93/ori.png)
确定偏移量
![确定偏移量](/2019/04/06/PWN%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF-%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%BC%8F%E6%B4%9E%E6%80%BB%E7%BB%93/offset.png)
payload
![payload](/2019/04/06/PWN%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF-%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%BC%8F%E6%B4%9E%E6%80%BB%E7%BB%93/payload.png)
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 F11st's Cyber Journey!