本文从格式化字符串函数,到格式化字符串漏洞原理,再到格式化漏洞利用包括(获取栈变量数值、获取栈变量对应字符串、泄露任意地址内存、覆盖栈内存)方面与大家分享,不足之处请各位大佬指正,最开始在wiki上面学的格式字符串,里面的源码就直接用了wiki上面的例子,加一些调试等等
0x00格式化字符串(函数):
格式化字符串函数为接受可变数量的参数,并将第一个参数作为格式化字符串,根据其来解析之后的参数。格式化字符串函数就是将计算机内存中表示的数据转化为我们人类可读的字符串格式。格式化字符串在利用的时候主要分为
格式化字符串函数和格式化字符串
常见的有格式化字符串函数有输入函数(scanf),输出函数(printf、fprintf。通常会使用printf([格式化字符串],参数)的形式来进行调用,
printf函数的格式化字符串常见的有:
%d/i,有符号整数
%u,无符号整数
%x/X,16进制unsigned int
%o,8进制unsigned int
%c用来输出一个字符。
%s用来输出一个字符串。
%x表示以十六进制数形式输出整数。(%x(输出16进制数,前面没有0x),%p(输出16进制数,前面带有0x))
%p, void *型,输出对应变量的值。printf("%p",a)用地址的格式打印变量a的值,printf("%p", &a)打印变量a所在的地址。
%n,不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
比较常见的格式化字符串漏洞模式:
形如:
char str[50];
scarf("%s",str);
printf(str)
0x01 格式化字符串漏洞原理
格式化字符串函数是根据格式化字符串函数来进行解析的。相应的要被解析的参数的个数也由这个格式化字符串所控制。
一般使用大概框架是这样的
char str[50];
scanf("%s",str);
printf("%s",str);
但是有些人可能写成了下面这种形式
char str[50];
scarf("%s",str);
printf(str);
printf函数族的设计:当第一个参数可被控制时,攻击者将有机会对任意内存地址进行读写操作。所以为了不让攻击者有机可乘,不能printf中的format字符串的操纵权交给用户。因此要保证printf函数的第一个参数是不可变的,。
printf函数执行之前并不知道参数个数,它的内部有个指针,用来索检格式化字符串。对于特定类型%,就去取相应参数的值,直到索检到格式化字符串结束。
在进入printf之后,函数首先获取第一个参数,一个一个读取其字符会遇到两种情况
当前字符不是%,直接输出到相应标准输出。
当前字符是%, 继续读取下一个字符
如果没有字符,报错
如果下一个字符是%,输出%
否则根据相应的字符,获取相应的参数,对其进行解析并输出
当我们没有提供参数时,程序会将栈上存储格式化字符串地址按照所输入的解析:
char str[50];
scarf("%s",str);
printf(str);
尽管没有参数,上面的代码也会将string 后面的内存当做参数以解析其地址对应的字符串输出。这样就会造成攻击者有机会对任意内存地址进行读写操作
0x02 格式化字符串漏洞利用
利用格式化字符串漏洞,还可以获取所想要输出的内容。一般会有如下几种操作
1、泄露栈内存:获取某个变量的值、获取某个变量对应地址的内存
2、泄露任意地址内存:利用GOT表得到libc函数地址,进而获取libc,进而获取其它libc函数地址
(1)获取栈变量数值
#include <stdio.h>
int main()
{
char s[100];
int a = 1, b = 0x22222222, c = -1;
scanf