缓冲区溢出是一种非常普遍、非常危险的
漏洞,在各种
操作系统、应用软件中广泛存在。利用
缓冲区溢出攻击,可以导致程序运行失败、系统宕机、重新启动等后果。更为严重的是,可以利用它执行非授权指令,甚至可以取得系统特权,进而进行各种非法操作。
概念
计算机程序一般都会使用到一些内存,这些内存或是程序内部使用,或是存放用户的输入数据,这样的内存一般称作缓冲区。溢出是指盛放的东西超出容器容量而溢出来了,在计算机程序中,就是数据使用到了被分配内存空间之外的内存空间。而缓冲区溢出,简单的说就是计算机对接收的输入数据没有进行有效的检测(理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符),向缓冲区内填充数据时超过了缓冲区本身的容量,而导致数据溢出到被分配空间之外的内存空间,使得溢出的数据覆盖了其他内存空间的数据。
危害
在计算机安全领域,缓冲区溢出就好比给自己的程序开了个后门,这种安全隐患是致命的。缓冲区溢出在各种操作系统、应用软件中广泛存在。而利用
缓冲区溢出漏洞实施的攻击就是缓冲区溢出攻击。缓冲区溢出攻击,可以导致程序运行失败、系统关机、重新启动,或者执行攻击者的指令,比如非法提升权限。
在当前网络与分布式系统安全中,被广泛利用的50%以上都是
缓冲区溢出,其中最著名的例子是1988年利用fingerd
漏洞的蠕虫。而
缓冲区溢出中,最为危险的是
堆栈溢出,因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址,带来的危害一种是程序崩溃导致拒绝服务,另外一种就是跳转并且执行一段
恶意代码,比如得到shell,然后为所欲为。
攻击
原理
通过往程序的
缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的
堆栈,造成程序崩溃或使程序转而执行其它指令,以达到攻击的目的。造成
缓冲区溢出的原因是程序中没有仔细检查用户输入的参数。
下面通过一个示例来详细看看什么是缓冲区溢出。程序的缓冲区就像一个个格子,每个格子中存放不同的东西,有的是命令,有的是数据,当程序需要接收用户数据,程序预先为之分配了4个格子(下图1中黄色的0~3号格子)。按照程序设计,就是要求用户输入的数据不超过4个。而用户在输入数据时,假设输入了16个数据,而且程序也没有对用户输入数据的多少进行检查,就往预先分配的格子中存放,这样不仅4个分配的格子被使用了,其后相邻的12个格子中的内容都被新数据覆盖了。这样原来12个格子中的内容就丢失了。这时就出现了缓冲区(0~3号格子)溢出了。
在上面示例的基础上来看一个代码实例,程序如下:
void function(char *str) {
char buffer[16];
}
上面的
strcpy()将直接把str中的内容copy到buffer中。这样只要str的长度大于16,就会造成buffer的溢出,使程序运行出错。存在象
strcpy这样的问题的标准函数还有strcat(),
sprintf(),vsprintf(),gets(),scanf()等。
当然,随便往
缓冲区中填东西造成它溢出一般只会出现“分段错误”(Segmentation fault),而不能达到攻击的目的。最常见的手段是通过制造
缓冲区溢出使程序运行一个用户shell,再通过shell执行其它命令。如果该程序有root或者suid执行权限的话,攻击者就获得了一个有
root权限的shell,可以对系统进行任意操作了。
缓冲区溢出攻击之所以成为一种常见安全攻击手段其原因在于缓冲区
溢出漏洞太普遍了,并且易于实现。而且,
缓冲区溢出成为远程攻击的主要手段其原因在于
缓冲区溢出漏洞给予了攻击者他所想要的一切:植入并且执行攻击代码。被植入的攻击代码以一定的权限运行有缓冲区
溢出漏洞的程序,从而得到被攻击主机的控制权。
在1998年Lincoln实验室用来评估
入侵检测的的5种远程攻击中,有2种是
缓冲区溢出。而在1998年CERT的13份建议中,有9份是是与
缓冲区溢出有关的,在1999年,至少有半数的建议是和缓冲区溢出有关的。在Bugtraq的调查中,有2/3的被调查者认为
缓冲区溢出漏洞是一个很严重的安全问题。
缓冲区溢出漏洞和攻击有很多种形式,会在第二节对他们进行
描述和
分类。相应地防卫手段也随者
攻击方法的不同而不同,将在第四节描述,它的内容包括针对每种攻击类型的有效的防卫手段。
漏洞
缓冲区溢出攻击的目的在于扰乱具有某些特权运行的程序的功能,这样可以使得攻击者取得程序的控制权,如果该程序具有足够的权限,那么整个主机就被控制了。一般而言,攻击者攻击root程序,然后执行类似“exec(sh)”的执行代码来获得
root权限的shell。为了达到这个目的,攻击者必须达到如下的两个目标:
2. 通过适当的初始化
寄存器和内存,让程序跳转到入侵者安排的
地址空间执行。
实验分析
2000年1月,Cerberus 安全小组发布了
微软的IIS 4/5存在的一个
缓冲区溢出漏洞。攻击该
漏洞,可以使Web服务器崩溃,甚至获取超级权限执行任意的代码。
微软的IIS 4/5 是一种Web服务器程序;因而,该
缓冲区溢出漏洞对于网站的安全构成了极大的威胁;它的描述如下:
浏览器向IIS提出一个
HTTP请求,在域名(或IP地址)后,加上一个文件名,该文件名以“.htr”做后缀。于是IIS认为
客户端正在请求一个“.htr”文件,“.htr”扩展文件被映像成ISAPI(Internet Service API)
应用程序,IIS会复位向所有针对“.htr”资源的请求到 ISM.DLL程序 ,ISM.DLL 打开这个文件并执行之。
浏览器提交的请求中包含的文件名存储在
局部变量缓冲区中,若它很长,超过600个字符时,会导致局部变量缓冲区溢出,覆盖返回
地址空间,使IIS崩溃。更进一步,在如图1所示的2K
缓冲区中植入一段精心设计的代码,可以使之以系统超级权限运行。
预防措施
缓冲区溢出是代码中固有的漏洞,除了在开发阶段要注意编写正确的代码之外,对于用户而言,一般的防范措施为
1、关闭端口或服务。管理员应该知道自己的系统上安装了什么,并且哪些服务正在运行
2、安装软件厂商的
补丁,漏洞一公布,大的厂商就会及时提供补丁
3、在
防火墙上过滤特殊的流量,无法阻止内部人员的
溢出攻击4、自己检查关键的服务程序,看看是否有可怕的漏洞
5、以所需要的最小权限运行软件
防范方法
缓冲区溢出攻击占了远程
网络攻击的绝大多数,这种攻击可以使得一个匿名的Internet用户有机会获得一台主机的部分或全部的控制权。如果能有效地消除
缓冲区溢出的漏洞,则很大一部分的安全威胁可以得到缓解。
有四种基本的方法保护
缓冲区免受
缓冲区溢出的攻击和影响。
保护
这个方法使得
缓冲区溢出不可能出现,从而完全消除了缓冲区溢出的威胁,但是相对而言代价比较大。
完整性检查
在程序
指针失效前进行完整性检查。虽然这种方法不能使得所有的
缓冲区溢出失效,但它能阻止绝大多数的
缓冲区溢出攻击。
非执行的缓冲区
通过使被攻击程序的
数据段地址空间不可执行,从而使得攻击者不可能执行被植入被攻击程序输入
缓冲区的代码,这种技术被称为非执行的缓冲区技术。在早期的
Unix系统设计中,只允许程序代码在
代码段中执行。但是Unix和MS Windows系统由于要实现更好的性能和功能,往往在
数据段中动态地放入可执行的代码,这也是
缓冲区溢出的根源。为了保持程序的兼容性,不可能使得所有程序的
数据段不可执行。
但是可以设定
堆栈数据段不可执行,这样就可以保证程序的兼容性。Linux和Solaris都发布了有关这方面的内核
补丁。因为几乎没有任何合法的程序会在
堆栈中存放代码,这种做法几乎不产生任何兼容性问题,除了在Linux中的两个特例,这时可执行的代码必须被放入堆栈中:
信号传递
Linux通过向进程
堆栈释放代码然后引发中断来执行在堆栈中的代码来实现向进程发送Unix信号。非执行
缓冲区的
补丁在发送信号的时候是允许缓冲区可执行的。
GCC的在线重用
研究发现gcc在
堆栈区里放置了可执行的代码作为在线重用之用。然而,关闭这个功能并不产生任何问题,只有部分功能似乎不能使用。
非执行堆栈的保护可以有效地对付把代码植入自动变量的
缓冲区溢出攻击,而对于其它形式的攻击则没有效果。通过引用一个驻留的程序的指针,就可以跳过这种保护措施。其它的攻击可以采用把代码植入堆或者静态数据段中来跳过保护。
编写安全的代码
编写正确的代码是一件非常有意义的工作,特别像编写C语言那种风格自由而容易出错的程序,这种风格是由于追求性能而忽视正确性的传统引起的。尽管花了很长的时间使得人们知道了如何编写安全的程序,具有安全漏洞的程序依旧出现。因此人们开发了一些工具和技术来帮助经验不足的程序员编写安全正确的程序。
最简单的方法就是用grep来搜索
源代码中容易产生漏洞的库的调用,比如对
strcpy和
sprintf的调用,这两个函数都没有检查输入参数的长度。事实上,各个版本C的标准库均有这样的问题存在。
此外,人们还开发了一些高级的查错工具,如fault injection等。这些工具的目的在于通过人为随机地产生一些
缓冲区溢出来寻找代码的
安全漏洞。还有一些静态分析工具用于侦测
缓冲区溢出的存在。
虽然这些工具帮助
程序员开发更安全的程序,但是由于C语言的特点,这些工具不可能找出所有的
缓冲区溢出漏洞。所以,侦错技术只能用来减少
缓冲区溢出的可能,并不能完全地消除它的存在。