首先,对格式化字符串漏洞的原理进行简单介绍。
格式化字符串函数可以接受可变数量的参数,并将第一个参数作为格式化字符串,根据其来解析之后的参数。通俗来说,格式化字符串函数就是将计算机内存中表示的数据转化为我们人类可读的字符串格式。几乎所有的 C/C++ 程序都会利用格式化字符串函数来输出信息,调试程序,或者处理字符串。一般来说,格式化字符串在利用的时候主要分为三个部分
这里我们给出一个简单的例子,其实相信大多数人都接触过 printf 函数之类的。之后我们再一个一个进行介绍。
常见的有格式化字符串函数有
函数 | 基本介绍 |
---|---|
printf | 输出到 stdout |
fprintf | 输出到指定 FILE 流 |
vprintf | 根据参数列表格式化输出到 stdout |
vfprintf | 根据参数列表格式化输出到指定 FILE 流 |
sprintf | 输出到字符串 |
snprintf | 输出指定字节数到字符串 |
vsprintf | 根据参数列表格式化输出到字符串 |
vsnprintf | 根据参数列表格式化输出指定字节到字符串 |
setproctitle | 设置 argv |
syslog | 输出日志 |
err, verr, warn, vwarn 等 | 。。。 |
这里我们了解一下格式化字符串的格式,其基本格式如下
每一种 pattern 的含义请具体参考维基百科的格式化字符串 。以下几个 pattern 中的对应选择需要重点关注
%
‘字面值,不接受任何 flags, width。就是相应的要输出的变量。
在一开始,我们就给出格式化字符串的基本介绍,这里再说一些比较细致的内容。我们上面说,格式化字符串函数是根据格式化字符串函数来进行解析的。那么相应的要被解析的参数的个数也自然是由这个格式化字符串所控制。比如说‘%s‘表明我们会输出一个字符串参数。
我们再继续以上面的为例子进行介绍
对于这样的例子,在进入 printf 函数的之前 (即还没有调用 printf),栈上的布局由高地址到低地址依次如下
注:这里我们假设 3.14 上面的值为某个未知的值。
在进入 printf 之后,函数首先获取第一个参数,一个一个读取其字符会遇到两种情况
那么假设,此时我们在编写程序时候,写成了下面的样子
此时我们可以发现我们并没有提供参数,那么程序会如何运行呢?程序照样会运行,会将栈上存储格式化字符串地址上面的三个变量分别解析为
对于 2,3 来说倒还无妨,但是对于对于 1 来说,如果提供了一个不可访问地址,比如 0,那么程序就会因此而崩溃。
这基本就是格式化字符串漏洞的基本原理了。
原文:https://www.cnblogs.com/FarmPick/p/10148474.html