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

我听说过缓冲区溢出,我想知道如何导致缓冲区溢出。 有人能给我看一个小缓冲区溢出的例子吗? 新的(它们用于什么?)     
已邀请:
缓冲区溢出基本上是在内存的精心设计的部分(或缓冲区)写在其预期范围之外时。如果攻击者能够设法从程序外部实现这一点,则可能会导致安全问题,因为它可能会让他们操纵任意内存位置,尽管许多现代操作系统可以防止出现这种情况。 虽然读取和写入超出预期范围通常被认为是一个坏主意,但术语“缓冲区溢出”通常保留用于在边界外写入,因为这可能导致攻击者轻松修改代码运行方式。维基百科上有一篇关于缓冲区溢出的文章,以及它们可用于攻击的各种方法。 关于如何自己编程,这将是一个简单的问题:
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)
编译和运行时会发生什么可能取决于您的操作系统和编译器。     
缓冲区溢出的经典示例:
// 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和东西)     
在现代的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
具有SUID根访问权限的缓冲区溢出C程序现在我们已经实现了。 现在让我们搜索我们需要将多少字节放入缓冲区以使程序分段出错
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)
现在你可以做下一步了......     
缓冲区溢出只是写入缓冲区的末尾:
int main(int argc, const char* argv[])
{
    char buf[10];
    memset(buf, 0, 11);
    return 0;
}
    
除了已经说过的内容之外,请记住,当发生缓冲区溢出时,您的程序可能会“崩溃”,也可能不会“崩溃”。它应该崩溃,你应该希望它 - 但如果缓冲区溢出“溢出”到你的应用程序也分配的另一个地址 - 你的应用程序似乎可以正常运行更长的一段时间。 如果您使用的是更高版本的Microsoft Visual Studio - 我建议使用stdlib中的新安全对应项,例如sprintf的sprintf_s,等等......     
这应该是重现它:
void buffer_overflow() 
{
    char * foo = "foo";
    char buffer[10];

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

    char accessViolation = foo[0];
}
    
“经典”缓冲区溢出示例是:
int main(int argc, char *argv[])
{
    char buffer[10];
    strcpy(buffer, argv[1]);
}
这使您可以使用缓冲区溢出参数并将它们调整到您的心灵内容。 “黑客 - 剥削的艺术”一书(链接到亚马逊)详细介绍了如何玩缓冲区溢出(显然纯粹是一种智力练习)。     
如果要检查程序是否存在缓冲区溢出,可以使用Valgrind等工具运行它。他们会为您找到一些内存管理错误。     
这是对您收到的答案的一般性评论。例如:
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使用“更安全”的高风险函数变体,如
memcpy
strcpy
。当编译器可以推导出目标缓冲区大小时,编译器会使用更安全的变体。如果副本超过目标缓冲区大小,则程序调用
abort()
。 要禁用FORTIFY_SOURCE进行测试,应使用
-U_FORTIFY_SOURCE
-D_FORTIFY_SOURCE=0
编译程序。     
在这种情况下,缓冲区是为特定目的而留出的一部分内存,缓冲区溢出是当缓冲区中的写入操作继续经过末尾(写入具有不同目的的内存)时发生的情况。这总是一个错误。 缓冲区溢出攻击是使用此错误来完成程序的作者无法实现的内容的攻击。     
给出正确的答案:要了解更多内容,您可能希望立即收听Podcast安全性。在第39集(前一段时间),他们深入讨论了这一点。这是一种快速的方法,无需消化整本书就能获得更深入的理解。 (在链接中,您将找到具有多个大小版本的存档以及成绩单,如果您更倾向于视觉导向)。音频不是这个主题的完美媒介,但史蒂夫正在努力解决这个问题。     

要回复问题请先登录注册