【问题标题】:Unable to understand a format string exploitation code无法理解格式字符串利用代码
【发布时间】:2011-08-03 17:09:17
【问题描述】:

我在阅读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 个%x 之后包含41414141AAAA 的十六进制表示),即正好在当前%esp 的 4 个地址之上。我也努力盯着汇编代码看,但我想我无法跟踪堆栈上字符串的操作。

【问题讨论】:

  • 我不明白你的命令调用。 ~/research/paper 是可执行文件吗?我会理解./myprog &lt; inputdatacat inputdata | ./myprog,但我不明白你在做什么。
  • 好的,我明白了。执行a.out,打印secret = 0xbffffc68,然后等待用户输入。对我来说,这只是将输入回显到最后一行的屏幕,然后退出。 ~/research/paper 只是一个 shell 提示符。
  • @kerrek SB:我很抱歉。为了更清楚,我已将其删除。
  • 我没有得到并且需要测试的是fgets 如何从标准输入读取超过 'AAAA' 字节。它应该达到 EOF 而不是实际读出 512 个字节吗?我的意思是当它读取标准输入时,它是否应该得到一个以空结尾的字符串然后是 EOF?
  • @Mr. Shickadance:如果遇到换行符,fgets 最多将接下来的 n-1 个字符读取到缓冲区中。

标签: c


【解决方案1】:

在进入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

targetsnprintf 使用其单词作为参数之前被snprintf 的结果覆盖:它首先在 0xbfd25800 处写入“AAAA”(0x41414141),然后“%x”读取 0xbfd257ec 处的值,然后将其写入 0xbfd25804, ..., 然后 "%x" 读取 0xbfd25800 (0x41414141) 的值并将其写入 0xbfd25814, ...

【讨论】:

  • 肯定targetsnprintf 覆盖,但我没有得到后面的部分,即在 snprintf 使用它的词作为参数之前。我认为目标被 snprintf 覆盖。我的问题是如何用值 1414141 覆盖目标?换句话说 %x %x...in 缓冲区的序列如何被 1414141 替换?
  • @Terminal,其中 0xbfd25800 现在包含零,在评估 %x 序列之前,snprintf 会覆盖“AAAA”部分,因为snprintf 从字符串的左侧开始并复制一切,直到它达到%x。这样它就不必分配一个临时缓冲区来格式化整个字符串并一次复制它。
  • @karl bielefeldt:谢谢我明白了!!然后我认为该链接上给出的解释不正确。如果你们中的任何人都可以对原始帖子链接中的代码之后给出的解释有所了解,那就太好了。我相信我在那里误解了一些东西,特别是他们如何找到缓冲区的偏移量。
  • @karl bielefeldt:我终于明白了那篇文章想要表达的意思。感谢您的帮助。
【解决方案2】:

首先我们看一下调用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() 就写在那里。我们还可以看到,在 0xbffff00 处,snprintf() 的最后一个参数:目标的地址,实际上是 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) 

就是这样!综上所述,我们在此处找到了字符串,因为字符串以相反的方式存储在堆栈中,并且 target 的开头(或结尾?)接近 esp。

【讨论】:

    猜你喜欢
    • 2020-07-27
    • 2012-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    • 1970-01-01
    相关资源
    最近更新 更多