黑客的基于堆栈的溢出代码:剥削的艺术

时间:2013-07-17 13:31:26

标签: c stack buffer-overflow

这可能与 this 有关,但我不确定它是否在同一条船上。

所以我一直在重读Hacking:剥削的艺术,我对本书中的一些C代码有疑问,这对我来说没有意义:

让我们假设我们回到〜2000年,我们实际上没有堆栈cookie和ASLR(可能我们这样做,但它尚未实现或不广泛),或者我们还有其他任何类型的保护现在有一天。

他向我们展示了这段代码,以利用一个简单的基于堆栈的溢出:

#include <stdlib.h>

char shellcode[] = "..." // omitted

unsigned long sp(void) 
{ __asm__("movl %esp, %eax); }

int main(int argc, char *argv[]) {
int i, offset;
long esp, ret, *addr_ptr;
char *buffer, *ptr;

offset = 0;
esp = sp();
ret = esp - offset;

// bunch of printfs here...

buffer = malloc(600);

ptr = buffer;
addr_ptr = (long *) ptr;
for(i = 0; i < 600; i+=4)
{ *(addr_ptr++) = ret; }

for(i = 0; i < 200; i++)
{ buffer[i] = '\x90'; }

ptr = buffer + 200;
for(i = 0; i < strlen(shellcode); i++)
{ *(ptr++) = shellcode[i]; }

buffer[600-1] = 0;

execl("./vuln", "vuln", buffer, 0);

free(buffer);

return 0;
}

所以他想要做的就是获取ESP的地址并用该地址覆盖保存的EIP,这样处理器就会跳转到内存中的NOP底座并在堆栈中执行shellcode。

我不明白的是,他如何使用他在sp()当前所称的特定ESP值。

根据我的理解,堆栈看起来像这样的东西:

...
saved ebp <-- execl
saved eip
"./vuln"
"vuln"
buffer
0
*ptr <-- sp() returns this address?
*buffer
*addr_ptr
ret
esp
offset
i
saved ebp <-- main
saved eip
argc
argv
...

因为他打电话(我知道它是一个函数指针,所以我猜不完全准确的措辞?)sp()在漏洞利用的早期,不应该给他一个糟糕的ESP地址吗?即便如此,我也不知道他怎么能在这里使用这种技术,因为他永远不会让ESP指向他的 vuln 程序中缓冲区的顶部。

感谢。

3 个答案:

答案 0 :(得分:1)

  

我不知道他怎么能在这里使用这种技术,因为他永远不会让ESP指向他的 vuln 程序中缓冲区的顶部。

我没有读过很多书,但我想我已经弄明白了。这是*buffer的样子:

NOP sled | shellcode | Address of buffer in the exploit's stack frame

vuln strcpy() buffer进入自己的堆栈时,它无法检查边界并用 exploit的堆栈帧,或至少接近它的东西(因此是NOP雪橇)。 复制到vuln堆栈帧的NOP sled和shellcode是偶然的;那些从哪里开始运行。雪橇和shellcode的重要性vuln期望buffer更大否则保存的EIP将被shellcode覆盖,而不是buffer

的地址

然后,当在vuln上使用strcpy()的{​​{1}}的任何部分返回时,它将转到NOP底座并执行shellcode。

重点是buffer在两个不同的地方读取两次

编辑:无视这一点,我感到困惑(尽管感谢接受!)。希望我也不会混淆你,这就是我写这个编辑的原因。易受攻击的程序位于完全不同的虚拟内存空间中,因为它由操作系统在单独的进程中运行(或者与新映像相同的进程?无论如何)。因此 vuln 无法访问exploit的堆栈堆。

ESP技巧必须以某种方式猜测复制缓冲区中的NOP雪橇最终会在 vuln 的堆栈中结束。我个人认为偏差大于0,因为与 vuln 相比,漏洞利用的堆栈非常小。

那就是说,我很确定在 vuln 中还有两个shellcode副本(否则,它可能来自buffer?)。偏移量为0,也许他正在运行存储在 argv [] 中的shellcode ...?!?在这种情况下,你仍然会遇到一个缓冲区中的地址指向另一个缓冲区中的NOP sled的情况,就像我原来的答案一样。我以前错了,所以如果这没有意义,请告诉我。

答案 1 :(得分:0)

很多将取决于代码打算利用的具体操作系统。如果不知道这一点,任何讨论都必须具有一定的通用性[并且我会猜测]。

一种可能性是,你遗漏的“一堆印刷品”中有一些重要的东西......

如果那里真的没有发生任何巧妙的事情,我猜想当它有效地传递一个长(600字节)命令行参数时,它试图利用的漏洞在execl(..)调用和/或操作系统内。在那里的某个地方[我猜]子程序将为新进程设置环境,并且沿途将把作为参数(buffer)传入的600字节字符串复制到可能的状态在新进程的堆栈上是一个小的(ish)固定大小的缓冲区,并且[推测]用原始调用中的堆栈指针的许多副本覆盖此“setup”函数的返回地址。当“命令行复制功能”返回时,它将返回原始副本中精心准备的buffer并执行shellcode。

(如果省略的shellcode包含零字节...\x00...,那么这不可能是正在发生的事情,因为它会标记在设置命令行缓冲区时复制的字符串的结尾。 / p>

答案 2 :(得分:0)

:)我已经在这几天试图找出实际发生的事情了。在这个过程中我也发现了这篇文章。现在我已经看到了正在发生的事情,我想我应该分享我理解的东西,以便其他人喜欢和我一样。也会觉得这很有用。

unsigned long getesp()
{
__asm__("movl %esp, %eax");
}

此函数实际用于猜测返回地址。它返回我们的shellcode注入程序的ESP值而不是易受攻击程序的ESP值。但是由于堆栈开始于几乎相同的地址(对于没有启用ASLR的系统)和aleph在他的文章中提及&#34;大多数程序不会超过几百个 或者在任何时候进入堆栈的几千个字节。因此,通过了解 堆栈开始的地方我们可以尝试猜测我们尝试的缓冲区在哪里 溢出将是&#34;,我们可以了解我们的shellcode应该在哪里。让我解释一下。

假设对于我们的测试程序,堆栈从1000开始。当我们执行它时,上面的代码实际上返回了这个地址。现在考虑我们易受攻击的程序,并假设我们尝试注入的缓冲区位于地址970,返回地址存储在1040.

[BUFFER 970] [RETURN_ADDRESS 1070]

好?现在缓冲区已经提交了NOPS直到上半部分,然后是shellcode,然后是返回地址..

  

[NOP SLED] [SHELL_CODE] [RETURN_ADDRESS]

让我们像这样填写

NOPS [970-1010] SHELLCODE [1010-1050] RETURN_ADDRESS [1050-1070]

getesp()返回的值可以了解堆栈的位置。因此,如果我们用getesp()返回的1000重写返回地址,我们可以看到该漏洞仍然有效,因为1000处的地址是用nops提交的。执行将滑到shellcode!