格式化字符串漏洞
Format String Vulnerability Principle
格式化字符串函数介绍
格式化字符串函数可以接受可变数量的参数,并将第一个参数作为格式化字符串,根据其来解析之后的参数。通俗来说,格式化字符串函数就是将计算机内存中表示的数据转化为我们人类可读的字符串格式。几乎所有的C/C++程序都会利用格式化字符串函数来输出信息,调试程序,或者处理字符串。一般来说,格式化字符串在利用的时候主要分为三个部分:
- 格式化字符串函数
- 格式化字符串
- 后续参数
格式化字符串函数
常见的格式化字符串函数有
-
输入:
- scanf
-
输出
函数 | 基本介绍 |
---|---|
printf | 输出到 stdout |
fprintf | 输出到指定 FILE 流 |
vprintf | 根据参数列表格式化输出到 stdout |
vfprintf | 根据参数列表格式化输出到指定 FILE 流 |
sprintf | 输出到字符串 |
snprintf | 输出指定字节数到字符串 |
vsprintf | 根据参数列表格式化输出到字符串 |
vsnprintf | 根据参数列表格式化输出指定字节到字符串 |
setproctitle | 设置 argv |
syslog | 输出日志 |
err, verr, warn, vwarn 等 | ... |
格式化字符串
格式化字符串基本格式如下:
%[parameter][flags][field width][.precision][length]type
主要关注:
-
parameter
- n$,获取格式化字符串中的指定参数
-
type
- d/i:有符号整数
- u:无符号整数
- x/X:16进制unsigned int。x使用小写字母;X使用大写字母。如果指定了精度,则输出的数字不足时在左侧补0.默认精度为1。精度0且值为0,则输出为空。
- o:8进制unsigned int。如果指定了精度,则输出的数字不足时在左侧补0.默认精度为1。精度为0且值为0,则输出为空。
- s:如果没有用l标志,输出num结尾字符串直到精度规定的上限;如果没有指定精度,则输出所有字节。如果用了l标志,则对应函数参数指向wchar_t型数组,输出时把每个宽字符转化为多字节字符,相当于wcrtomb函数。
- c:如果没有l标志,把int参数转为unsigned char型输出;如果用了l标志,把wint_t参数转为包含两个元素的wchart_t数组,其中第一个元素包含要输出的字符,第二个元素为null宽字符。
- p:void*型,输出对应变量的值。printf("%p",a)用地址的格式打印变量a的值,printf("%p",&a)打印变量a所在的地址。
- n:不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
- %:'%'字面值,不接受任何flags,width
参数
就是相应的要输出的变量。
格式化字符串漏洞原理
格式化字符串函数是根据格式化字符串来进行解析的。那么相应的要被解析的参数的个数也是自然是由这个格式化字符串所控制的。
举个例子
在进入printf函数之前(就还没有调用printf),栈上的布局由高地址到地址一次如下:
some value
3.14
123456
addr of "red"
addr of format string:Color %s,Number %d,Float %4.2f"
再进入printf之后,函数首先获取第一个参数,一个个读取其字符会遇到两种情况
- 当前字符不是%,直接输出到相应标准输出。
- 当前字符是%,继续读取下一个字符。
- 如果没有字符,报错
- 如果下一个字符是%,输出%
- 否则根据相应的字符,获取相应的参数,对其进行解析并输出。
那么假设,此时我们在编写程序时候,写成了下面的样子:
printf("Color %s, Number %d, Float %4.2f");
此时我们可以发现我们并没有提供参数,那么程序会如何运行呢?程序照样会运行,会将栈上存储格式化字符串地址上面的三个变量分别解析为
- 解析其地址对应的字符串
- 解析其内容对应的整形值
- 解析其内容对应的浮点值
对于2,3来说倒还无妨,但是对于1来说,如果提供一个不可访问地址,比如说0,那么程序就会因此而奔溃。
Format String Exploit
上面,我们展示了格式化字符串漏洞的两个利用手段
- 使程序崩溃,因为%s对应的参数地址不合法的概率比较大。
- 查看进程内容,根据%d,%f输出了栈上的内容。
程序崩溃
通常来说,利用格式化字符串漏洞使得程序崩溃是最为简单的利用反那个是,因为我们只需要输入若干个%s即可。这是因为栈上不可能每个值都对应了合法的地址,所以总是会有某个地址可以使得程序崩溃。这一利用,虽然攻击者本身似乎并不能控制程序,但是这样却可以照成程序不可用。比如说,如果远程服务有一个格式化字符串漏洞,那么我们就可以攻击其可用性,使服务崩溃,进而使得用户不能够访问。
泄露内存
利用格式化字符串漏洞,我们还可以获取我们所想要输出的内容。一般会有如下几种操作。
- 泄露栈内存
- 获取某个变量的值
- 获取某个变量对应地址的内存
- 泄露任意地址内存
- 利用GOT表得到libc函数地址,进而获取libc,进而获取其他libc函数地址。
- 盲打,dump整个程序,获取有用信息。
当然格式化字符串漏洞还有很多内容,但是发现wiki上都掌握了,就懒得写了,想要学习的小伙伴可以上wiki
内容来源
CTF Wiki Format String Vulnerability Principle
CTF Wiki Format String Exploit