基于代码重用的漏洞利用及其防御技术研究

2016-05-14 11:05谷俊鲜明杜瑞祥陈恬
网络空间安全 2016年5期

谷俊 鲜明 杜瑞祥 陈恬

[摘要]代码重用漏洞利用技术是突破DEP和W⊕X的主流技术,其灵巧的攻击思想是现行漏洞利用技术的重要方向。因此研究基于代码重用漏洞利用技术及其防御方法,对提升程序安全和系统安全有着重要意义。论文将阐述基于代码重用的攻击原理和主要方法以及各种方法的优缺点和约束条件,总结当前主流防御技术的优缺点,并对下一步研究工作提出展望。

[关键词]代码重用;W⊕X;数据执行保护;ROP Android

[中图分类号]TN926-34;TP311 [文献标识码]A

1 引言

一直以来缓冲溢出漏洞对计算机网络安全造成巨大的威胁,每年都有数目众多的缓冲溢出漏洞被发现。攻击者不停地尝试利用系统的这些漏洞对程序进行攻击。早期的缓冲溢出漏洞利用都是向堆栈中注入恶意代码,劫持正常的程序控制流程,将程序跳转到自己预设的Shellcode,进而执行恶意代码,完成攻击。

后来,针对缓冲溢出漏洞利用方法的特点,出现了相应的防护技术。包括Solar Designers Stack Patch,通过栈不可执行。防止代码注入攻击:另外类似的还有DEP技术和W⊕X技术;通过硬件或软件的支持,防止注入数据被执行,保证内存区域不能既可执行又可写入。Linux PAX项目中,已经实现了W⊕X技术,通过将不需要执行的页设为不可执行,限制可写页和可执行页的转换,防止程序动态生成和执行代码:另外对需要动态生成代码的程序,PAX提供了解除限制的方法;同时Pax技术使得堆栈上的数据不可执行,并且对elf文件中的段进行限制,只允许实际的代码段执行,有效地防御了改写GOT表等攻击。在Windows XP sp2以上的系统中都应用了类似的DEP技术。

2 基于代码重用的漏洞利用

新的防御技术有效地限制了代码注入攻击,传统的代码注入攻击几乎不能成功。攻击者们进而研究新的方法。基于代码重用的漏洞利用不需要执行注入的数据,而是利用程序中已有的合法函数或者指令片段进行组合,实现特定功能。基于代码重用的漏洞利用被认为是能有效突破DEP和W⊕X的技术,它所需要的条件是:(1)存在能够控制栈区的缓冲溢出漏洞;(2)程序已有代码和指令的位置可以被找到。

比较流行的基于代码重用的漏洞利用方法主要五种。

2.1 Return-to-libc

Retum-to-libc通过栈溢出,把栈帧中的返回地址覆盖为一些关键函数的地址,并在栈中写入系统关键函数的参数。这样,不需要在栈上写Shellcode,就实现了溢出攻击。因为该攻击方法主要利用的是libc库的一些关键函数,故被称作Return-to-libc。Libc库为攻击者提供了相当多重要功能的函数,如system()、exec()等函数可以直接执行某个程序或者指令,使构造攻击更加简单。AttackLab中,介绍了Retum-to-libc的简单实验,在Nergal的文章中作者介绍了在应用了Pax技术的系统中实现Retum-to-libc的方法。该攻击方法一开始被认为不是图灵完全的。2011年由Minh Trall实现一种Return-into-libc攻击,并证明该攻击时图灵完全的。

然而,Rentum-to-libc仍然具有一些缺陷:(1)Retum-to-libc的功能依赖于系统中存在的库函数,如果将这些特定的函数进行修改或移除,Retum-to-libc可以构造攻击的能力受到严重限制;(2)Retum-to-libc只能顺序调用库函数,将函数的功能进行串联,它不能实现如分支操作、循环操作等诸多功能。

2.2 Return-Oriented Programming(ROP)

针对Retum-to-libc攻击的不足,ROP不调用系统库中已有的函数,而是把程序中已有的以Ret结尾的短指令片段(一般不超过5条指令)进行再组合,这些被利用的指令片段被称为Gadget。ROP攻击通过溢出,将Gadget的地址预先注入到栈中,然后劫持控制流,将程序执行流转向第一个ROP Gadget,当第一个ROP Gadget执行完毕后,利用Ret指令的特性,跳转到栈顶所指向的下一个Gadget。这样依次重复,可以将多个Gadget串联执行,以实现特定的功能。Shacham在X86架构的系统中成功实现了ROP攻击,随即该技术被扩展到其他多种架构。如SPARC、Atmel AVR、PowerPC、Z80、ARM架构和Harvard架构平台,如Sequoia的AVCAdvantage投票机器和苹果公司的iPhone系列移动操作系统,而Harvard平台是被认为对传统的代码注入攻击免疫的平台。在早期的攻击构造中,攻击者大多数使用手工的方法来组织Gadget构造攻击。后来,研究人员将这种攻击的构造的各个阶段进行自动化操作,将这种攻击变得更加实用。研究人员同时证明了ROP可以被应用于Rootkit攻击,通过复用内核中的代码,攻击者可以成功的隐藏进程。Bittau等实现了一种ROP变种攻击(BROP),利用ROP控制栈参数,执行系统调用。Schuster实现了另一种ROP变种攻击(COOP),能够很好地利用C++编写的面向对象代码进行攻击。

由于ROP攻击是利用Ret指令在Gadget之间跳转,有很明显的特征,很容易被检测到,同时这些Ret指令没有对应的Call指令,也可以通过检测Call-ret指令组合发现这类攻击。

2.3 Pop-jmp

Pop-jmp对ROP进行改进,为了消除ROP攻击的特征,Checkoway提出Pop-jmp攻击方法,它利用Pop给寄存器赋值,然后通过Jmp跳转到寄存器所指向的位置,它与Rop攻击区别不大。Ret指令语义上就等于Pop Eip和Jmp Eip两条指令连续调用。在文提出一种利用类Ret指令作为“指令蹦床”,使每个Gadget的Jmp指令先跳转到“指令蹦床”,然后通过“指令蹦床”跳转到下一Gadget。其原理如图1所示。

其具体步聚:(1)通过栈溢出将返回地址EIP覆盖为“指令蹦床”的地址;(2)通过“ebx赋值gadget”的popebx给ebx赋值为“指令蹦床”的地址;(3)通过“jmp ebx”跳转到“指令蹦床”;(4)用“指令蹦床”跳转到相应的Gadget;(5)Gadget执行完跳转至“指令蹦床”。

第4、5步循环跳转,完成攻击。从其攻击原理可以看出,Pop-jmp虽然消除了Ret指令特征,但产生了新的Pop_jnlp指令特征;而“指令蹦床”限制了可用的寄存器和Gadget,而且每次跳转到“指令蹦床”,容易被检测。

2.4 Jump-Oriented-Programming(JOP)和Branch Instruction-Oriented Programming(BIOP)

为了克服ROP和Pop-jmp的一些缺陷,TylerBletsch在文章提出JOP攻击。Pop-jmp攻击相比,它不限于用Esp引用Gadget地址,也不依靠Ret指令以及Pop-jmp指令进行Gadget串联。其攻击如图2所示,用Gadget分配表(Dispateh Table)存放数据和地址,每次Gadget执行完跳转到分配器。分配器通过对栈地址的加减,间接寻址下一个Gadget,这样栈地址相当于一个程序计数器。其优点是对栈的引用更加灵活,经过精心设计,可以利用栈中任意内存。

但是该方法存在两个缺点:与JOP攻击一样,Gadget分配器存在寄存器依赖性,使JOP功能受限;二是,难以找到gadget分配器。

文章提出的BIOP,对JOP改进并实现自动化攻击构造。它继承了JOP不依赖栈的控制流跳转,而且寻找一类同时含有Call,Ret联合Gadget,联合Gadget通过Call结尾的Gadget调用一个以Ret结尾的Gadget,使Gadget中的Call指令有对应的Ret指令。这种攻击不依赖栈的控制,而是依赖寄存器,每次跳转之前要给寄存器赋上正确的值,Gadget之间存在约束关系。

2.5 Sigreturn Oriented Programming(SROP)

Erik Bosman和Herbert Bos提出了SROP攻击方法,SROP攻击利用Sigreturn系统调用进行构造,该系统调用在Unix和Linux系统发送Signal的时候会被间接地调用。当内核向某个进程发起一个Signal,该进程会被暂时挂起(suspend),进入内核,然后内核为该进程保存相应的上下文,并跳转到之前注册好的SignalHandler中处理相应Signal,当Signal Handler返回之后,内核为该进程恢复之前保存的上下文,最后恢复进程的执行。上下文数据就保存在被挂起进程的栈上,这些数据帧被称为Signal Frame,Signal Frame被保存在用户进程的地址空间中的,是用户进程可读写的:从Sigreturn调用返回时,系统不对Signal Frame进行检查。攻击者可以伪造Signal Frame帧,发起Sigretum调用。将帧的关键数据精心设计,当调用返回时,就可以得到程序的控制流。通过连续的构造Signal Frame,可以发起多个系统调用,实现特定功能。这种方法简化了攻击流程,需要的先决条件也较少,没有Gadget的副作用,暂时没有出现专门的防御工具。

3 基于代码重用的漏洞利用的防御技术

随着基于基于代码重用的漏洞利用技术发展,其防御方法也随之产生。

3.1 基于控制流的防御方法

通过抑制非正常控制流以达到防止基于代码重用的漏洞利用。基于代码重用的漏洞利用本质是劫持控制流,只要保证控制流的正确和完整,就可以有效抑制基于代码重用的漏洞利用。Abadi、Zhang M实现了根据Control How Graph(CFG)检查控制流的方法,Davi设计了一种基于硬件的控制流检查方法,对Call、Ret、Jmp指令。CFG的生成能够确保程序按照正确的流程执行,但是动态生成CFG图很复杂而耗时,且难以防御COOPt=]攻击。基于控制流的防御由于CGF的不完善性还难以实用。

Cowan引入金丝雀方法(canary)检查控制流,该方法在返回地址附近预制一个Canary值,每次返回前检查Canary值是否被更改。它一定程度上能够保护栈的完整性,防止程序栈遭受攻击,进而保护程序控制流。但是Canary方法也不能完全保证控制流的完整和正确。Scut中,作者通过格式化字符串漏洞突破了Canary保护,BROO同样可以突破Canary。

由于性能、灵活度、算法复杂度、实现难度等各方面的考量,控制流的完整性检查还很不完善。

3.2 地址空间布局随机化(ASLR)

要完成基于代码重用的漏洞利用,必须先获取Gadget或者关键函数的地址。因此,Gadget和关键函数的实际首地址将决定程序的控制流,如果我们能够每次都随机加载程序库,程序库每次加载的起始地址随机化,让Gadget和关键函数的控制流程和地址失效。因此,ASLR将增加代码重用的难度。Shacham描述了针对ASLR机制的基于代码重用的漏洞利用方法和实际效果,并介绍了在32位机器上应对ASLR问题的方法。Bryant、Seacord等,通过空雪橇操作在应用了ASLR的系统中定位地址,Bittau通过每次猜测一个字节,缩短暴力破解ASLR的周期。在32位机器上,目前地址随机化的位数较少,地址可以很快通过很多方法定位,不能完全阻止基于代码重用的漏洞利用。但会增加攻击者的难度;在64位机器上的地址随机化将使Gadget的定位变得十分困难,基于代码重用的漏洞利用会受到很大影响。因此向64位机器的迁移将大幅提高漏洞利用的难度,同时32位机器上的精细的随机化,将提高漏洞利用的难度,但需要同其他方法相结合才能保证程序的相对的安全性。

在当前的粗粒度的随机化的实施方案中,对代码段整体的起始地址进行了随机化。而这样的随机化有两个问题,首先,随机化的地址空间范围有限,在32位的机器中,有效的随机化空间只有216,因而其随机地址范围很小;其次,由于是整块的随机化,因而只要获取代码中的一个有效地址,就能计算推测出所有的指令序列的地址。另一方面,基于代码重用的漏洞利用更关注Gadget的地址,即Ret或Jmp序列之前一定长度之前的指令序列,而其它部分的指令序列,由于指令副作用等原因并不关注。因而,对构建有效的Gadget段序列的随机化更为重要,对于段序列的随机化更能提高程序的安全性,抑制基于代码重用的漏洞利用。

3.3 基于编译阶段的防护

基于编译阶段的防护通过修改编译器,在编译阶段消除能够被攻击者恶意使用的指令序列和函数,如减少ret,jmp等指令,在库中消除类似system、execv等关键函数。目前其关注于修改编译器生成指令的偏好,尽量消除非必要的控制流指令,增加对必须的控制流指令的监护。Jinku Li提出分析ret指令来源,针对每一种情形分别处理,消除被曲解的ret指令,对于正常的ret指令,其使用间接返回(Return Indirection)的策略,即将可能被篡改的返回地址抽取出来,放在一个单独的表中,而在栈中原来的位置存放该地址在表中的索引,每次返回时,通过该索引去获取实际的返回地址,再将有效地保护返回地址,防止程序流程被篡改。

但是,该方法需要重写编译器,需要重新编译原有代码,对于庞大的遗留代码很难有效,同时会带来巨大的负载开销。

3.4 基于动态插桩的防护

基于动态插桩工具(Dynamic BinaryIndtrumentation,DBI)的方法,通过动态插桩工具提供的程序执行时状态信息,基于函数回调机制,引入动态的监控和判定条件,进而判定程序的合法状态。防止对目标代码段的断章取义,进而阻止非法重用二进制代码。Davi基于DBI实现了代码重用防护,通过DBI提供的动态信息,使Shadow Stack保护函数的返回值不会被篡改,因而能有效防止传统基于代码重用的漏洞利用。黄志军等引入黄志军等引入DBI技术。检查函数返回地址,防止恶意使用库代码。但DBI工具性能不足,以及DBI工具的平台局限性,难以推广。

在DBI方法的基础上,出现了统计学的分析法,通过剖析被重用的Gadget。统计不同的指令的频率。在Chen中,通过ret指令序列的长度及其连续出现的频率来识别ROP攻击。能够一定程度上识别传统ROP攻击。但是对于不依赖Ret及类Ret指令的攻击不适用。而且对于部分特别的攻击序列,这样的统计规律不一定成立。与此同时,在缺乏大量攻击样本的前提下,很难得出—个准确的统计学规律。

4 发展与展望

随着防御技术的提高,基于代码重用的漏洞利用难度也在增加,但是其仍有很大的利用空间。各个操作系统中仍有很多库和代码没有应用ASLR机制,我们可以在这些库和代码中寻找Gadget,我们构造利用代码时不必局限于某一个库,提高攻击的灵活性。如果把自动构造应用其中,自动探测寻找未应用ASLR的代码和库,将提高漏洞利用的成功率和实用性。

而在防护方面,基于上面四种防护技术,进行软硬件结合的控制流完整性和正确性保护,以及细粒度的ASLR技术研究是很有必要的,但是要注意均衡防护技术与被保护程序运行效率的关系,减少对效率的损耗。

5 结束语

本文描述了现有流行的基于代码重用的漏洞利用方法以及防御方法,总结了各自优缺点。随着各种代码重用防御技术的发展,基于代码重用的漏洞利用的难度也越来越大。但是在图灵模型下不能从根本消灭基于代码重用的漏洞利用。最新出现的SROP,还没有出现有效的防御方法,下一步我们将设计并实现一种SROP防御方法。