一、漏洞原理

%d - 十进制 - 输出十进制整数
%s - 字符串 - 从内存中读取字符串
%x - 十六进制 - 输出十六进制数
%c - 字符 - 输出字符
%p - 指针 - 指针地址
%n - 到目前为止所写的字符数

*******************************************************************

 

产生这个漏洞的原因只有一个,那就是程序员偷懒。

比如我想让用户输入一个名字,然后再把这个名字原样输出,一般人可能会这么写

1
2
3
char str[100];
scanf("%s",str);
printf("%s",str);

这个程序没有问题。
但总会有一些人为了偷懒会写成这种样子

1
2
3
char str[100];
scanf("%s",str);
printf(str);

这个程序在printf处用了一种偷懒的写法。这看起来是没有什么问题,程序也正常的打印了名字,但是却产生了一个非常严重的漏洞。

一般来说,每个函数的参数个数都是固定的,被调用的函数知道应该从内存中读取多少个变量,但printf是可变参数的函数,对可变参数的函数而言,一切就变得模糊了起来。函数的调用者可以自由的指定函数参数的数量和类型,被调用者无法知道在函数调用之前到底有多少参数被压入栈帧当中。所以printf函数要求传入一个format参数用以指定到底有多少,怎么样的参数被传入其中。然后它就会忠实的按照函数的调用者传入的格式一个一个的打印出数据。由于编程者的疏忽,把格式化字符串的操纵权交给用户,就会产生后面任意地址读写的漏洞。

示例程序:

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main(void)
{
	char a[100];
	scanf("%s",a);
	printf(a);
	return 0;
}

假设我们的输入为:

1
AAAA%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x

程序的输出为(此次):

1
AAAA61fe4c,61ffcc,76e4d250,70734fbf,fffffffe,76e473da,41414141,252c7825,78252c78,2c78252c,252c7825

注意,这其中有一组为41414141,那就是这个字符串开始的位置。

看一下栈里的样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
0061FE30   0061FE4C  |format = "AAAA%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x"
0061FE34   0061FE4C  |<%x> = 0x61FE4C
0061FE38   0061FFCC  |<%x> = 0x61FFCC
0061FE3C   76E4D250  |<%x> = 0x76E4D250
0061FE40   FF12BE58  |<%x> = 0xFF12BE58
0061FE44   FFFFFFFE  |<%x> = 0xFFFFFFFE
0061FE48   76E473DA  |<%x> = 0x76E473DA
0061FE4C   41414141  |<%x> = 0x41414141
0061FE50   252C7825  |<%x> = 0x252C7825
0061FE54   78252C78  |<%x> = 0x78252C78
0061FE58   2C78252C  |<%x> = 0x2C78252C
0061FE5C   252C7825  \<%x> = 0x252C7825
0061FE60   78252C78
0061FE64   2C78252C
0061FE68   252C7825
0061FE6C   78252C78
0061FE70   00000000
0061FE74   00000000
0061FE78   00000000

0x0061FE4C 是格式化字符串开始的位置,通过不断的取变量操作,最终我们就能读取到程序的每一个位置。

分类: Pwn

发表评论

电子邮件地址不会被公开。


CAPTCHA Image
Reload Image
皖ICP备18016857号-1