这可能与 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 程序中缓冲区的顶部。
感谢。
答案 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!