0

我正在阅读有关格式字符串漏洞的教程,以学习如何更安全地编码。到目前为止,我编写的程序如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    char text[100];
    strcpy(text, argv[1]);

    printf(text);
}

我这样运行它:

>>> ./foo $(ruby -e 'print "AAAA" + "%08x."*9 + "%x"')
AAAAffe466f4.00000001.f763b1c9.ffe458df.ffe458de.00000000.ffe459c4.ffe45964.00000000.41414141

我可以在末尾看到“41414141”,这是字符串开头的 AAAA。但是,当我像这样使用“%s”时:

>>> ./foo $(ruby -e 'print "AAAA" + "%08x."*9 + "%s"')

我得到一个段错误。谁能指出我正确的方向?

4

3 回答 3

1

在动态字符串上使用通常是未定义的行为printf,因为您不能保证该字符串没有格式说明符。正确的说法是,

printf("%s", text);

要不就

puts(text);

现在,也就是说,您的第一个示例归结为printf("%x%x");. 这当然是 UB,但是这两个%x说明符只会让您从堆栈中读取有限的数量(两个字),这将打印垃圾,但只有有限的数量。

另一方面,当您说 时printf("%s"),该函数需要一个指针,该指针指向您无法控制的内存区域中以空字符结尾的字节序列!本质上,该函数将从堆栈中读取一个字,假装它是一个指针,然后读取该值指向的内存——这几乎肯定会导致分段错误,因为您不允许通过以下方式访问大多数内存地址默认。即使地址指向您被允许访问的内存,也没有理由很快就会出现零字节,因此您很可能只是跑出页面并进入非法内存。

于 2012-09-29T23:10:38.263 回答
1

问题是,此时,您在堆栈上达到原始 AAAA;但是,说明%s符需要一个指向字符串的指针,即 AAAA 的地址。您想要执行的操作没有格式字符串说明符,因为在正常执行过程中您不会将字符串直接粘贴为printf()的参数;一个想法是%c%c%c%c至少将数据打印为字符而不是十六进制值,但这也不起作用,因为 C 中参数的最小大小是int,甚至说明%c符也适用于int大小的参数内存区域。

于 2012-09-29T23:13:08.120 回答
0

test你有最后%s。遇到 a但您没有提供任何 --> segfault时printf需要一个-pointer。改为使用或char%sputsprintf("%s", test);

$ ruby -e 'print "AAAA" + "%08x."*9 + "%x"'
AAAA%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%x

$ ruby -e 'print "AAAA" + "%08x."*9 + "%s"'
AAAA%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%s

这两个字符串都无效,printf因为您没有传递所需的参数。

于 2012-09-29T23:06:57.413 回答