快捷搜索:

利用格式化字符串漏洞对系统发起攻击

什么是款式化字符串进击?

款式化字符串破绽同其他许多安然破绽一样是因为法度榜样员的怠惰造成的。当你正在涉猎本文的时刻,大概有个法度榜样员正在编写代码,他的义务是:打印输出一个字符串或者把这个串拷贝到某缓冲区内。他可以写出如下的代码:

printf("%s", str);

然则为了节约光阴和前进效率,并在源码中少输入6个字节,他会这样写:

printf(str);

为什么不呢?干嘛要和多余的printf参数打交道,干嘛要花光阴分化那些愚笨的款式?printf的第一个参数无论若何都邑输出的!法度榜样员在不知不觉中打开了一个安然破绽,可以让进击者节制法度榜样的履行,这便是不能偷懒的缘故原由所在。

为什么法度榜样员写的是差错的呢?他传入了一个他想要逐字打印的字符串。实际上该字符串被printf函数解释为一个款式化字符串(format string)。函数在此中探求特殊的款式字符比如"%d"。假如碰着款式字符,一个变量的参数值就从客栈中掏出。很显着,进击者至少可以经由过程打印出客栈中的这些值来偷见地度榜样的内存。然则有些工作就不那么显着了,这个简单的差错容许向运行中法度榜样的内存里写入随意率性值。

Printf中被轻忽的器械

在阐明若作甚了自己的目的滥用printf之前,我们应该深入领会printf供给的特点。假定读者曩昔用过printf函数并且知道通俗的款式化特点,比如若何打印整型和字符串,若何指定最大年夜和最小字符串宽度等。除了这些通俗的特点之外,还有一些深奥和鲜为人知的特点。在这些特点傍边,下面先容的对我们对照有用:

* 在款式化字符串中任何位置都可以获得输出字符的个数。当在款式化字符串中碰着"%n"的时刻,在%n域之前输出的字符个数会保存到下一个参数里。例如,为了获取在两个款式化的数字之间空间的偏量:

int pos, x = 235, y = 93;

printf("%d %n%d\n", x, &pos, y);

printf("The offset was %d\n", pos);

* %n款式返回应该被输出的字符数目,而不是实际输出的字符数目。当把一个字符串款式化输出到一个定长缓冲区内时,输出字符串可能被截短。不斟酌截短的影响,%n款式表示假如不被截短的偏量值(输出字符数目)。为了阐明这一点,下面的代码会输出100而不是20:

char buf[20];

int pos, x = 0;

snprintf(buf, sizeof buf, "%.100d%n", x, &pos);

printf("position: %d\n", pos);  简单的例子

除了评论争论抽象和繁杂的理论,我们将会应用一个详细的例子来阐明我们刚才评论争论的道理。下面这个简单的法度榜样能满意这个要求:

/*

* fmtme.c

*Format a value into a fixed-size buffer

*/

#include

int

main(int argc, char **argv)

{

char buf[100];

int x;

if(argc != 2)

exit(1);

x = 1;

snprintf(buf, sizeof buf, argv[1]);

buf[sizeof buf - 1] = 0;

printf("buffer (%d): %s\n", strlen(buf), buf);

printf("x is %d/%#x (@ %p)\n", x, x, &x);

return 0;

}

对这个法度榜样有几点阐明:第一,目的很简单:将一个经由过程敕令行通报值款式化输出到一个定长的缓冲区里。并确保缓冲区的大年夜小限定不被冲破。在缓冲区款式化后,把它输出。除了把参数款式化,还设置了一个整型值随后输出。这个变量是随后我们进击的目标。现在值得我们留意的是这个值应该始终为1。

本文中所有的例子都是在x86 BSD/OS 4.1机械上完成。假如你到莫桑比克履行义务跨越20年光阴可能会对x86不认识,这是一个little-endian机械。这抉择在例子中多精度数字的表示措施。在这里应用的详细数值会由于系统的差异而不合,这些差异表现在不合体系布局、操作系统、情况以致是敕令行长度。颠末简单调剂,这些例子可以在其他x86平台上事情。经由过程努力也可以在其他体系布局的平台上事情。

用Format进击

现在是我们戴上黑帽子开始以进击者要领思虑问题的时刻了。我们现在手头有一个测试法度榜样。知道这个法度榜样有一个破绽并且懂得法度榜样员是在哪里犯差错的(直接把用户输入的敕令行参数作为snprintf的款式化参数)。我们还拥有关于printf函数深入的常识,知道若何运用这些常识。让我们开始修补我们的法度榜样吧。

从简单的开始,我们经由过程简单的参数调用法度榜样。看这儿:

% ./fmtme "hello world"

buffer (11): hello world

x is 1/0x1 (@ 0x804745c)

现在这儿还没有什么特其余工作发生。法度榜样把我们输入的字符串款式化输出到缓冲区里,然后打印出它的长度和数值。法度榜样还奉告我们变量x的值是1(以十进制和十六进制分手显示),x的存储地址是0x804745c。接下来我们试着应用一些款式指令。鄙人面的例子中我们打印出在款式化字符串之上栈堆中的整型数值:

% ./fmtme "%x %x %x %x"

buffer (15): 1 f31 1031 3133

x is 1/0x1 (@ 0x804745c)

对这个法度榜样的快速阐发可以揭示在调用snprintf函数时法度榜样客栈的筹划:

AddressContentsDescription

fp+8Bufferpointer 4-byte address

fp+12Bufferlength 4-byte integer

fp+16&n

您可能还会对下面的文章感兴趣: