我听说过缓冲区溢出,想知道如何引发它。
有人能展示一个小的缓冲区溢出例子吗?还有它们被用于什么?
我听说过缓冲区溢出,想知道如何引发它。
有人能展示一个小的缓冲区溢出例子吗?还有它们被用于什么?
缓冲区溢出的经典示例:
// noone will ever have the time to type more than 64 characters...
char buf[64];
gets(buf); // let user put his name
缓冲区溢出通常不是故意发生的,而是由于所谓的 "off-by-one" 错误导致的。意思是您计算数组大小时错误了一位,可能是因为您忘记考虑终止的空字符,或者因为其他一些原因。缓冲区溢出基本上是指当一个经过设计的内存段(或缓冲区)被写入到其预期界限之外时。如果攻击者能够通过程序外部将此事发生,可能会导致安全问题,因为这可能允许他们操纵任意内存位置,尽管许多现代操作系统防止了最糟糕情况的发生。
虽然读取和写入超出预期边界通常被认为是一个坏主意,但是"缓冲区溢出"一词通常保留给写入超出边界的情况,因为这可能使攻击者轻松修改代码运行方式。有一篇关于缓冲区溢出以及它们被用来进行攻击的各种方式的很好的维基百科文章。
就如何自己编程实现一个缓冲区溢出而言,这只是一个简单的问题:
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)
无论程序能否编译通过以及运行时会发生什么,可能取决于您的操作系统和编译器。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:~#
这种情况与您从互联网上获取的旧式缓冲区溢出教程无关,也不适用于Aleph One教程,因为它们现在可能无法在您的系统中运行。
现在让我们创建一个易受缓冲区溢出攻击的程序。
---------------------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函数很危险,因为该函数不检查我们将输入多少字节。在您的C程序中编译时使用额外选项-fno-stack-protector和-mpreferred-stack-boundary = 2以去除堆栈保护。
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 root访问场景的缓冲区溢出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权限运行。
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)
程序收到段错误的返回代码。让我们输入更多字节并查看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;
}
snprintf
,它具有标准化的优点(ISO C 99)。还有asprintf
(GNU和BSD libc),g_strdup_printf
(Glib)。 - sleske这应该足以复现它:
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]);
}
这让你可以玩弄缓冲区溢出参数并调整它们以满足你的需求。书籍 "Hacking - The Art of Exploitation"(链接指向亚马逊)详细介绍了如何玩弄缓冲区溢出(显然仅作为一种智力锻炼)。
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编译程序。缓冲区溢出是指插入超出分配内存容量的字符。