网络安全课程设计-基于缓冲区溢出的漏洞研究 下载本文

图4-2 两块存储区的距离

图4-3 输入长度为60字节

正常的存储空间如图4-4所示。现在我们增加输入字符串长度,输入长度刚好为64个字节的字符串,系统弹出了一个出错的信息,如图3.5.3-5所示。“Expression: *file!=_T(‘\\0’)”的含义为输入的文件名不能为\\0。此时buf1中存储的内容是64个字节长的字符串,而buf2中已经没有任何内容了。当我们输入了64个字节的字符串后,实际在内存中填充的并不仅仅是64个字节,而是65个字节。这是由于一个字符串的结束是以\\0为标志的,且是系统自动添加的。当我们填充了64个字节的字符串后,从低地址向高地址方向增长,buf1已经被填充满了,而这个\\0必然要突破buf1的存储空间,而扩展到了buf2存储空间的开始,所以buf2的起始地方就变成了\\0。当程序输出buf2字符串的时候,首先读到的是\\0,因而认为是一个空字符串,出现了图4-5所示的警告。

图4-4 内存中堆的存储空间

6

图4-5 警告

再次执行这个程序,继续增加输入字符串的长度为大于64个字节,而且刻意构造一个自定义的字符串“hostility”,是输入为“64字节填充数据”+“hostility”。可见buf1的内容长度是超过了64个字节的,而buf2的内容就变成了hostility。按照程序的流程,将会将内容写入到了文件名为hostility的文件当中去。如图4-6所示为在内存中堆的存储空间。

图4-6 内存中堆的存储空间

首先buf1填充了大于64个字节的字符串,余下的hostility就扩展到了buf2的空间之中。同样,字符串要以\\0表示结束。但是原先的buf2中的内容也有一个\\0表示字符串的结束,但是这个\\0落在了hostility的\\0的后边,所以系统当看到hostility后边的\\0时就认为字符串结束了,所以输出的是hostility。而读取buf1的内容时候,到存储空间结束也没有遇到\\0,那么它就继续往下读,直到遇见了\\0,所以它读取的长度已经超过了它本身分配的存储空间的长度。这样就构造了一个新的文件名覆盖了原先的内容,从而输出到一个我们定制的文件中,产生了基于堆的溢出。

由于程序所需的数据和空间并不是在一开始或程序编写的过程中就能完全确定的,这就要求有一种机制使我们能根据具体情况来分配所需的空间,堆正是应此需求而被提出的。

系统在创建新进程的时候为其分配相应的内存空间作为该进程的默认堆,当然在进程执行过程中用户可以根据需要申请新的堆空间,但不论是默认堆,还是用户后来申请

7

的堆,它们的组织结构是一样的,均如图4-7所示。其中,通过多次实验证实Windows平台下双指针区中重复存储了放有下次分配的起始地址的内存的地址。

高端内存 参数n …… 参数1 …… 局部变量1 …… 局部变量n 低端内存 图4-7 堆栈结构

高端内存 …… 双指针区 下一个空闲堆的管理结构 BufferN BufferN管理结构 …… Buffer1 Buffer1管理结构 堆的总体管理结构 低端内存 图4-8 堆的结构

由于堆在分配和释放时的动态性,使得利用堆进行缓冲区溢出攻击要比利用堆栈难得多,并且基于堆的缓冲区溢出攻击也不像基于堆栈的缓冲区溢出攻击那样有固定模式可用,这就是基于堆的溢出攻击在现实中比较少的原因。基于堆的缓冲区溢出攻击会因为堆溢出后产生的后果不同而有不同的利用方式。常见的利用方式有:

(1)改写内存参数。通过改写程序中重要变量的值,比如某个宇符串的长度等,使程序在运行中再产生其他溢出。这种方法一般只能改写数据段里而的东西,因为该位置相对固定并且可写。

(2)改变程序中函数指针值。通过溢出将该函数指针的值改为攻击者想让程序跳

8

转到的位置,当程序中调用该函数指针指向的函数时,系统转去执行那里的指令,从而达到攻击的口的。

从上而所述的两种利用方式也可以看出,基于堆的缓冲区溢出攻击并不像基于堆栈的那样简单,它没有较为通用的模式,所需的条件有时也比较苛刻。

4. 3基于LB库的缓冲区溢出攻击

上而两类攻击都是利用自己开发的攻击代码来执行操作,需要解决攻击代码的定位和跳转问题,而基于LIB库的缓冲区溢出攻击方法避兔了这两个问题并躲过了堆栈和堆不可执行的保护措施。该方法利用的是系统中现成的LIB库函数,不用手动植入攻击代码。这种方法之所以能成功,是因为有些系统函数(如WinExec( ), system( )等)是根据参数中给出的函数名称(and exe)来调用相应的函数。利用这种方法需要解决的问题是,将完成特定口的所需数据存放合适的位置,并想办法使程序跳转到特定库函数的入口。这种方法会用到上而两类攻击的一些技巧。

第五章 防范措施

面对缓冲区溢出攻击的挑战,根据各种攻击的机制,提出了不同的防范措施。现在常见的防范措施主要有如下几种。

5. 1安全编码

现在缓冲区溢出攻击之所以成为主要攻击手段,除了开发语言本身和操作系统设计策略上的缺陷外,人为因素也占了很大比重。由于以前人们的安全编码意识不强或者急于完成任务而忽略了对所编代码安全性的检查,所以造成隐患。提高编码者的安全意识,消除造成缓冲区溢出攻击的人为因素,可以大大减少这类攻击的发生。这一点可以从已发生过的攻击事件中得以证实。

安全编码首先尽量选用自带边界检查的语言(如Perl Python和Java等)来进行程序开发;其次在使用像C这类的开发语言时要做到尽量调用安全的库函数,对不安全的调用进行必要的边界检查。

编写正确代码,除了人为注意外,还可以借助现有的一些工具。例如,使用源代码扫描工具PurifyPlus可以帮助发现程序中可能导致缓冲区溢出的部分。

这种措施的缺点是:对人的依赖性太强,现实中程序员要受多方而因素的影响,很难取得满意的效果。

5. 2非可执行的缓冲区

由第4节,我们会发现除了基于LIB库的缓冲区溢出攻击外,所有其他的攻击方式之所以得逞正是由于操作系统赋予了缓冲区可执行的属性。取消缓冲区的可执行性属性,

9