我在阅读this文章时遇到了显示格式字符串利用的代码。
#include <stdio.h>
int main(void)
{
char secret[]="hack.se is lame";
char buffer[512];
char target[512];
printf("secret = %pn",&secret);
fgets(buffer,512,stdin);
snprintf(target,512,buffer);
printf("%s",target);
}
使用以下输入执行
[root@knark]$ ./a.out
secret = 0xbffffc68
AAAA%x %x %x %x %x %x %x //Input given
AAAA4013fe20 0 0 0 41414141 33313034 30326566
- [root@knark]$
到目前为止我所理解的是%x
的序列将继续在当前%esp
以上的地址处打印值(我假设堆栈向下朝向较低地址增长)。
我无法理解的是,给定的输入存储在buffer
数组中,该数组距离当前%esp
不能少于512字节。那么,输出如何在4 41414141
之后包含AAAA
(%x
的十六进制表示),即刚好在当前%esp
的4个地址之上。我也很努力地盯着汇编代码,但我认为我无法跟踪堆栈上字符串的操作。
答案 0 :(得分:4)
在进入snprintf
时,堆栈具有以下内容:
0xbfd257d0: 0xxxxxxxxx 0xxxxxxxxx 0xxxxxxxxx 0x080484d5
0xbfd257e0: 0xbfd25800 0x00000200 0xbfd25a00 0x00000000
0xbfd257f0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd25800: 0x00000000 0x00000040 0xb7f22f2c 0x00000000
0xbfd25810: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd25800 -> target (initially 0x00000000 0x00000040 ...)
... -> garbage
0xbfd257e8 -> pointer to buffer
0xbfd257e4 -> 512
0xbfd257e0 -> pointer to target
0xbfd257df -> return address
在target
使用其单词作为参数之前, snprintf
会被snprintf
的结果覆盖:它首先将“AAAA”(0x41414141)写入0xbfd25800,然后写入“%x”读取0xbfd257ec处的值并将其写入0xbfd25804,...,然后“%x”读取0xbfd25800(0x41414141)处的值并将其写入0xbfd25814,...
答案 1 :(得分:1)
首先,让我们在调用snprintf()后查看堆栈:
Reading symbols from /home/blackbear/a.out...done.
(gdb) run
Starting program: /home/blackbear/a.out
secret = 0xbffff40c
ABCDEF%x %x %x %x %x %x %x
Breakpoint 1, main () at prova.c:13
13 printf("%s",target);
(gdb) x/20x $esp
0xbfffeff0: 0xbffff00c 0x00000200 0xbffff20c 0x00155d7c
0xbffff000: 0x00155d7c 0x000000f0 0x000000f0 0x44434241
0xbffff010: 0x35314645 0x63376435 0x35353120 0x20633764
0xbffff020: 0x66203066 0x34342030 0x32343334 0x33203134
0xbffff030: 0x34313335 0x20353436 0x37333336 0x35333436
(gdb)
我们实际上可以在0xbffff00c看到已经格式化的字符串,所以sprintf()就在那里写了。我们还可以在0xbfffeff0看到snprintf()的最后一个参数:target的地址,实际上是0xbffff00c。 所以我可以推断出字符串是从堆栈的分配空间的末尾保存到开头的,因为我们还可以看到添加strcpy():
blackbear@blackbear-laptop:~$ cat prova.c
#include <stdio.h>
#include <string.h>
int main(void)
{
char secret[]="hack.se is lame";
char buffer[512];
char target[512];
printf("secret = %p\n", &secret);
strcpy(target, "ABCDEF");
fgets(buffer,512,stdin);
snprintf(target,512,buffer);
printf("%s",target);
}
blackbear@blackbear-laptop:~$ gcc prova.c -g
prova.c: In function ‘main’:
prova.c:14: warning: format not a string literal and no format arguments
prova.c:14: warning: format not a string literal and no format arguments
blackbear@blackbear-laptop:~$ gdb ./a.out -q
Reading symbols from /home/blackbear/a.out...done.
(gdb) break 13
Breakpoint 1 at 0x8048580: file prova.c, line 13.
(gdb) run
Starting program: /home/blackbear/a.out
secret = 0xbffff40c
Breakpoint 1, main () at prova.c:13
13 fgets(buffer,512,stdin);
(gdb) x/10x $esp
0xbfffeff0: 0xbffff00c 0x080486bd 0x00000007 0x00155d7c
0xbffff000: 0x00155d7c 0x000000f0 0x000000f0 0x44434241
0xbffff010: 0x00004645 0x00000004
(gdb)
就是这样!总之,我们在那里找到了字符串,因为字符串以相反的方式存储在堆栈中,并且目标的开头(或结束?)接近esp。