基于shellcode检测的缓冲区溢出攻击防御技术研究

时间:2022-09-14 07:55:02

基于shellcode检测的缓冲区溢出攻击防御技术研究

摘要:缓冲区溢出攻击对计算机和网络安全构成极大威胁。从缓冲区溢出攻击原理和shellcode实现方式出发,提出针对shellcode的溢出攻击防御技术。描述shellcode获取控制权前后,从代码特点、跳转方式及shellcode恶意功能实现过程等方面入手,检测并阻止shellcode以对抗溢出攻击的几种技术。最后对这些技术的优缺点进行比较分析,指出其中较为优秀的方法,并就更全面提高系统安全性提出了一些建议。

关键词:

缓冲区溢出攻击;缓冲区溢出攻击防御;shellcode;Hook API

中图分类号: TP393.08

文献标识码:A

0引言

缓冲区溢出攻击是一种非常普遍而又危害极大的攻击,溢出发生后,导致任意代码执行,对系统安全构成极大威胁。远程攻击者能利用这种攻击获取系统控制权,窃取秘密信息或以该机器为跳板攻击其他主机。因此防御溢出攻击对保证系统乃至整个网络的安全尤为重要。

缓冲区溢出攻击防御技术也是计算机和网络安全界研究的重要内容。根据溢出及攻击的原理和特点,相应出现了多种防护技术。俄勒冈科学理工研究生学院计算机科学与工程系的Crispin Cowan等人在他们的文章中介绍并分析了编写正确的代码、不可执行缓冲区、数组边界检查和程序指针完整性检查等几种防御方法,研究了对这几种方法进行组合以更有效对抗缓冲区溢出攻击[1]。iDefense的Peter Silberman和Richard Johnson详细介绍了运行于Linux和Windows平台下的几种缓冲区溢出防御工具包括StackGuard、ProPolice、StackShield、StackDefender、OverflowGuard等,从可能的攻击模式出发,对这些工具的效果和性能进行了比较,并指出可能绕过这些防御的方法[2]。可以看出,现有的方法和工具都有各自的针对性和优缺点,从不同程度增加了攻击成功的难度。值得注意的是攻击者仍能想出各种方法绕过这些防护措施。防止缓冲区溢出攻击的最好方法就是从代码入手,防止溢出发生。然而就目前形势看,完全消除缓冲区溢出在近期内是不可能的,因此在溢出既已发生的情况下,采取有效措施对抗攻击仍然非常必要。本文将从shellcode角度出发,根据其代码表现形式、跳转方式及功能实现过程的特点,分析获取控制权前后两个阶段检测预防shellcode以对抗溢出攻击的方法。

1攻击原理和shellcode

研究针对shellcode的防御技术,必须先了解缓冲区溢出攻击原理和shellcode的实现。

1.1缓冲区溢出攻击原理

缓冲区是内存中用于存放数据的区域,当程序向缓冲区填充的数据长度超过缓冲区本身容量时,就会发生溢出。若溢出的数据覆盖了相邻缓冲区中存放的函数调用返回地址、函数指针等数据,就会导致程序运行流程改变,可能使恶意代码得以执行。

程序后

程序调用strcpy函数向只有6字节的缓冲区buf填入16字节的数据,导致溢出,覆盖了上一个ebp和返回地址ret等数据,函数返回时程序的运行流程被改变。攻击者利用缓冲区溢出实施攻击主要过程如图2。

1.2攻击代码shellcode表现形式及功能实现

shellcode是具有特定功能的指令集,攻击者将shellcode注入被攻击程序通过它来实现恶意功能。shellcode通常直接操作寄存器、调用API函数,因此必须是形如“\x90\x90\xeb\x16”的16进制机器码。

早期shellcode通常只获取远程shell或绑定端口,实现起来相对简单。在变形shellcode出现之前,shellcode通常包含两部分:NOPs(空指令)指令段和真正的功能段。加入NOP是为了使攻击者不用精确计算溢出点,增加攻击成功率。

图片

图3简单shellcode

为躲避检测,变形shellcode使用其他无效指令如[inc eax,dec eax]来代替NOP字段。并对 shellcode的功能部分进行加密,运行时再由解密引擎解密。变形shellcode形如图4。

图片

图4变形shellcode

简单shellcode功能有限,满足不了攻击者的要求。Windows下实用功能的shellcode通常要完成更复杂的操作,需要调用系统API函数,如LoadLibraryA(W), GetProcAddressA(W), CreateFileA(W),WriteFileA(W),GetModuleHandleA(W),CreateProcessA(W),WinExec等。获取内核模块基址和API函数地址的工作由shellcode完成。获取内核模块基址方法主要有:解析进程环境块(Process Environment Block, PEB)、利用结构化异常处理结构(Structured Exception Handling, SEH)和线程环境块(Thread Environment Block, TEB)结构、在内存中搜索kenel32模块等。获取API函数地址的方法也有多种,如解析PE(Portable Executable)模块的导出函数表和导入函数表等。这些都是比较成熟的技术,在介绍shellcode的文章中都有描述[3]。

2基于shellcode防护溢出攻击的原理

要利用溢出漏洞实施攻击,必须能在目标系统执行shellcode,否则攻击就失去了意义。这对已知或未知的漏洞都不例外。因此,如果能够采取措施阻止shellcode执行,不但能够抵御已知漏洞的攻击,也能使系统免受未知漏洞的攻击。从本文第一章的介绍可以看到,shellcode从代码表现形式、指令控制权的获取以及功能的实现过程,每一步都有其特定的形式,切断其运行过程的任何一步,就能阻止攻击成功。

下面将shellcode执行过程分为获取控制权前后两部分,分别描述如何检测并切断shellcode执行过程以对抗攻击。

3获取控制权前shellcode的检测和防御

3.1基于数据特点的shellcode检测技术

shellcode数据表现形式特点可被用来识别溢出攻击。一旦数据被发现具有这些特点,入侵检测系统将阻止其进入被保护系统或程序。

针对图3型shellcode,入侵检测系统只要在内存中发现一定数量的NOPs则会产生报警,认为存在shellcode。难点在于如何确定产生报警的NOP数量。数量过多会导致漏报,太少又会产生大量误报。变形shellcode的检测相对复杂。主要通过检测解密引擎、检测无效指令、模拟解密引擎、解密shellcode等方法来实现。

已有入侵检测系统使用上述方法,一旦发现shellcode立即报警,在有害数据进入系统或程序前阻止它,防止了溢出攻击。入侵检测程序NIDSfindshellcode通过检测无效指令识别shellcode,经实验发现产生报警的无效指令条数在80~90之间为最佳。

3.2模块基址重定位阻止shellcode获取控制权

对于通过覆盖返回地址跳转到shellcode的溢出攻击,一般需要形如JMP Register/CALL Register的指令实现跳转。为实现通用性,攻击者在不同系统和软件版本中寻找相对固定的跳转地址。如Windows下的通用跳转地址JMP ESP 0x7FFA4512/0x7FFA9C1B, Wnidows 2000下通用JMP EBX 0x7FFA4A1B。如果跳转地址不正确,至多造成拒绝服务。

对模块基址重定位,改变跳转指令的地址,能抵抗这类溢出攻击。Windows操作系统提供了对DLL和EXE等模块重定位的机制,ReBaseImage()[4]能够实现模块的重定位功能。

4获取控制权后shellcode的检测和防御

4.1监控shellcode获取模块和API地址的行为以防御缓冲区溢出攻击

shellcode必须自己获取运行中调用的API函数地址。如果能监控shellcode获取模块和API地址的行为,就能发现进而阻止溢出攻击。

为获取API地址,shellcode必须知道该API所在模块地址,获取模块地址的方法[3]有:第一种方法通过解析PEB获取;

第二种方法搜索进程SEH链表获取Kernel32.UnhandledExceptionFilter的地址,再由该地址对齐追溯获得kernel32的基地址;

第三种方法利用TEB,类似于第二种方法,但只适用于Windows NT;

第四种方法直接在内存中搜索kernel32模块。一般应用程序都会加载kernel32.dll。在内存中先搜索匹配模块的“MZ”和“PE”,再跟据模块结构比较模块名是否为“kernel32”。

得到模块基址后,shellcode所需API地址可以通过解析模块的导出地址表(Export section)和导入地址表(Import Address Table, IAT)等方法获得,也可先在kernel32.dll中搜索GetProcAddress的地址,再用GetProcAddress(“模块名”, “函数名”)获取其他API地址。shellcode所需模块也可通过LoadLibrary(“模块名”)加载。

可以看到shellcode需要分析和利用PEB、SEH、TEB、导出函数表、IAT等结构来获取模块和API地址。如果能对这些结构进行保护和监控就能阻止shellcode正确执行。

举个例子说明如何保护导出函数表:先把存放在PE文件导出函数表中的函数地址考入预先分配的内存,再将原来导出表中的函数地址设为NULL,然后将函数地址指针指向预先分配的那块内存,并将该内存页面属性设为不可访问(PAGE_NOACCESS,对具有该属性页面的任何访问都会引发访问异常)。这样当shellcode解析导出函数表时就会引发异常,从而执行到监控程序,在监控程序中判断该异常是否因shellcode引发。原理如图6所示。

对其他结构的保护采用类似原理。监控程序主要执行以下操作:

1) 监控程序接管KiUserExceptionDispatcher函数,该函数是Windows异常处理过程中很重要的一个步骤,它负责派发用户级空间发生的所有异常到异常链中的异常处理函数地址,当用户层空间发生异常时就会调用该函数。对该函数的接管通过Hook来实现。这样,当异常发生时就会执行到监控程序;

2) 在监控程序中检查当前异常处理结构是否指向合法位置,因为通常shellcode为处理搜索内存引发的访问违反异常,都会有自己的异常处理程序。如果是非法的位置则认为是shellcode,结束当前进程。否则执行3);

3) 检查引起异常的指令地址,若来自可写区域则认为是溢出攻击,结束程序。否则执行4);4) 检查RtlEnterCriticalSection 指针是否被修改以检测堆溢出攻击。如果修改了则结束程序。否则执行5);

5) 检查该异常是否因为引用了被保护的内存地址(包括进程PEB、SEH、TEB、导出函数表、IAT等)。若不是程序正常执行。否则执行6);

6) 检查引用被保护地址的指令是否来自非法区域,如果是,则结束程序。否则为正常引用,由监控程序赋予该引用指令正确的地址,继续执行程序。

这种方法利用shellcode运行时的行为特征,对其利用的结构进行监控,一旦发现非法的引用就认为发生了溢出攻击,结束程序并报警。

4.2Hook API[5]防御shellcode

shellcode执行过程中几乎都要调用系统API函数。特别是LoadLibraryA(W), GetProcAddressA (W), CreateFileA(W),WriteFileA(W), GetModuleHandleA(W),CreateThreadA(W),WinExec等函数。即使shellcode已经正确获取了这些函数的地址,通过Hook这些函数,当它们被调用时,程序跳转到自定义的监控程序。在监控程序中,检查调用这些API时程序的运行状态信息,若发现调用来自于栈或内存中其他可写区域,则认为发生了缓冲区溢出攻击,监控程序立即结束该程序并产生报警。图7和代码简要描述了这种方法的实现过程。

当Hook的API被调用时,程序跳转到我们自定义的程序执行,在自定义程序中判断调用是否来自非法区域,如果是则结束程序并产生报警,否则执行原API。

程序前

5结语

对于基于数据特点的防护方法,攻击者能通过编写形式更加复杂多样的shellcode绕过这种检测。这种方法的另一个缺点是对系统的性能影响很大。

模块重定位防护法的问题是不可能所有的DLL都被重定位,且影响系统的稳定性和性能;运行中的exe通常被加载在0x00400000且无法改变,攻击者可使用当前EXE模块中的跳转地址。另外,从原理上看这种方法无法保护系统不受堆溢出攻击。

Hook API的方法能对抗一般的溢出攻击,但也存在一些问题:有些API存在多个版本,如果只Hook其中一种,不能起到完全防护作用;防护效果依赖于所Hook的API的数量。如果数量过多会影响程序的性能。更为严重的是,攻击者仍能通过伪造栈桢等方法来躲避这种防御。

监控运行时行为特征的方法,对shellcode利用的结构进行保护,一旦发现非法的引用就认为发生了溢出攻击,这种方法能够保护以下几种攻击:

1) 覆盖RtlEnterCriticalSection指针的堆溢出攻击;

2) 解析PEB的shellcode;

3) 所有利用了SEH的shellcode;

4) 暴力搜索内存的shellcode;

5) 解析PE文件导出函数表的shellcode;

6) 解析PE文件导入地址表的shellcode。

就目前形势看,这种方法封堵了shellcode执行的大部分路径,因此防护更全面。

基于shellcode的防护方法是众多防护方法的一种,是从shellcode的角度出发对缓冲区溢出攻击进行检测并防御。这种方法也不可能万无一失,为保证系统安全,建议同其他措施配合使用以提高系统安全性。比如对系统进行配置,关闭不必要的服务,隐藏系统信息以降低攻击成功率;及时安装补丁程序,防止已知漏洞攻击;安装防火墙以限制远程攻击者对本地系统的连接。同时,系统使用者也要提高警惕,定期对系统作全面地检查以确保系统安全。

6结语

本文分析了通过shellcode的检测和预防对抗缓冲区溢出攻击的技术。可以说检测并预防shellcode是阻止缓冲区溢出攻击成功的最后一道防线,在其他防御技术失败的情况下能够保护系统的安全,在缓冲区溢出漏洞完全消除前具有非常重要的价值。当然,这种技术还存在不足,如何能在不影响系统和程序正常执行及性能的情况下对shellcode作尽可能全面的监控和检查,还有待进一步完善。

上一篇:基于二元树复小波特征表示的人脸识别方法 下一篇:基于蚁群算法的Petri网最优路径序列寻找