缓冲区溢出攻击的剖析及防范方案

点击数:643 | 发布时间:2025-02-27 | 来源:www.6v3c.com

    摘 要伴随Internet及有关信息技术的飞速进步,网上的电商呈现出很大的增长势头,但投入的增多意味着风险也随之而来,互联网安全问题成为各种网上活动需要考虑的头等大事。本文重点探讨一下缓冲区溢出对计算机系统导致的害处。由于几十年来,缓冲区溢出一直引起很多紧急的安全性问题。近年由CERT/CC发布的忠告中关于缓冲区溢出漏洞占56.76%以上。本文第一讲解了缓冲区溢出的定义,从程序语言本身存在缺点,不够健壮的角度出发,对缓冲区溢出的原理进行了详细的讲解;第三,通过一个会致使缓冲区溢出的程序代码对缓冲区溢出攻击的产生进行了实例剖析,同时还对Unix操作系统下的缓冲区溢出攻击进行了有针对性的剖析,并总结出缓冲区溢出攻击的种类;最后,结合缓冲区溢出攻击的种类,从系统管理和软件开发两个角度提出了缓冲区溢出攻击的防范方案。关键词:缓冲区溢出 攻击

    Abstract
    With 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 carpnal 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, accorpng 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攻击。比如在Unix系统中,用一些精心撰写的程序,借助SUID程序(如FDFORMAT)中存在的缓冲区溢出错误就能获得系统超级用户权限,在Unix获得超级用户权限就意味着黑客可以随便控制系统。为了防止这种借助程序设计语言漏洞而对系统的恶意攻击,大家需要要仔细剖析缓冲区溢出攻击的产生及种类,从而做出相应的防范方案。二 缓冲区溢出攻击的剖析2.1缓冲区溢出攻击的产生C编程语言中,静态变量分配在数据段中,动态变量分配在堆栈段中,C语言允许技术员在运行时在内存的两个不同部分(堆栈和堆)中创建存储器。一般,分配到堆的数据是那些malloc或新建时获得的数据,而分配到堆栈的数据一般包含非静态的局部变量和所有按值传递的参数。大多数其它信息存储在全局静态存储器中。一个程序在内存中一般分为程序段、数据段和堆栈三个部分。程序段里为程序的机器码和只读数据,这个段一般是只读代码,故禁止对程序段进行写操作。数据段放的是程序中的静态数据。存储器主要分为三个部分,一是文本地区,即程序区,用来存储程序指令,只读属性;二是数据地区,它的大小可以由brk系统调用来改变;三是堆栈,其特征是LIFO。当C程序调用函数的时候,第一将参数压入堆栈,然后保存指令寄存器(IP)中的内容作为返回地址(RET),放入堆栈的是地址寄存器(FP),然后把目前的栈指针(SP)拷贝到FP,作为新的基地址,并为当地变量留出肯定的空间,把SP减去适合的数值。计算机实行一条指令,并保留指向下一条指令的指针(IP)。当函数或过程被调用的时候,在堆栈中被保留下来的指令指针将被作为返回地址(RET)。实行完成后,RET替换IP,程序接着继续实行本来的步骤。这里有一个直观的缓冲区溢出的小例子:void function{char buffer[16];strcpy;}Void main{int I;char buffer[128];forbuffer[I]=A;buffer[127]=0;function;printf;}在函数function中,将一个128字节长度的字符串拷贝到只有16字节长的局部缓冲区中。在用strcpy函数前,没进行缓冲区边界检查,致使从buffer开始的256个字节都将被*str的内容A覆盖,包含堆栈指针和返回地址,甚至*str都将被A覆盖。再看看堆栈的结构,因为栈式内存分配具备一条指令即可为子程序分配全部局部变量的存储空间的特征,分配和去配的开销极低,高级语言一般在堆栈上分配局部存储空间。同时,堆栈也被用来存放子程序的返回地址。对C语言来讲,调用函数的语句f被翻译为如下指令:push argn…….push arg1push ncall f而函数的入口则翻译为如下入口指令(在Intel X86上)pushl ebpmov esp,ebpsub esp,m #m为f的局部变量的空间大小在Intel X86体系结构上,堆栈是从上向下成长的,因此调用以上函数时的堆栈结构如图1所示:arg1……argnn返回地址ebp局部变量高地址低地址
    图1 堆栈结构图比如,调用以下函数时Void f{ char dest[4]; memcpy;}堆栈及变量的地方如图2所示:srcl返回地址ebpdest[3]dest[2]dest[1]dest[0]高地址低地址图2 堆栈及地方的变量图从堆栈结构可以看到,当用精心筹备好的地址改写返回地址时,即可把控制步骤引向我们的代码。C2级操作系统提供了进程空间的隔离机制,因此,借助缓冲区溢出攻击可以在别的进程上下文中实行我们的代码,从而绕过操作系统的安全机制,下面是一个例子:Void main{ char *str[2]={”/bin/sh”,0}; exec ;}编译后反编译,并加以整理,得到与以上程序等价的机器码:“xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00”“x00xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80”“xb8x01x00x00x00xbbx00x00x00x00xcdx80xe8xdlxffxff”“xffx2fx62x69x6ex2fx73x68x00x89xecx5dxc3”事例程序如下:/ test /char shellcode[]={“xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00”“x00xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80”“xb8x01x00x00x00xbbx00x00x00x00xcdx80xe8xdlxffxff”“xffx2fx62x69x6ex2fx73x68x00x89xecx5dxc3”};void f{char dest[4];memcpy;}void main{int shellentry[3];shellentry[0]=shellcode;shellentry[1]=shellcode;shellentry[2]=shellcode;f;}由以上程序可以看出缓冲区溢出攻击的重点:由于memcpy并不检验边界,所以dest溢出时,使shellcode的地址覆盖了子程序的返回地址,当子程序实行ret指令时,CPU的指令指针寄存器EIP指向shellcode,从而实行shellcode。这里讨论一个日常的Unix环境下,借助缓冲区溢出的到一个Shell的行攻击办法的达成。其中,S代表Shellcode,A代表填写的返回地址,因为Shellcode在虚地址的高档,所以这个返回地址(32bit)一般不会含有零字节:(1) 启动一个一个Shell的代码——Shellcode的获得一般的获得办法是先用高级语言撰写同样功能的程序,然后用调试工具抽取需要的二进制代码。高级语言程序如下:shellcode.c#include<stpo.h>void main{char *name[2];name[0]=”bin/sh”;name[1]=NULL;execve;exit;}把上述程序编译之后,可以用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指令压入堆栈的串 # 地址送到esimovl %esi, 0x8 # 3 bytesmovb $0x0, 0x7 # 4 bytesmovl $0x0, 0xc # 7 bytesmovl $0xb, %eax # 5 bytesmovl %esi, %ebx # 2 bytes # 实行execve;leal 0x8 , %ecx # 3 bytesleal 0xc , %edx # 3 bytesint $0x80 # 2 bytesmovl $0x1,%eax # 5 bytes #实行exitmovl $0x0,%ebx # 5 bytesint $0x80 # 2 bytescall –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{_asm_;}void main{printf);}一般,进程运行时向堆栈中写入的数据不会超越数百个字节或数千个字节,有了这个起始地址,用简单的一个个尝试的办法也是可以攻击的。但显然这不是一种效率高的办法。解决的方法是在缓冲区前端填充几百字节NOP指令,只须猜测的地址落在NOP指令序列中,仍可以实行shellcode,从而成倍地增加猜中的机会。(3) 攻击代码中字节代码为零的消除Unix的程序中很多用了strcpy函数,shellcode中含有0x00,因为一般是攻击一个字符缓冲区,假如攻击代码中含有0,则它会被当成字符串的结尾处置,于是攻击代码被截断。消除的办法是对代码做适合的变换,因此在这里需要用一些汇编程序设计方法,把shellcode转换成不含0x00的等价代码。(4)被攻击的缓冲区非常小的状况当缓冲区太小,可能使NOP部分或shellcode部分覆盖返回地址ret,致使缓冲区起址到返回地址的距离不足以容纳shellcode,如此设定的跳转地址就没用上,攻击代码不可以被正确实行。一个办法就是借助环境变量。当一个进程启动时,环境变量被映射到进程堆栈空间的顶端。如此就能把攻击代码(NOP串+Shellcode)放到一个环境变两中,而在被溢出的缓冲区中填上攻击代码的地址。譬如,可以把shellcode放在环境变量中,并把环境变量传入到要攻击的程序中,就能对有缓冲区溢出漏洞的程序进行攻击。借助如此办法,还可以设计非常大的攻击代码。2.2缓冲区溢出攻击的种类缓冲区溢出的目的在于扰乱具备某些特权运行程序的功能,如此就能让攻击者获得程序的控制权,假如该程序具备足够的权限,那样整个主机甚至服务器就被控制了。一般而言,攻击者攻击root程序,然后实行类似“exec”的实行代码来获得root的shell。但并不一直如此,为了达到这个目的,攻击者需要达到如下两个目的:l 在程序的地址空间里安排适合的代码l 通过适合地初始化寄存器和存储器,让程序跳转到安排好的地址空间实行。大家可以参考这两个目的来对缓冲区溢出攻击进行分类。1.在程序的地址空间里安排适合的代码有两种在被攻击程序地址空间里安排攻击代码的办法:(1) 植入法:攻击者向被攻击的程序输入一个字符串,程序会把这个字符串放到缓冲区里。这个字符串所包括的数据是可以在这个被攻击的硬件平台运行的指令流。在这里攻击者用被攻击程序的缓冲区来存放攻击代码,具体方法有以下两种差别:a.攻击者不必为达到此目的而溢出任何缓冲区,可以找到足够的空间来放置攻击代码;b.缓冲区可设在任何地方:堆栈(存放自动变量)、堆(动态分配区)和静态数据区(初始化或未初始化的数据)。(2) 借助已经存在的代码有时攻击者所要的代码已经存在于被攻击的程序中了,攻击者所要做的只不过对代码传递一些参数,然后使程序跳转到想要实行的代码那里。譬如,共及代码需要实行“exec”,而在libc库中的代码实行“exec”,其中arg是一个指向字符串的指针参数,那样攻击者只须把传入的参数指针改向指向“/bin/sh”,然后调转到libc库中相应的指令序列即可。2.控制程序转移到攻击代码的办法所有这类办法都是在试图改变程序的实行步骤,使之跳转到攻击代码。其基本特征就是给没边界检查或有其他弱点的程序送出一个超长的缓冲区,以达到扰乱程序正常实行顺序的目的。通过溢出一个缓冲区,攻击者可以用几乎暴力的办法(穷尽法)改写相邻的程序空间面直接跳过系统的检查。这里的分类基准是攻击者所寻求的缓冲区溢出的程序空间种类。原则上可以是任意的空间。譬如起初的Morris Worm(莫尔斯蠕虫)就是用了fingerd程序的缓冲区溢出,扰乱fingerd要实行的文件的名字。事实上很多的缓冲区溢出是用暴力的办法来寻求改变程序指针的。这种程序不一样的地方就是程序空间的突破和内存空间的定位不同。通常来讲,控制程序转移到攻击代码的办法有以下几种:(1) 函数返回地址每当一个函数调用发生时,调用者会在堆栈中留下函数返回地址,它包括了函数结束时返回的地址。攻击者通过溢出这类自动变量,使这个返回地址指向攻击代码,如此就通过改变程序的返回地址,当函数调用结束时,程序跳转到攻击者设定的地址,而不是原先的地址。这种的缓冲区进出被叫做“stack smashing attack”,是现在常见的缓冲区溢出攻击方法。(2) 函数指针“Void”中声明了一个返回值为Void函数指针的变量foo。函数指针定位任何地址空间,所以攻击者仅需在任何空间内的函数指针附近找到一个可以溢出的缓冲区,然后溢出来改变函数指针,当程序通过函数指针调用函数时,程序的步骤就会发生改变而达成攻击者的目的。(3) 长跳转缓冲区在C语言中包括了一个简单的检验/恢复系统,称为“setjmp/longjmp”,意思是在检验点设定“setjmp”,用longjmp“来恢复检验点。然而,假如攻击时可以进入缓冲区的空间,那样“longjmp”事实上是跳转到攻击者的代码。像函数指针一样,longjmp缓冲区可以指向任何地方,所以攻击者所要做的就是找到一个可供溢出的缓冲区。一个典型的例子就是Perl 5.003,攻击者第一进入用来恢复缓冲区溢出的longjmp缓冲区,然后诱导进入恢复模式,如此就使Perl的讲解器跳转到攻击代码上了。3.综合代码植入和步骤控制技术最简单和容易见到的溢出缓冲区攻击种类就是在一个字符串里综合了代码植入和激活记录。攻击者定位一个可供溢出的自动变量,然后向程序传递一个非常大的字符串,在引发缓冲区溢出改变激活记录的同时植入了代码(由于C语言技术员一般在习惯上只为用户和参数开辟非常小的缓冲区)。代码植入和缓冲区溢出未必要在一次动作内完成,攻击者可以在一个缓冲区内放置代码(这时并不可以溢出缓冲区),然后攻击者通过溢出另一个缓冲区来转移程序的指针。如此的办法一般用来解决可供溢出的缓冲区不够大(不可以放手全部的代码)。假如攻击者试图用已常常驻的代码而不是从外部植入代码,他们一般需要把代码做为参数。举例说明,在libc(几乎所有些C程序都用它来连接)中的一部分代码段会实行“exec”,其中的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,CR,LF,-1等不可以在常见的字符串函数中用,由于这类函数一旦遇见这类终止符号,就结束函数过程了。b.随机符号:借助一个在函数调用时产生的一个32位的随机数来达成保密,使得攻击者不可能猜测到附加字节的内容。而且,每次调用,附加字节的内容都在改变,也没办法预测。通过检查堆栈的完整性的堆栈保护法是从Synthetix办法演变来的。Synthetix办法通过用准不变量来确保特定变量的正确性。这类特定的变量的改变是程序达成能预测的,而且只能在满足肯定的条件才能可以改变。这种变量大家称为准不变量。Synthetix开发了一些工具用来保护这类变量。攻击者通过缓冲区溢出而产生的改变可以被系统当做非法的动作。在某些极端的状况下,这类准不变量大概被非法改变,这是就需要堆栈保护来提供更健全的保护了。实验的数据表明,堆栈保护对于各种系统的缓冲区溢出攻击都有非常不错的保护用途,并能维持较好的兼容性和系统性能。早先大家报告的堆栈保护所能抑制的漏洞都在表一中列出。随后,大家用堆栈保护的办法重新架构了一个完整的Linux系统。然后大家用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服务器。假如这种服务器存在基于堆栈的攻击,那样攻击者就能随便地获得Web服务器的控制权,允许攻击者阅读隐秘的内容和肆意篡改主页的内容。同时,Web服务器也是对性能和带宽需要较高的一个服务器部件。大家用WebStone对带有和不带堆栈保护的Apache Web服务器进行了测试,测试的结果在表二中列出。和SSH一样,他们的性能几乎没不同。在顾客数目较少的状况下,带有保护的服务器性能比不带保护的略微好些,在推广客户端数目多的时候,不带保护的性能好些。在最坏的状况下,带保护的服务器比不带保护的要差8%的连接性能,而在平均延时上维持优势。象以前一样,大家把这类归结为噪声的影响。因此,大家的结论是:堆栈保护对Web服务器系统性能没重大的影响。3)指针保护:编译器生成程序指针完整性检查在堆栈保护设计的时候,冲击堆栈构成了缓冲区溢出攻击的容易见到的一种形式。有人推断存在一种模板来构成这类攻击(在1996年的时候)。从此,不少简单的漏洞被发现,推行和补丁了,不少攻击者开始用在第二部分中描述的更普通的办法推行缓冲区溢出攻击。指针保护是堆栈保护针对这样的情况的一个推广。通过在所有些代码指针之后放置附加字节来检验指针在被调用之前的合法性。假如检验失败,会发出报警信号和退出程序的实行,就好似在堆栈保护中的行为一样。这种策略有三点应该注意:a.附加字节的定位:附加字节的空间是在被保护的变量被分配的时候分配的,同时在被保护字节初始化过程中被初始化。如此就带来了问题;为了维持兼容性,大家不想改变被保护变量的大小,因此不可以简单地在变量的结构概念中加入附加字。还有,对各类型型也有不同附加字节数目。b.检查附加字节:每次程序指针被引用的时候都要检查附加字节的完整性。这个也存在问题;由于“从存取器读”在编译器中没语义;编译器更关心指针的用法,而各种的优化算法倾向于从存储器中读入变量。c.还有伴随不相同种类型的变量,读入的办法也每个都不一样。已经开发的指针保护的一个原型(还是基于gcc的),通过附加字节来保护静态分配的函数指针,但不适用于结构和数组种类。这个计划还远未完成。一旦这个项目完成了,那样用它和堆栈保护构成的可实行代码将不会遭到缓冲区溢出的攻击了。现在为止,只有极少一部分用非指针变量的攻击能逃脱指针保护的测试。但,可以通过在编译器上强制对某一变量加入附加字节来达成测试,这个时候需要技术员自己手工加入相应的保护了。(7)借助编译器将静态数据段中的函数地址指针存放地址和其他数据的存放地址离别大家了解缓冲区溢出的一个基本条件是在缓冲区的高地址附近存放着可供溢出覆盖的函数地址指针,假如大家破坏了这一条件,就会使缓冲区溢出不可以覆盖函数地址指针。譬如,大家可以假定编译器在编译程序时会将静态数据段中的函数地址指针存放地址和其他数据的存放地址隔开相当大的一段距离,比如数10M,数100M,如此才会迫使攻击者只有送入长的出乎想象数据才能抵达函数地址指针存放地址并覆盖它,以如此的不可操作性来制止攻击者达成攻击意图。另外,大家还可以使这两种数据段间的线形地址空间不分配物理地址并设置为不可读写,这类都可以通过硬件达成,如此每当缓冲区溢出进入这段地区,就会出现地址保护错误,从而阻止了缓冲区溢出攻击。另外一种较为简洁的办法是一直维持使静态数据段中的函数地址指针存放地址低于其他数据的存放地址。但这个办法只是预防了静态数据段中的缓冲区溢出攻击,而不可以防止堆的缓冲区溢出攻击,由于编译器和操作系统并不了解用户申请的内存是用来存放函数地址指针还是其他数据,从而没办法为其隔离分配内存。表1是针对静态数据段、堆栈和堆的缓冲区溢出的防范方案,其中没把边界检查包含在内,由于它能有效地预防所有些缓冲区溢出,但其运行开销也是惊人的。表的最上面一行是攻击代码所植入的内存空间,表的最左面一列是溢出办法,而中间单元就是相应的防范方案。表1 缓冲区溢出的防范方案堆栈(Stack Buffer)堆(Heap Buffer)静态数据段(Static Buffer)活动记录堆栈保护非实行的缓冲区堆栈保护堆栈保护函数指针指针保护非实行的缓冲区指针保护指针保护长跳转缓冲区指针保护非实行的缓冲区指针保护指针保护其它变量手工的指针保护非实行的缓冲区手工的指针保护手工的指针保护为了预防静态数据段、堆栈和堆这三种数据段中的一个缓冲区溢出覆盖另一个段中的函数地址指针,大家应该为这三种数据段隔离分配线形地址空间。四 总结缓冲区溢出是当今非常时尚的一种互联网攻击办法,它易于攻击而且风险紧急,给系统的安全带来了很大的隐患。因此,怎么样准时有效地测试出计算机互联网系统入侵行为,已愈加成为互联网安全管理的一项要紧内容。缓冲区溢出的漏洞一般有以下几种状况:(1) 系统漏洞如操作系统、服务器程序、数据库程序等,这种漏洞可以通过升级软件、打安全“补丁”等办法来解决。(2) 应用软件在软件开发过程中,假如技术员没非常强的安全意识和好的编程习惯,也会产生不少安全漏洞。本文从上面的系统环境及程序设计语言角度,对现在主要的互联网攻击方法:缓冲区溢出攻击的出现和原理进行了详细的剖析,并依据缓冲区溢出攻击的种类提出了相应的防范方案。然而,高速发展的互联网技术还需要大家继续对各种黑客攻击系统的办法做更多的关注和应对手段的探索,为互联网安全做出更大的实践意义的研究。参考文献[1] 张小斌,严望佳,黑客剖析与防范技术[M] 北京清华大学出版社,1999。[2] 陈意云 编译原理和技术[M],合肥,中国科技大学出版社,1997。[3] 汪立东,方滨兴,Unix缓冲区溢出攻击:技术原理、防范与测试,计算机工程与应用,2000(2)。[4]谭毓安,互联网攻击防护编码设计,北京期望电子出版社,2002。[5]www.nsfocus.com

  • THE END

    声明:本站部分内容均来自互联网,如不慎侵害的您的权益,请告知,我们将尽快删除。

专业院校

返回顶部

Copyright©2018-2024 中国考试人事网(https://www.bzgdwl.com/)
All Rights Reserverd ICP备18037099号-1

  • 中国考试人事网微博

  • 中国考试人事网

首页

财经

建筑

医疗