繁体   English   中英

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

[英]Stack-based overflow code from Hacking: The Art of Exploitation

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

因此,我一直在重新阅读《黑客:剥削的艺术》,我对书中的某些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地址吗? 即使如此,我也看不出他怎么能在这里使用该技术,因为他将永远不会获得指向vuln程序内部缓冲区顶部的ESP。

谢谢。

我什至看不到他怎么可以在这里使用该技术,因为他永远不会获得指向vuln程序内部缓冲区顶部的ESP。

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

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

vulnstrcpy()buffer到它自己的堆栈,它无法检查的范围和覆盖自己的EIP与缓冲区的起始地址信息中的漏洞的堆栈帧,或者至少接近它(因此NOP雪橇)。 NOP vuln和复制到vuln堆栈框架的shellcode是偶然的; 不是他们的出发地。 至关重要的是, vuln和shellcode必须小于 vuln期望的buffer ,否则保存的EIP将被shellcode而不是buffer的地址覆盖。

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

重要的一点是buffer在两个不同的位置读取了两次

编辑:忽略这一点,我感到困惑(尽管感谢您的接受!)。 希望我也不会混淆您,这就是为什么我要编写此编辑内容。 易受攻击的程序位于完全不同的虚拟内存空间中,因为它是由操作系统在单独的进程中运行的(或具有新映像的同一进程?无论如何)。 因此, vuln无法访问漏洞利用程序的堆栈堆。

ESP技巧必须是某种猜测猜测复制缓冲区中的NOP滑板在vuln堆栈中最终位置的方式。 我个人希望偏移量比0大得多,因为漏洞利用的堆栈比vuln的堆栈小。

就是说,我很确定vuln中仍然有两个Shellcode 副本 (否则,它的strcpy()可能来自什么?)。 偏移量为0,也许他正在运行存储在argv [] ...?!中的shellcode。 在那种情况下,您仍然会遇到这样的情况,一个缓冲区中的地址指向另一个缓冲区中的NOP底座,就像我的原始答案一样。 我以前错了,所以让我知道是否没有道理。

在很大程度上取决于代码打算使用的特定操作系统。 在不知道这一点的情况下,任何讨论都必须有点泛泛[我的猜测也是这样]。

一种可能性是您遗漏的“ printfs束”中有一些重要内容...

如果真的没有什么聪明的事情发生,我想它试图利用的漏洞在有效传递长(600字节)命令行参数时在execl(..)调用和/或OS中。 在某个地方(我猜是),一个子例程将为新进程设置环境,并且在此过程中,将作为参数( buffer )传入的600字节字符串复制到一个很小的( ish)在新进程的堆栈上固定大小的缓冲区,并且[大概]用原始调用中的堆栈指针的许多副本覆盖此“设置”函数的返回地址。 当“命令行复制功能”返回时,它将因此从原始副本返回到精心准备的buffer并执行shellcode。

(如果省略的shellcode包含一个零字节...\\x00...那么就不可能发生这种情况,因为它会在设置命令行缓冲区时标记要复制的字符串的结尾)。

:)我已经在这里待了好几天试图找出实际情况。 在此过程中,我还发现了这篇文章。 现在,我对发生的事情有了一个大致的了解,我认为我应该分享自己的理解,这样像“我”这样的其他人也会发现这很有用。

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

该函数实际上用于猜测返回地址。 它返回我们的shellcode注入程序的ESP值,而不是易受攻击程序的ESP值。 但是由于堆栈从几乎相同的地址开始(对于未启用ASLR的系统),并且正如aleph在其文章中提到的那样:“大多数程序在任何时候都不会将数百或几千个字节推入堆栈。因此,通过知道堆栈从哪里开始,我们可以尝试猜测我们试图溢出的缓冲区将在哪里,我们可以了解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!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM