什么是缓冲区溢出?如何导致缓冲区溢出?

时间:2009-02-22 02:05:42

标签: c++ buffer-overflow fortify-source

我听说过缓冲区溢出,我想知道如何导致缓冲区溢出。

有人能给我看一个小的缓冲区溢出示例吗? 新的(它们用于什么?)

12 个答案:

答案 0 :(得分:32)

缓冲区溢出的经典示例:

// noone will ever have the time to type more than 64 characters...
char buf[64];
gets(buf); // let user put his name

单独的缓冲区溢出通常不会故意发生。它最常发生的原因是所谓的“一个一个”错误。意思是你错误地计算了数组大小 - 可能是因为你忘了考虑终止空字符,或者因为其他一些东西。

但它也可以用于一些邪恶的东西。实际上,用户很久就知道这个漏洞,然后插入70个字符,最后一个包含一些特殊字节,覆盖一些堆栈槽 - 如果用户真的很棘手,他/她会点击堆栈中的返回地址槽,并覆盖它,以便它跳转到刚插入的缓冲区:因为用户输入的不是他的名字,而是他之前编译并转出的shell代码。然后那个人就会执行。有一些问题。例如,您必须安排在该二进制代码中不要使用“\ n”(因为获取将停止在那里阅读)。对于混乱危险字符串函数的其他方式,二进制零是有问题的,因为字符串函数停止将其复制到缓冲区。人们已经使用xor两倍相同的值来产生零,而没有明确地写出零字节。

这是经典的做法。但是有一些安全块可以告诉这些事情发生以及其他使堆栈不可执行的东西。但我想有比我刚才解释的更好的技巧。一些汇编人现在可能会告诉你有关这方面的长篇故事:)

如何避免

总是使用带有最大长度参数的函数,如果你不是 100%确定缓冲区确实足够大。不要玩“哦,数字不会超过5个字符”这样的游戏 - 它有一天会失败。请记住,一个火箭科学家说这个数字不会超过一定数量,因为火箭永远不会那么快。但是有一天, 实际上更快了,结果是一个整数溢出而且火箭崩溃了(这是Ariane 5中的一个错误,这是历史上最昂贵的计算机错误之一)。

例如,而不是使用fgets。而不是sprintf使用snprintf哪里适合和可用(或只是C ++风格的东西,如istream和东西)

答案 1 :(得分:26)

缓冲区溢出基本上是在内存的精心设计的部分(或缓冲区)写在其预期范围之外时。如果攻击者可以设法从程序外部实现这一点,则可能会导致安全问题,因为它可能会允许他们操纵任意内存位置,尽管许多现代操作系统可以防止出现这种情况。

虽然读取和写入超出预期范围通常被认为是一个坏主意,但术语“缓冲区溢出”通常保留用于写入在边界之外,因为这可能导致攻击者轻松修改代码运行方式。维基百科上有一篇关于buffer overflows的好文章以及它们可以用于攻击的各种方法。

关于如何自己编程,这将是一个简单的问题:

char a[4];
strcpy(a,"a string longer than 4 characters"); // write past end of buffer (buffer overflow)
printf("%s\n",a[6]); // read past end of buffer (also not a good idea)

是否编译以及运行时会发生什么可能取决于您的操作系统和编译器。

答案 2 :(得分:12)

在现代Linux操作系统中,如果没有一些EXTRA实验,你无法利用缓冲区溢出。 为什么?因为在这个现代GNU C编译器中你会被 ASLR (地址堆栈层随机化)和堆栈保护器阻止。你不会轻易找到记忆因为记忆会落入由 ASLR 引起的随机记忆中。如果你试图溢出程序,你将被堆栈保护程序阻止。

开始时你需要把ASLR放到0 默认值为2

root@bt:~# cat /proc/sys/kernel/randomize_va_space
2
root@bt:~# echo 0 > /proc/sys/kernel/randomize_va_space
root@bt:~# cat /proc/sys/kernel/randomize_va_space
0
root@bt:~#

在这种情况下,不是关于你可能从互联网上获得的OLD STYLE缓冲区溢出教程。或者aleph一个教程现在不再适用于你的系统。

现在让缓存溢出方案的程序漏洞

---------------------bof.c--------------------------
#include <stdio.h>
#include <string.h>

int main(int argc, char** argv)
{
        char buffer[400];
        strcpy(buffer, argv[1]);

        return 0;
}
---------------------EOF-----------------------------

看看strcpy函数是没有堆栈保护的危险,因为函数没有检查我们将输入多少字节。 使用额外选项 -fno-stack-protector dan -mpreferred-stack-boundary = 2 进行编译,以便在C程序中取消堆栈保护器

root@bt:~# gcc -g -o bof -fno-stack-protector -mpreferred-stack-boundary=2 bof.c
root@bt:~# chown root:root bof
root@bt:~# chmod 4755 bof

缓冲区溢出C程序与SUID root访问权限现在我们已经做到了。 现在让我们搜索我们需要将多少字节放入缓冲区以产生程序分段错误

root@bt:~# ./bof `perl -e 'print "A" x 400'`
root@bt:~# ./bof `perl -e 'print "A" x 403'`
root@bt:~# ./bof `perl -e 'print "A" x 404'`
Segmentation fault
root@bt:~#

你看我们需要404字节才能使程序分段出现故障(崩溃)现在需要覆盖 EIP 多少字节? EIP是指令执行后。所以黑客会在程序的二进制SUID上覆盖 EIP 到邪恶指令他们想要的东西。如果SUID根目录中的程序,该指令将以root访问方式运行。

root@bt:~# gdb -q bof
(gdb) list
1       #include <stdio.h>
2       #include <string.h>
3
4       int main(int argc, char** argv)
5       {
6               char buffer[400];
7               strcpy(buffer, argv[1]);
8
9               return 0;
10      }
(gdb) run `perl -e 'print "A" x 404'`
Starting program: /root/bof `perl -e 'print "A" x 404'`

Program received signal SIGSEGV, Segmentation fault.
0xb7e86606 in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6
(gdb) run `perl -e 'print "A" x 405'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/bof `perl -e 'print "A" x 405'`

Program received signal SIGSEGV, Segmentation fault.
0xb7e800a9 in ?? () from /lib/tls/i686/cmov/libc.so.6
(gdb)

程序GOT分段故障返回码。让我们输入更多的字节,然后看看EIP寄存器。

(gdb) run `perl -e 'print "A" x 406'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/bof `perl -e 'print "A" x 406'`

Program received signal SIGSEGV, Segmentation fault.
0xb7004141 in ?? ()
(gdb)

(gdb) run `perl -e 'print "A" x 407'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/bof `perl -e 'print "A" x 407'`

Program received signal SIGSEGV, Segmentation fault.
0x00414141 in ?? ()
(gdb)

再多

(gdb) run `perl -e 'print "A" x 408'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/bof `perl -e 'print "A" x 408'`

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb)

(gdb) i r
eax            0x0      0
ecx            0xbffff0b7       -1073745737
edx            0x199    409
ebx            0xb7fc9ff4       -1208180748
esp            0xbffff250       0xbffff250
ebp            0x41414141       0x41414141
esi            0x8048400        134513664
edi            0x8048310        134513424
eip            0x41414141       0x41414141 <-- overwriten !!
eflags         0x210246 [ PF ZF IF RF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
(gdb)

现在你可以做下一步了......

答案 3 :(得分:10)

缓冲区溢出只是写入缓冲区的末尾:

int main(int argc, const char* argv[])
{
    char buf[10];
    memset(buf, 0, 11);
    return 0;
}

答案 4 :(得分:5)

除了已经说过的内容之外,请记住,当发生缓冲区溢出时,您的程序可能会“崩溃”,也可能不会“崩溃”。它应该崩溃,你应该希望它 - 但如果缓冲区溢出“溢出”到你的应用程序也分配的另一个地址 - 你的应用程序似乎可以正常运行更长的一段时间。

如果您使用的是更高版本的Microsoft Visual Studio,我建议您使用stdlib中的新安全对应文件,例如sprintf的sprintf_s,等等......

答案 5 :(得分:1)

这应该足以重现它:

void buffer_overflow() 
{
    char * foo = "foo";
    char buffer[10];

    for(int it = 0; it < 1000; it++) {
        buffer[it] = '*';
    }

    char accessViolation = foo[0];
}

答案 6 :(得分:1)

“经典”缓冲区溢出示例是:

int main(int argc, char *argv[])
{
    char buffer[10];
    strcpy(buffer, argv[1]);
}

这可以让你玩缓冲区溢出参数并调整它们的内容。书“Hacking - The Art of Exploitation”(链接到亚马逊)详细介绍了如何解决缓冲区溢出问题(显然纯粹是一种智力练习)。

答案 7 :(得分:1)

如果要检查程序是否存在缓冲区溢出,可以使用Valgrind等工具运行它。他们会为您找到一些内存管理错误。

答案 8 :(得分:1)

这是对您收到的答案的一般性评论。例如:

int main(int argc, char *argv[])
{
    char buffer[10];
    strcpy(buffer, argv[1]);
}

int main(int argc, const char* argv[])
{
    char buf[10];
    memset(buf, 0, 11);
    return 0;
}

在现代Linux平台上,这可能无法按预期或预期工作。由于FORTIFY_SOURCE安全功能,它可能无法正常工作。

FORTIFY_SOURCE使用&#34;更安全&#34;高风险函数的变体,如memcpystrcpy。当编译器可以推导出目标缓冲区大小时,编译器会使用更安全的变体。如果副本超过目标缓冲区大小,则程序将调用abort()

要禁用FORTIFY_SOURCE进行测试,您应该使用-U_FORTIFY_SOURCE-D_FORTIFY_SOURCE=0编译该程序。

答案 9 :(得分:0)

在这种情况下,缓冲区是为特定目的而留出的一部分内存,缓冲区溢出是当缓冲区中的写操作不断超过结束时(写入具有不同目的的内存) 。这总是一个错误。

缓冲区溢出攻击是指使用此错误来完成程序作者无法实现的内容的攻击。<​​/ p>

答案 10 :(得分:0)

给出正确答案:要了解本主题,您可能希望立即收听Podcast安全性。在Episode 39(前一阵子),他们深入讨论了这一点。这是一种快速的方式,无需消化整本书就能获得更深入的理解。

(在链接中,您将找到具有多个大小版本的存档以及成绩单,如果您更倾向于视觉导向)。音频不是这个主题的完美媒介,但史蒂夫正在努力解决这个问题。

答案 11 :(得分:0)

缓冲区溢出是指已插入的字符超出了分配的内存可以容纳的范围。