缓冲区溢出攻击与防御的研究
2014-10-21丛佩春王治杰
丛佩春 王治杰
摘 要 首先介绍了缓冲区溢出的基本概念,研究并总结了缓冲区溢出的原理和过程。还通过一些攻击示例介绍了系统如何被溢出攻击,描述了网络攻击者利用缓冲区溢出漏洞进行系统攻击的一般过程,最后简单讨论了几种缓冲区溢出的保护方法。
关键词 缓冲区溢出漏洞 缓冲区溢出攻击 缓冲区溢出保护
中图分类号:TP3 文献标识码:A
1缓冲区溢出的概述
安全漏洞与缓冲区溢出是分不开的,据微软发布的安全报告可知,目前最常见的安全问题80%以上都是缓冲区溢出产生的问题。市场上几乎每个应用程序和操作系统都存在黑客可能利用的缓冲区溢出漏洞,对于微软Windows系统来说是尤其严重,以至于微软在产品的新版本中采用了一种完全不同的解决方法。
2缓冲区溢出国内外研究现状
缓冲区溢出攻击作为一种主流的攻击手法,早在20世纪80年代,国外就有人開始讨论溢出攻击,例如1988年的Morris蠕虫,利用的攻击方法之一就是Fingerd的缓冲区溢出,这次蠕虫攻击导致全球6000多台机器被感染,损失巨大由此,缓冲区溢出问题逐渐得到人们的重视。1989年,Spafford提交了一份分析报告,描述了VAX机上的BSD版unix的Fingerd的缓冲区溢出程序的技术细节,从而引起了一部分安全人士对这个研究领域的重视。
缓冲区溢出的防御有一个主要的方面,第一个方面是从操作系统本身的机制对溢出进行遏制,提出了堆栈不可执行的技术;第二个方面是开发一些工具来保护堆栈2003年,Richard Jones和Paul Kelly开发了一个gcc的补丁,用来实现C程序数组边界检查,防范溢出问题。第三个方面是在溢出发生后,使用防火墙,入侵检测系统(IDS)等进行一些访问控制。
3缓冲区溢出攻击原理
当正常的使用者操作程序的时候,所进行的操作一般不会超出程序的运行范围,数据被添加到分配给该缓冲区的内存块之外,会发生缓冲区溢出,这时候就会出现数据泄漏或侵占了其它的数据空间。缓冲区溢出的攻击原理就是越过缓冲区长度界限向程序中输入超出其常规长度的内容,造成缓冲区的溢出从而破坏程序的堆栈,使程序运行出现特殊的问题转而执行其它指令。
4缓冲区溢出漏洞攻击方式
(1)在程序的地址空间里安排适当的代码
在程序的地址空间里安排适当的代码往往是相对简单的。如果要攻击的代码在所攻击程序中已经存在了,那么就简单地对代码传递一些参数,然后使程序跳转到目标中就可以完成了。攻击代码要求执行“exec(‘/bin/sh)”,而在libc库中的代码执行“exec(arg)”,其中的“arg”是个指向字符串的指针参数,只要把传入的参数指针修改指向“/bin/sh”,然后再跳转到libc库中的响应指令序列就可以了。当然,很多时候这个可能性是很小的,那么就得用一种叫“植入法”的方式来完成了。缓冲区可以设在:堆栈(自动变量)、堆(动态分配的)和静态数据区(初始化或者未初始化的数据)等的任何地方。也可以不必为达到这个目的而溢出任何缓冲区,只要找到足够的空间来放置这些攻击代码就够了。
(2)控制程序转移到攻击代码的形式
缓冲区溢出漏洞攻击都是在寻求改变程序的执行流程,使它跳转到攻击代码,最为基本的就是溢出一个没有检查或者其他漏洞的缓冲区,这样做就会扰乱程序的正常执行次序。通过溢出某缓冲区,可以改写相近程序的空间而直接跳转过系统对身份的验证。原则上来讲攻击时所针对的缓冲区溢出的程序空间可为任意空间。但因不同地方的定位相异,所以也就带出了多种转移方式。
(3)植入综合代码和流程控制
常见的溢出缓冲区攻击类是在一个字符串里综合了代码植入和Activation Records。攻击时定位在一个可供溢出的自动变量,然后向程序传递一个很大的字符串,在引发缓冲区溢出改变Activation Records的同时植入代码(权因C在习惯上只为用户和参数开辟很小的缓冲区)。这样的方法一般是用于可供溢出的缓冲区不能放入全部代码时的。如果想使用已经驻留的代码不需要再外部植入的时候,通常必须先把代码做为参数。在libc(熟悉C的朋友应该知道,现在几乎所有的C程序连接都是利用它来连接的)中的一部分代码段会执行“exec(something)”,当中的something就是参数,使用缓冲区溢出改变程序的参数,然后利用另一个缓冲区溢出使程序指针指向libc中的特定的代码段。
5缓冲区溢出的保护方法
(1)通过操作系统使得缓冲区不可执行,从而阻止攻击者殖入攻击代码。这种方法有效地阻止了很多缓冲区溢出的攻击,但是攻击者并不一定要殖入攻击代码来实现缓冲区溢出的攻击,所以这种方法还是存在很多弱点的。
(2)在程序指针失效前进行完整性检查。这样虽然这种方法不能使得所有的缓冲区溢出失效,但它的确确阻止了绝大多数的缓冲区溢出攻击,而能够逃脱这种方法保护的缓冲区溢出也很难实现。
(3)强制写正确的代码的方法。编写正确的代码是一件非常有意义但耗时的工作,特别像编写C语言那种具有容易出错倾向的程序(如:字符串的零结尾),这种风格是由于追求性能而忽视正确性的传统引起的。尽管花了很长的时间使得人们知道了如何编写安全的程序,具有安全漏洞的程序依旧出现。