2008年6月29日星期日

缓冲溢出漏洞的防御

缓冲溢出通常发生在C或者C++编程语言中。在2002年,针对缓冲漏洞的修正占了全部安全修正的22.5%1,而基于此的开发攻击所占的比重更高(2003年为75%)。

2展示了某典型的、使用固定长度缓冲区函数的栈在内存中的布局。当函数调用结束时,控制流根据返回地址继续。在x86处理器上,栈总是从反向生长的(即从高内存地址向低内存地址生长),所以缓冲区存储在返回地址之前。缓冲区溢出攻击,利用程序中的某个漏洞,如在图2函数调用中,放入超过缓冲区容量的数据,从而覆写返回地址指向一个恶意代码的起始地址,使得函数调用结束后就跳转到恶意代码起始处执行(图3)。

防御手段1——“金丝雀”(Canaries)法

通过在缓冲区和控制数据之间放置“金丝雀2”(或叫做“金丝雀”数据),来监视缓冲区是否溢出。当缓冲区溢出时,第一个损坏的数据就是“金丝雀”,之后对“金丝雀”数据进行验证就可预警发生了缓冲溢出。

Stack-Smashing Protector(SSP)GCC编译器的对“金丝雀”法的实现。SSP在缓冲区与控制数据之间放置“特殊符号”(已知符号,随机符号或随机符号与控制信息异或产物),之后检查这些符号来侦测溢出。(同时,SSP还把数组变量移向栈底来增加溢出的难度)

金丝雀”法的防御是有限的,比如不能防御通过溢出来改变结构体中的函数指针的攻击。

防御手段2——对内存页保护增加执行位

最近的CPU,都开始支持在内存的页保护中加入执行位(比如IntelAMDNX(No

eXecute )技术),从而阻止不在“可执行”页中的代码。这样,当溢出使得控制流跳转到恶意代码起始时,由于恶意代码所在页不具备执行位,故会产生页异常导致程序退出。

通过增加可执行位能够对缓冲溢出攻击提供较为全面的保护。但仍有不如意之处(以主流x86处理器为例):

  • 需要CPU支持

  • 需要启用PAE3扩展(这是由于传统页保护字段中没有多余位可用)。启用PAE会带来大约6%的性能上开销

  • 不能完全抵御缓冲漏洞的攻击。比如改写返回地址到system ()函数上,此操作并未对当前进程中注入新的可执行代码,从而绕过保护机制。

防御手段3——段限制法

使用段限制,即只有进程虚拟地址的前NM字节可执行。N值由操作系统来选定。

操作系统来保证所有程序的代码在段限制长度之内(图4左部),而数据,栈则在虚拟内存高地址处(图4右部)。当缓冲溢出执行恶意代码时,会触发段错误而导致程序退出。

段限制法在大多数程序中可行,但在某些非常少见的情形下(比如负责Linux图形的XFree86TM服务端)也许不是最好保护方式。此外,段限制法要求CPU支持分段机制。在不支持分段的情形下(比如CPU不支持或x86-64处理器运行于纯64位模式4)不能使用。不过此种情形下,处理器一般支持页保护字段中的“执行位”。

防御手段4——随机化内存布局

随机化内存布局基于一个观察:即攻击者只有知道缓冲区大体位置的才能实施攻击。在防御手段2中提到的如改写返回地址到system ()函数的攻击方法,也需要知道system函数的位置。这些可以通过攻击者在其电脑上运行相似的系统来观察到。通过随机化内存布局,使得攻击者不能猜测相关的地址,从而抵御攻击。

例如,随机化程序以下部分的内存地址:

  • 共享库

  • 程序堆

随机化内存布局需要编译器支持,并且重新构筑关键的程序。

防御手段234,即构成了RedHat ExecShield技术,并且融合到了Linux内核2.6.25版。

1数据源自http://cve.mitre.org/board/archives/2002-10/msg00005.html

2金丝雀对有毒气体敏感,在煤矿中,被用来预警有毒气体。

3PAE允许32CPU访问大于4GB的内存。参见Intel® 64 and IA-32 Architectures Software Developer's ManualVolume 3A: System Programming Guide, Part 14.13 PAGE-LEVEL PROTECTION AND EXECUTE-DISABLE BIT

4参见Intel® 64 and IA-32 Architectures Software Developer's ManualVolume 3A: System Programming Guide, Part 13.2.4 Segmentation in IA-32e Mode

0 评论: