VC++缓冲区溢出攻击的分析与防范策略

时间:2022-06-11 01:20:29

VC++缓冲区溢出攻击的分析与防范策略

[摘要]首先解释了缓冲区溢出的概念和溢出原理,并在VC++环境下比较了缓冲区非溢出、下标越界溢出、堆溢出、综合代码和激活记录溢出四种情况,由此总结出了避免缓冲区溢出的基本方法;其次通过一个导致缓冲区溢出的小程序对缓冲区溢出攻击的产生进行了实例分析,总结出缓冲区溢出攻击的类型;最后,从静态防范和动态防范两个方面提出了溢出的防范策略,缓冲区溢出攻击的分析与防范对网络信息安全具有非常重要的意义。

[关键词]缓冲区;堆溢出;静态防范:攻击

一、缓冲区溢出概念

缓冲区是用户为程序运行时在计算机中申请的一段连续的内存,它保存了给定类型的数据。缓冲区溢出指的是一种常见且危害很大的系统攻击手段,通过向程序的缓冲区写入超出其长度的内容,造成缓冲区的溢出,破坏程序的堆栈,使程序转而执行其他的指令,以达到攻击的目的。为了达到这个目的,攻击者必须达到如下的两个目标:(1)程序的地址空间里安排适当的代码。(2)通过适当的初始化寄存器和内存,让程序跳转到入侵者安排的地址空间执行。

二、缓冲区溢出概念危害

在当前网络与分布式系统安全中,被广泛利用的50%以上都是缓冲区溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕虫。而缓冲区溢出中,最为危险的是堆栈溢出,因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址,带来的危害一种是程序崩溃导致拒绝服务;另外一种是跳转并且执行一段恶意代码,比如得到shell,然后为所欲为。

三、缓冲区溢出的原理

通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,破坏程序的堆栈,造成程序崩溃或使程序转而执行其它指令,以达到攻击的目的。造成缓冲区溢出的原因是程序中没有仔细检查用户输入的参数。例如下面程序:

void function(char*str){

char buffer[16];

strcpy(buffer,str);}

上面的strcpy()将直接把str中的内容copy到buffer中。这样只要str的长度大于16,就会造成buffer的溢出,使程序运行出错。存在像strcpy这样的问题的标准函数还有strcat(),sprintf(),vsprintf(),gets(),scanf()等。当然,随便往缓冲区中填东西造成它溢出只会出现“分段错误”(Seg-mentation fault),而不能达到攻击的目的。最常见的手段是通过制造缓冲区溢出使程序运行一个用户shell,再通过shell执行其它命令。如果该程序属于root且有suid权限的话,攻击者就获得了一个有root权限的shell,可以对系统进行任意操作了。

缓冲区溢出攻击之所以成为一种常见安全攻击手段其原因在于缓冲区溢出漏洞太普遍,并且易于实现。缓冲区溢出成为远程攻击的主要手段其原因在于缓冲区溢出漏洞给予了攻击者所想要的一切,植入并且执行攻击代码。被植入的攻击代码以一定的权限运行有缓冲区溢出漏洞的程序,而得到被攻击主机的控制权。在1998年Lincoln实验室用来评估入侵检测的的5种远程攻击中,有2种是缓冲区溢出。在1998年CERT的13份建议中,有9份与缓冲区溢出有关,在1999年,至少有半数的建议和缓冲区溢出有关。在Bugtraq的调查中,有2/3的被调查者认为缓冲区溢出漏洞是一个很严重的安全问题。

缓冲区溢出漏洞和攻击有很多种形式,会在第二节对他们进行描述和分类。相应地防卫手段也随着攻击方法的不同而不同,将在第四节描述,它的内容包括针对每种攻击类型制定有效的防卫手段。

四、缓冲区溢出分类

1.堆栈溢出。破坏系统堆栈中被调用函数的返回地址是缓冲区溢出攻击者最常用的方法。首先通过输入大量的数据,以此造成缓冲区溢出,如果溢出改变了堆栈中保存的函数返回地址,则函数返回时,可使程序转向执行其它指令,以达到攻击的目的。攻击代码一般是类似于exce(“sh”)的这样能获得系统控制权的程序。通常情况下攻击者输入的超长字符串实际上是含一个二进制机器语言的小程序,这就是攻击代码。因此,改变的函数返回地址将指向溢出数据,也就是这段攻击代码。

2.修改函数指针溢出。如果程序中使用了函数指针变量,例如类似于“void(*foo)()”这样的语句。函数的指针可能位于堆栈静态数据区中等,如果被攻击者攻击的缓冲区点恰好就在某个指针的附近,攻击者就有机会修改函数指针,当程序通过指针变量调用该函数时就会转去执行攻击者事先放置的代码。

3.长跳转溢出。在c语言中包含了一个简单的检验/恢复系统,称为“set jmp/long jmp”,意思是在检验点设定“setjmp(buffer)”,用“long jmp(buffer)”来恢复检验点。如果攻击时能够进入缓冲区的空间,“long jmp(buffer)”实际上是跳转到攻击者的代码。像函数指针一样,long jmp缓冲区能够指向任何地方,所以攻击者所要做的就是找到一个可供溢出的缓冲区。例如Perl 5.003,攻击者首先进入用来恢复缓冲区溢出的long jmp缓冲区,然后诱导进入恢复模式,这样就使Perl的解释器跳转到攻击代码上了。

4.综合代码植入和激活记录溢出。最简单和常见的溢出缓冲区攻击类型就是在一个字符串里综合了代码植入和激活记录。攻击者定位一个可供溢出的自动变量,然后向程序传递一个很大的字符串,在引发缓冲区溢出改变激活记录的同时植入代码(C语言程序员通常在习惯上只为用户和参数开辟很小的缓冲区)。

代码植入和缓冲区溢出不一定要在一次动作内完成,攻击者可以在一个缓冲区内放置代码(这个时候并不能溢出缓冲区),然后攻击者通过溢出另一个缓冲区来转移程序的指针。这样的方法一般用来解决可供溢出的缓冲区不够大(不能放下全部的代码)。如果攻击者试图使用已经常驻的代码而不是从外部植入代码,他们通常必须把代码作为参数。例如在libc(几乎所有的C程序都用它来连接)中的一部分代码段会执“exec(something)”,其中something就是参数,攻击者使用缓冲区溢出改变程序的参数,利用另一个缓冲区溢出,使程序指针指向libc中的特定的代码段。

五、缓冲区溢出攻击的防范策略

1.下标越界溢出防范。只要在所有拷贝数据的地方进行数据长度和有效性的检查,确保目标缓冲区中数据不越界并有效,就可避免缓冲区溢出,更不可能使程序跳转到恶意代码上。但诸如C/C++自身是一种不进行强类型和长度检查的一种程序设计语言,而程序员在编写代码时由于开发速度和代码的简洁性,往往忽视了程序的健壮性,导致缓冲区溢出,因此,我们必须从程序语言和系统结构方面加强防范。很多不安全程序的出现是由于调用了一些不安全的库函数,这些库函数往往没有对数组边界进行检查。这些函数有strcpy()、sprintf()、str-cat()等,所以一种简单的方法是利用grep搜索源程序,找出对这些函数的调用,然后代以更安全的函数,如strncpy()替换strcpy()。进一步的查找可以是检查更广范围的不安全操作,如在一个不定循环中对数组的赋值等。

2.堆溢出的防范。缓冲区溢出的根本原因是没有数组边界检查,当数组被溢出时,一些关键的数据就有可能被修改,攻击代码也可以被植入。因此,对数组进行边界检查,使超长代码不可能植入,就完全没有了缓冲区溢出攻击产生的条件。只要数组不能被溢出,溢出攻击就无从谈起。

为了实现数组边界检查,则所有的对数组的读写操作都应当被检查,以确保对数组的操作在正确的范围内。最直接的方法是检查所有的数组操作,但会使性能下降很多,通常可采用一些优化的技术来减少检查次数。

上一篇:“教\学\用”的统一是优化英语教学的关键 下一篇:如何培养舞蹈艺术表现力