彭小利,侯 翔
(四川文理学院 计算机学院,四川 达州635000)
随着全球信息时代进入互联网阶段,网络给人们的工作和生活都带来了很大的便利,而不法分子利用系统的漏洞对用户主机发起的攻击也使人们不寒而栗.据国家计算机网络入侵防范中心每个月对网络安全漏洞的监测和分析中可看到,所有漏洞中,缓冲区溢出漏洞几乎均居首位,如2011年9月份十大重要安全漏洞分析,[1]居首位的仍是缓冲区溢出漏洞.缓冲区溢出漏洞攻击占所有系统攻击总数的80%以上.[2]图1是缓冲区溢出攻击后的一种表现.很多损失巨大的网络攻击都是由缓冲区溢出攻击造成的.如2001年造成26亿美金损失的红色代码及其变体就是利用了Microsoft IIS的缓冲区溢出进行的攻击.
图1
缓冲区是计算机内存中堆栈的一个区域,用来暂时存放CPU输入输出的数据,是连续的临时存储区.在内存中,不同类型的数据存放在不同的区域中,用高级语言编写的程序的机器码存放于代码段CS;已经初始化的全局变量和静态局部变量放在数据段DS;未初始化的全局变量和局部变量放在BBS段;函数的参数、返回地址等放在栈;动态分配的内存则放在堆.内存存放数据是从低地址向高地址的方向存,而栈存放数据的方向刚好和内存存放的方向相反,如图2所示.现通过一段程序来说明溢出的过程:
#include<stdio.h>
int main()
{
char name[5];
printf("Please enter your name:");
gets(name);
printf("Hello",name);
return 0;
}
图2
编译并执行该程序,在对话框中输入“PXL”并回车,就会输出“Hello PXL”,重新执行程序并在对话框中输入“abcdefgabcdefgabcdefg”并回车,就会产生图1的结果,此时就发生了缓冲区溢出.原因在:name[5]将用来装用户输入的字符,串长度定义为5,第二次输入的“abcdefgabcdefgabcdefg”已经远远超过了5个字符的长度,每一个字符占一个内存单元,由于存放name的堆只定义了5个字符即5个内存单元,远不够用于存放用户输入的“abcdefgabcdefgabcdefg”,只好向内存的高地址方向存放,而栈中存放的是主程序的返回地址等信息,且向低地址方向依次存放,因此name数组中用户输入的内容就会占据栈中的信息,覆盖栈中的返回地址等数据.在main返回时,就会把覆盖在原返回地址上的用户输入的一部分数据作为返回地址,CPU试图执行该位置处的地址,结果出现错误,于是就有可能出现图1所示的系统提示,这就是一次缓冲区(堆栈)的溢出.
缓冲区内的数据及数据的长度是用户输入的数据决定的,而一般情况下程序都不会检查输入数据的长度,当输入的数据过长不但占据缓冲区还占据了边界外的空间,此时缓冲区溢出就随之产生,如该例中所示.
缓冲区溢出将会导致两种后果,一是因过长的字符覆盖了相邻的空间,导致原有程序运行失败甚至系统崩溃;二是不法者(后文均用攻击者)则可利用此漏洞发起攻击.
缓冲区溢出攻击就是攻击者利用缓冲区溢出漏洞而进行的攻击行为.通过向缓冲区中写入攻击者事先设计好的超长数据,以破坏堆栈,使得计算机转至攻击者编写的具有攻击目的的程序而运行之,缓冲区溢出示意图如图2(a)、(b)所示.
图2
发起缓冲区溢出攻击的攻击者的目的是为了取得被攻击主机的控制权,因此其攻击的对象主要是具有特权运行的程序,扰乱这些特权运行程序的功能,最终运行攻击者的程序,达到攻击的目的.
缓冲区溢出攻击的关键是向攻击主机植入攻击代码,然后利用缓冲区溢出漏洞控制攻击主机执行攻击代码.
利用缓冲区溢出攻击取得被攻击主机的程序运行权前,需要在被攻击主机上放置好攻击代码,可以是事先已经安放在被攻击主机外存中具有系统功能调用的程序,也可以是存放于被攻击主机的代码段或数据段的代码,这些代码攻击者会通过邮件的方式发送到被攻击主机,也可以是攻击者安放在某些网页上,当用户在网站上进行下载操作甚至是进入静态网页时都有可能将这些攻击代码存入自己的主机.
攻击者通过检测被攻击主机的缓冲区漏洞,即通过试探性的攻击,向其输入一超长数据,使之改变被攻击主机原定的执行程序,将被攻击主机将要执行的指令指针指向攻击者事先植入的攻击代码.
一旦将指令指针指向事先安插好的攻击代码地址,攻击代码就开始执行,可以是攻击者设计好的任何非法操作.
由于很多软件开发人员都从C语言学起,并已经将C语言中的习惯应用在了其他的高级语言中,且很多软件都是用C语言所编写,因此要完全根除缓冲区溢出已不可能,但可尽量避免缓冲区溢出攻击.
在C语言的库版本中有很多可能引起缓冲区溢出的脆弱函数,如strcpy()、sprintf()、gets()、scanf(),以及在循环内的getc()、getchar()等,这些函数使用频率很高,而C语言在习惯上只为用户和参数分配一个很小的缓冲区,且不对用户输入的数据长度进行判断,因此程序员在编程时可加入数组上限等代码用于检测用户输入数据长度是否超长,从根源上阻止缓冲区溢出不被攻击者所利用.在使用C++、JAVA、C#等高级语言编写时,同样需要注意这个问题.
在缓冲区溢出漏洞存在的情况下,禁止未经授权的控制流的改变.即使攻击者检测到缓冲区溢出漏洞,也注入了攻击代码,但不允许控制语句指向攻击代码.
当缓冲区溢出漏洞存在,且被攻击者利用以修改了程序的控制流,使之转向攻击代码,但可设置系统禁止执行如exec()等系统调用函数的使用.
缓冲区溢出攻击是危害极大的网络攻击方式,本文从计算机用户使用过程中的常见现象出发来分析缓冲区溢出攻击,以引起人们的足够重视,就目前来说,要根除缓冲区溢出攻击已是不可能的,只能尽量多加防范免受攻击,除以上的防范方式以外,还可使用一些安全的编程语言和使用相对安全的标准库函数等软件技术;而软硬件具有相通性,[3]所以还有很多硬件技术也可辅助防御,最终做到减少损失.
[1]罗鸿彦,薛 质.Linux下缓冲区溢出的分析与利用[J].信息安全与通信保密,2008(8):133-135.
[2]彭小利.软硬件的相通性[J].四川文理学院学报,2010(9):57-59.
[3]张 君.缓冲区溢出分析与防御[J].安全视窗,2011(9):55-57.