姜燕,刘娜
(湖北医药学院 公共管理学院,湖北 十堰 442000)
缓冲区溢出是指向缓冲区中传输的数据超出了缓冲区所能容纳的最大长度,从而使提交的数据超过相应的边界而进入了其他区域[1]。缓冲区溢出攻击则是人为将大于缓冲区长度的数据传输到缓冲区,从而覆盖其他区域的数据,达到破坏性目的的操作。缓冲区溢出已成为一种十分普遍和危险的安全漏洞,存在于各种操作系统和应用软件中。利用缓冲区溢出攻击,可以导致程序运行失败、系统死机、计算机重新启动等后果,更为严重的是可以利用它执行非授权指令,甚至可以取得系统特权,进而进行各种非法操作。
程序执行时的内存从逻辑上可以分为代码区和数据区两大部分,而数据区又可以分为静态数据区、堆栈和堆3个部分。代码区存放可执行代码,只能读不能写,因此相对比较安全;数据区的数据经常随着程序的运行变化,是攻击发生的主要地方。本文以在堆栈中的溢出说明缓冲区溢出攻击的原理。
堆栈是在程序运行过程中由操作系统分配的内存区域,当程序中发生函数调用时,主要完成如下操作:首先把函数参数压入堆栈;然后向堆栈压入指令寄存器(IP)中的内容,作为返回地址 (RET);接下来放入堆栈的是基址寄存器(EBP),把当前的栈指针(ESP)拷贝到 EBP,做为新的基地址;最后把ESP减去适当的数值,为本地变量留出一定空间[2]。堆栈的情形如图1所示。
图1 堆栈的情形Fig.1 Stack situation
通过以下程序的执行过程可以说明对堆栈的操作和溢出的产生过程。
编译运行以上代码,输入“network”结果会输出network,其中对堆栈的操作是先在栈底压入返回地址,接着将栈指针EBP入栈,此时EBP等于现在的ESP,之后ESP减10,即向上增长10个字节,用来存放strbuffer数组。最后,从main返回,弹出RET里的返回地址并赋值给IP,CPU继续执行IP所指向的命令。如果我们输入的字符串长度超过10个字节,则由于输入的字符串太长,strbuffer数组容纳不下,只好向堆栈的底部方向继续写入。这些超出10字节部分的数据覆盖了堆栈的基地址寄存器、RET或函数参数部分的数据。从main返回时,就必然会把新的数据视作返回地址,CPU会试图执行新数据所指向的指令,结果出现难以预料的后果,这样就产生了一次堆栈溢出。
除了上述攻击外,还有针对堆的溢出攻击和基于lib库的缓冲区溢出攻击及格式化串[3]的攻击,无论是何种攻击,最根本的原因是由于C语言的char*这种数据结构的存在。在进行字符串处理时不能自动进行长度的检查,留下了很多的安全隐患。
缓冲区溢出攻击通常分为 3步[4]:1)在目标程序中植入攻击代码(shellcode);2)拷贝数据到需要溢出的缓冲区并破坏邻近区域的数据结构;3)获得shell执行攻击代码(shellcode)。其中第二步是进行攻击的主要环节。
面对缓冲区溢出攻击的挑战,根据各种攻击的机制,提出了不同的防范措施,这些措施包括技术层面的和非技术层面的,在技术层面有软件技术手段和硬件技术手段。常见的防范措施主要有如下几种。
根据缓冲区溢出的特点,只要对输入字符串或传递给函数的字符串参数的长度做边界检查,就可以有效地防止缓冲区溢出的发生,从而也就不可能发生任何缓冲区溢出攻击。因此,防范缓冲区溢出攻击的首要方法是使用带边界检查机制的安全计算机语言编写程序,如Java、Pascal、Perl和Python等。若使用的是C语言等不带边界检查机制的语言,则需要在编写程序时由程序员负责考虑各种可能发生缓冲区溢出的情况并加以避免或使用一些辅助技术进行检查。
2.1.1 人工检查
C语言中很多的字符串处理函数如strcpy()、strcat()、sprintf()、vsprintf()、gets()、scanf()等都不能进行数组边界的判定,因此需要在使用这些函数之前加上一些边界检查语句判断源字符串的长度,再进行拷贝操作。或使用C语言之后改进的带有边界检查功能的标准函数如strncpy()、strncat()、snprintf()、fgets()等替换上述函数。
此方法的优点是直接简单,缺点是对人的依赖性太高,而现实中程序员要受各个方面因素的影响,难以取得较好的效果。
2.1.2 静态发现技术
静态发现技术是在程序设计过程中,根据一定的规则发现源代码中潜在的漏洞,以便程序员进行改进。现在已经发展了很多成熟的静态发现技术,主要有 ITS4、Flawfinder、RATS和 BOON 等[5-6]。
ITS4(It’s the Software Stupid Source Scanner)是一种命令模式的交互式程序漏洞扫描工具,它能对C/C++程序的每一个函数进行代码分析,根据所建立的模式库匹配,然后依据危险级别给用户一个修改提示报告。
Flawfinder是基于Python语言开发的用来辅助进行C/C++程序安全审查的工具,运行于类UNIX平台。它内嵌了一些类似于ITS4的程序漏洞数据库,如缓冲区溢出、格式化串漏洞等,扫描速度较快。
RATS(Rough Auditing Tool for Security)作为程序安全性的审计工具,比Flawfinder支持更多的语言,提供了对C、C++、Perl、PHP以及 Python语言的漏洞扫描。
BOON (Buffer Overrun detectiON)是加州伯克利 David Wagner博士论文的实现原型,是专门针对C程序缓冲区溢出漏洞的检测工具。BOON借鉴面向对象思想,对每个字符串变量设置了两个属性,一个描述该字符串被分配的大小,另一个描述字符串实际被使用的大小。所有的字符串处理函数都将参考这两个属性对字符串进行操作。BOON通过扫描计算后得出源程序中的安全漏洞级别,程序员根据BOON的报告然后手动改正源程序的缺陷。BOON不检查有关格式化串漏洞的扫描。
完全找出程序中所有的错误是不可能的,所以静态发现技术是不完整的解决方案,它只是降低了程序被攻击的可能性,而且静态发现工具还需要维护一个与漏洞有关的不断变化的规则库。
2.1.3 动态防御技术
动态防御技术是从操作系统和编译系统角度,通过编译器自动地在程序中添加额外的代码以及让操作系统提供一些辅助的手段,来动态地发现运行的程序是否有缓冲区溢出 。 主要方法如下[5,7-8]:
Austin等人提出的安全指针方法将指针的表现形式扩展为安全指针,包含原指针的基地址及可访问范围信息。安全指针可以十分方便地实现边界检查。但由于改变了原有指针的表现形式,会与现有程序的代码形式不兼容,需要重新编写源代码。
Richard Jones和Paul Kelly针对安全指针的兼容性问题提出了另一种保留原指针形态的实时缓冲区边界检查方法。该方法将程序运行时创建的每个缓冲区视为一个对象,并动态地维护一张含有所有对象的列表。当需要进行缓冲区的指针操作时,通过将该指针映射到相应的对象来获得缓冲区的长度信息,从而实现边界检查。
Purify是C程序调试时查看存储器使用的工具,Purify检查所有的存储器存取,通过用Purify链接工具链接,可执行代码在执行的时候检查对数组的所有引用来保证其合法性。
LibSafe针对C库中的潜在有缓冲区溢出漏洞的函数重新包装,在函数调用前先计算目标地址有否被缓冲区溢出的可能,即要先进行边界检查,然后才调用以完成该函数正常的功能。LibSafe在实现时,其边界检查只是计算目标缓冲区是否会溢出到函数返回地址,对函数内部用到的局部变量(如函数指针)就检查不到,所以该方法不是万能的。LibSafe的优点是不需要重新编译源程序,只需更改系统的库函数即可。
此外,在编译器方面,当编译含有字符串数组的程序时编译器在程序开头预留出足够的空间后,接着就将这些空间进行初始化。编译器为函数中的字符串数组分配了空间,但并没有标志数组的末尾位置,所以根本无法正确测量字符串数组的确切大小。应该修改现有编译器的初始化操作,使它在字符串数组的末尾加上结束标志“ ”。这样,每当进行可能会引起溢出的操作时,总是先对源串和目的串的大小进行测量,然后将两者进行比较,如果源串的大小大于目的串的大小,那么就对源串进行截尾操作,或者直接报错退出。
动态防御技术的优点在于所有的防范工作均由系统和编译器来完成,减轻了程序员的编程负担,与常见的防范措施相比它有更普遍的应用性,而且更为有效。
另一类防范缓冲区溢出攻击的方法是将缓冲区属性设置为不可执行,使得攻击代码不能被执行,从而避免攻击的发生。 主要方法如下[1,5]:动态防御技术 PaX(Page Exec)通过使堆栈不可执行来阻止缓冲区溢出攻击的。程序导入内存运行的时候是经过页面映射机制,实现从逻辑地址到物理地址的转换,每一个逻辑地址都对应到具体的某一个物理页面,而每一个物理页面的性质是由属性位进行标识的。PaX通过扩展熟悉位的定义,将堆栈定义为不可执行,这样在程序运行过程中,进程所访问到的每个页面都会先被页面处理函数检查是否可执行的,从而就能比较顺利地发现基于堆或者栈的缓冲区溢出攻击。
返回地址在缓冲区溢出攻击中扮演了极其重要的角色,攻击者需要借助对它的修改来使程序转向选定的库函数或者向着预先植入的恶意代码方向去执行。通过在系统的其他地方对正确的返回地址进行备份,在返回前对两者进行核对,可以成功地阻断攻击。对于返回地址的保护,主要使用的是主动防御技术,此外还发展了一些硬件技术[5,9]。
StackGuard是基于覆盖函数返回地址则与返回地址相邻区域的数据也会受到改变的这一事实实现的。它通过对编译器gcc加补丁,使得在函数入口处能自动地在栈中生成canary标记,在函数调用结束时检测canary标记是否改变来发现并且阻止缓冲区溢出攻击。StackGuard是一种对函数返回地址进行完整性检查的基于编译器的技术。
针对StackGuard仅能保护栈中函数活动记录的返回地址,而对其他诸如函数指针类型的攻击不能有效防御的缺点,PointGuard做出了改进。因为存储在内存中的指针数据等容易受到缓冲区溢出的攻击,而存储在寄存器中的数据则不可能被类似的方法修改。基于这种思想,PointGuard在程序装入内存的时候,先将指针型数据加密,当程序访问到这些数据的时候,再在寄存器中进行解密。这样如果攻击者修改了内存中的指针数据,那么经过解密过程后必然使得指针不会指向攻击者期望的地方。
ProPolice主要思想来源StackGuard,不同之处在于ProPolice将函数中用到的局部变量重新排列,使得函数中用到的缓冲区紧邻Guard Value,这样栈中的其他变量就不会受到缓冲区溢出的影响,而且这时候如果缓冲区发生溢出,也会被Guard Value发现。
StackShield是比StackGuard安全性更好的使系统免受缓冲区溢出攻击的工具,它提供对堆栈中的函数返回地址和函数指针的保护。StackShield对函数返回地址的保护是通过全局返回地址栈或者返回地址范围检查实现的阁,这两种方法在编译的时候可选。在StackShield中,全局返回地址栈是系统实现的一个全局性的数组,里面保存着被调用到的函数的返回地址,这样当函数返回时,系统根本不管通常的堆栈中的函数返回地址,而直接用全局返回地址栈中的数据来代替。StackShield对函数指针的保护思想也比较简单。在正常情况下,函数指针应当指向代码区,而攻击者植入进来的代码通常在数据区或堆栈区,所以如果函数指针指向了这些区域,那我们就可以假定发生了缓冲区溢出攻击。
基于CPU硬件的缓冲区溢出攻击的防范是一种相对较新的方法,具体的做法是在微结构中增加一个秘钥寄生器,用这个秘钥来加密子程序的返回地址。在子程序调用时,加密的返回地址被插入到堆栈帧之间,而明文的返回地址仍然按原操作压入堆栈。这样,返回地址在堆栈中就有了两个拷贝。在子程序返回时,加密明文返回地址并与密文返回地址相比较。如果比较结果不一致,就能确定发生缓冲区溢出,CPU立即触发缓冲区溢出中断来防止攻击。所有与存取加密返回地址相关的堆栈操作、加解密操作和比较操作等都通过在微结构中增加一执行单元来实现。这些操作和硬件改变对软件都是透明的。与一些软件方法相比,提出的方法充分利用执行单元的并行性,从而极大地减少了对性能的影响。
文中首先对缓冲区溢出攻击的原理进行了探讨,然后给出了常见的几种防范措施。随着黑客攻击技术的不断发展,缓冲区溢出攻击的防范技术需从多方面进行进一步的改进,这需要网络安全专家和广大程序员的共同努力。
[1]王业君,倪惜珍,文伟平,等.缓冲区溢出攻击原理与防范的研究[J].计算机应用研究,2005(10):101-104.
WANG Ye-jun,NI Xi-zhen,WEN Wei-ping,et al.Buffer overflowattackprincipleandpreventionresearch[J].Application Research of Computers,2005(10):101-104.
[2]蒋卫华,李伟华,杜君.缓冲区溢出攻击:原理,防御及检测[J].计算机工程,2003,29(10):5-7.
JIANG Wei-hua,LI Wei-hua,DU Jun.Buffer overflow attack:principle, defense and testing[J].Computer Engineering,2003,29(10):5-7.
[3]姚建东,秦军,古志民.两种新的缓冲区溢出攻击原理及防范[J].计算机工程与应用,2003(10):156-159.
YAO Jian-dong,QIN Jun,GU Zhi-min.Two new buffer overflow attack principle and protection[J]. Computer Engineering and Application,2003(10):156-159.
[4]李娜,陈性元,车天伟.远程缓冲区溢出攻击的原理分析与检测[J].计算机工程与应用,2004(3):145-157.
LI Na,CHEN Xing-yuan,CHE Tian-wei.Remotebuffer overflow attack principle analysis and detection[J].Computer Engineering and Application,2004(3):145-157.
[5]林志强,夏耐,茅兵,等.缓冲区溢出研究综述[J].计算机科学,2004,31(9):110-113.
LIN Zhi-qiang,XIA Nai,MAO Bin,et al.Survey of research on buffer overflows[J].Computer Science,2004(3):145-157.
[6]常敏,卢超,袁春风.一种远程缓冲区溢出攻击的对策及其实现[J].计算机工程与应用,2003(33):145-157.
CHANG Min,LU Chao,YUAN Chun-feng.A remote buffer overflow attack countermeasures and its realization[J].Computer Engineering and Application,2003(33):145-157.
[7]肖道举,陈博文,陈晓苏.一种基于程序逻辑结构分析的缓冲区溢出攻击抵御方法[J].计算机工程与科学,2005,27(5):10-12.
XIAO Dao-ju,CHEN Bo-wen,CHEN Xiao-su.A program logic based on the analysis of the structure of the buffer overflow attack against method[J].Computer Engineering and Science,2005,27(5):10-12.
[8]刘圣卓,谢余强,魏强.缓冲区溢出攻击的防护措施[J].信息工程大学学报,2003,4(2):22-25.LIU Sheng-zhuo,XIE Yu-qiang,WEI Qiang.Buffer overflow attackprotectivemeasures[J].JournalofInformationEngineering University,2003,4(2):22-25.
[9]陈志强,严晓浪.CPU微结构中侦测和防止缓冲区溢出的实现[J].固体电子学研究与进展,2006,26(2):214-219.
CHEN Zhi-qiang,YAN Xiao-lang.CPU microstructure to detect and prevent the realization of buffer overflow[J].Research&Progress of SSE Solid State Electronics,2006,26(2):214-219.