当你调用像printf这样的函数时,formatstring和arguments被压入堆栈。如果省略参数但是在格式字符串中使用“%x”或“%s”或“%n”指定它们,则可以访问(读取或写入)formatstring。在一个系统上我测试,格式字符串是第四个参数。另一个是超过200。
例如,我得到了以下程序,它容易受到formatstring exploit的攻击,并包含以下语句:
printf(userSuppliedString);
不,我想阅读特定的地址。例如。 0xbffffdd7。 我用以下方式称呼它:
./fmt_vuln $(printf "\xd7\xfd\xff\xbf")%08x.%08x.%08x.%s
在此示例中,格式字符串是第四个参数(“%s”)。所以%s将采用格式字符串的开头。因为这是我们指定的地址,所以将打印此地址的内容。
现在在这台机器上,formatstring是第四个paraemter。但在其他Linux系统上,它的东西完全不同。 为什么会这样?
答案 0 :(得分:6)
不,也许你误解了。当你调用像printf这样的函数时,formattring和 参数被压入堆栈。如果你省略参数但是 用格式字符串中的“%x”或“%s”或“%n”指定它们 访问(读取或写入)formatstring。在一个系统上我测试, 格式字符串是第4个参数。在另一个它超出了 第200个。
当您使用一个参数(格式字符串)调用printf时,指向格式字符串的指针将被压入堆栈。这是一个char *
这个指针可以指向内存中的任何位置 - printf只是告诉它并将该内存位置作为格式字符串读取。
在通常的一个参数情况下,将字符串文字传递给printf ("hello world!");
编译器将文本hello world
放在内存中的某个位置,并生成指向它的指针以传递给printf。然后它执行任何调用约定它应该为函数调用做的事情 - 例如在x86上它将指针推送到堆栈。 Printf然后从堆栈中读取它的第一个参数并且很高兴!
在通常的n参数情况下,字符串文字和指针会发生同样的事情。对于函数调用,编译器传递每个值。再次使用x86(因为推送比描述具有复杂参数传递方案的ARM更容易描述)这些值从右到左被推送到堆栈。因此,如果您对printf ("%d, %s, %d", x, name, y);
的调用被推送到堆栈,则命名为x,最后是格式字符串。
现在,在printf中我们读取了我们的第一个参数(从堆栈中获取)。它是指向char *
的{{1}}。我们可以阅读这个,然后 - 知道编译器如何传递参数,我们可以读取被推入堆栈的三件事 - 我们再次感到高兴!
格式字符串漏洞的工作原理是错误地认为printf具有的信念和编译器所具有的信念。
我们可以通过调用由于向printf传递错误数量的参数而导致的未定义行为来显示它。在调用中"%d, %s, %d"
编译器不会推送与printf ("%s");
printf预期用于实现char *
指令相对应的参数。但是 - 因为printf不知道编译器没有这样做,所以无论如何都会在堆栈上查找参数。它从堆栈中提取一个未定义的值,并尝试读取它指向的字符串。
在您的情况下,您允许将任意格式字符串传递给printf。这些肯定与预期的参数数量和传递的参数数量不匹配,因此printf读取堆栈 - 它充满了垃圾。
如果你很幸运 - 你可以操纵这个垃圾指向你控制的东西 - 并且可以用它来阅读你没想到的信息。如果您可以欺骗%n参数指向您控制的某个位置,则可以使用打印的字符数写入该内存位置。
所以 - 考虑到这个描述,我无法找到解析你的问题的方法。也许你可以更清楚,我可以更新我的答案?
答案 1 :(得分:1)
./fmt_vuln $(printf "\xd7\xfd\xff\xbf")%08x.%08x.%08x.%s
在此示例中,格式字符串是第四个参数(“%s”)。
不,不是真的。问题是你没有访问printf
的第四个参数,而是访问其调用函数中的局部变量或参数(或者在堆栈的上方)。因此,它完全取决于调用函数的代码。为了演示它在386上做了什么:
Breakpoint 1, __printf (format=0xbffff543 "%p") at printf.c:29
29 printf.c: Adresář nebo soubor neexistuje.
in printf.c
(gdb) x/120a $ebp
Description: $esp return addr fmtstring parameters
0xbffff2d8: 0xbffff2f8 0x80483fd <main+25> 0xbffff543 0xb7ff1310
0xbffff2e8: 0x804842b <__libc_csu_init+11> 0xb7fb7ff4 0x8048420 <__libc_csu_init> 0x0
0xbffff2f8: 0xbffff378 0xb7e78e46 <__libc_start_main+230> 0x2 0xbffff3a4
0xbffff308: 0xbffff3b0 0xb7fe1860 0xb7ff7411 0xffffffff
0xbffff318: 0xb7ffeff4 0x8048254 0x1 0xbffff360
0xbffff328: 0xb7ff0996 0xb7fffac0 0xb7fe1b58 0xb7fb7ff4
0xbffff338: 0x0 0x0 0xbffff378 0xa32ae5c4
0xbffff348: 0x93d0f3d4 0x0 0x0 0x0
0xbffff358: 0x2 0x8048330 <_start> 0x0 0xb7ff65b0
0xbffff368: 0xb7e78d6b <__libc_start_main+11> 0xb7ffeff4 0x2 0x8048330 <_start>
0xbffff378: 0x0 0x8048351 <_start+33> 0x80483e4 <main> 0x2
0xbffff388: 0xbffff3a4 0x8048420 <__libc_csu_init> 0x8048410 <__libc_csu_fini> 0xb7ff1310
0xbffff398: 0xbffff39c 0xb7fff908 0x2 0xbffff539
0xbffff3a8: 0xbffff543 0x0 0xbffff546 0xbffff55a
0xbffff3b8: 0xbffff56a 0xbffff581 0xbffff58c 0xbffff5dc
0xbffff3c8: 0xbffff5f3 0xbffff654 0xbffff66f 0xbffff689
如您所见,格式字符串仅存在于内存中,位于由argv
指向的libc运行时初始化的区域中。你必须更好地研究你正在攻击的代码。