我听说过缓冲区溢出,我想知道如何导致缓冲区溢出.
有人能告诉我一个小缓冲区溢出的例子吗?新的(它们用于什么?)
缓冲区溢出的经典示例:
// 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个字符"这样的游戏 - 它有一天会失败.请记住,一个火箭科学家说这个数字不会超过一定数量,因为火箭永远不会那么快.但总有一天,它是实际上更快,什么是导致整数溢出和火箭坠毁(这是关于一个错误的阿丽亚娜5,在历史上最昂贵的计算机的错误之一).
例如,而不是使用fgets
.而不是sprintf
使用snprintf
其中合适的和可用的(或仅仅是C++风格之类的东西istream和东西)
缓冲区溢出基本上是在内存的精心设计的部分(或缓冲区)写在其预期范围之外时.如果攻击者可以设法从程序外部实现这一点,则可能会导致安全问题,因为它可能会允许他们操纵任意内存位置,尽管许多现代操作系统可以防止出现这种情况.
虽然读取和写入超出预期范围通常被认为是一个坏主意,但术语"缓冲区溢出"通常保留用于在边界外写入,因为这可能导致攻击者轻松修改代码运行方式.维基百科上有一篇关于缓冲区溢出的文章以及它们可用于攻击的各种方法.
关于如何自己编程,这将是一个简单的问题:
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)
编译和运行时会发生什么可能取决于您的操作系统和编译器.
在现代的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#include 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 #include2 #include 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,等等...