文章信息
- 傅建明 , 汤毅 , 刘秀文 , 张旭 . 2016
- FU Jianming, TANG Yi, LIU Xiuwen, ZHANG Xu . 2016
- 一种基于动态污点的内存越界访问检测框架
- A Memory Out-of-Bound Detection Framework Based on Dynamic Taint
- 武汉大学学报(理学版), 2016, 62(5): 401-410
- Journal of Wuhan University(Natural Science Edition), 2016, 62(5): 401-410
- http://dx.doi.org/10.14188/j.1671-8836.2016.05.001
-
文章历史
- 收稿日期:2015-02-20
2. 武汉大学 空天信息安全与可信计算教育部重点实验室,湖北 武汉 430072 ;
3. 武汉大学 计算机学院,湖北 武汉 430072
2. State Key Laboratory of Aerospace Information Security and Trusted Computing,Ministry of Education,Wuhan University, Wuhan 430072, Hubei, China ;
3. School of Computer, Wuhan University, Wuhan University, Wuhan 430072, Hubei,China
当访问的内存对象不同于期望的数据类型时,极有可能发生内存损坏,导致对象指针越界、对象未初始化、对象指向非指针数据或不存在的对象等内存错误.因内存损坏而引发的漏洞简称内存错误漏洞.自20世纪70年代以来,内存错误一直是软件的重要漏洞之一,如栈溢出和堆溢出、格式化字符串漏洞、NULL指针漏洞、整数溢出和释放后引用漏洞等在CERT和CVE漏洞列表中占据前列.内存错误漏洞一般会导致拒绝服务攻击、信息泄漏、信息窜改、控制劫持等安全威胁,攻击者劫持控制流,使得软件转向攻击者设定的代码(shellcode).2012年Pwn2own大会上,来自法国的VUPEN漏洞研究团队使用CVE-2012-1876漏洞,成功展示了针对Windows 7操作系统的攻击[1].2014年“Operation SnowMan”APT[2]使用了CVE-2014-0322漏洞,2014年“心脏滴血”漏洞[3]则使得互联网上使用SSL协议的大量服务器处于危险之中.虽然操作系统提供了地址空间随机化(address space layout randomization,ASLR)、数据执行保护(data execution prevention,DEP)[4]和结构化异常处理覆盖保护(structured exception handler overwrite protection,SEHOP)等安全机制,可以缓解部分针对软件漏洞的攻击,微软的EMET (enhanced mitigation experience Toolkit)[5]提供Heapspray检测、NULL Page检测、导出表访问过滤,同时强制ASLR、内存保护检查和敏感函数检测等,以降低漏洞攻击的成功率.但是,攻击者可以通过精心构造的攻击代码,绕过这些安全机制,比如alliedve.htm(http://hi.baidu.com/yuange1975?page=1).大多数攻击都是源于内存越界访问引发的,因此需要高效的内存越界检测方法,为阻止和分析内存越界漏洞提供技术支持.动态污点分析( dynamic taint analysis,DTA) 方法是近几年来提出的一项热门技术,已经广泛应用于信息安全各个领域,包括网络攻击检测与防护,恶意代码分析,信息安全验证,软件安全漏洞挖掘,协议格式逆向分析等.本文利用动态污点分析技术检测内存越界访问的内存错误.
1 相关工作当攻击者修改内存对象的长度标识对内存对象非法访问或者内存操作函数越界复制数据时,程序就会发生内存越界的访问行为.此时,指针就会访问过时的、没有初始化的数据,或者越界访问特定范围以外的数据.过时的指针一般称为悬浮指针(dangling pointer,DP),越界访问一般称为索引溢出(index/type overflow,IOF).本文的研究工作包括IOF和DP静态分析和约束检查,以及IOF和DP的动态检测.
静态分析是指静态分析源代码,获得指针的约束关系,并在指针使用前插入约束检查函数.SoftBound[6]通过对C/C++语言中的数据指针进行边界检查来保证内存访问的安全性,可以保护内存、寄存器、函数返回值中的指针.对每一指针,生成对应的基址base标识和指针范围bound标识;SoftBound在使用指针前插入check函数,保证指针无IOF.WIT(写完整性检查)[7]利用Phoenix编译器,在中间语言MIR上对代码进行指针分析和写安全性分析.指针分析就是寻找每个指针指向对象的集合(包括局部变量、全局变量及动态分配的内存对象),即Points-To Set(PTS).写安全性分析确保每个指针及其对应的PTS安全级别相同,确保指针无IOF.Baggy Bounds Checking[8]利用内存对象边界表记录了每一个对象的边界信息,对象访问时插入检查代码.SAFEDISPATCH[9]从源代码中解析出对象访问的方法,在访问前验证内存对象访问方法的一致性.基于规则的代码补丁则从源代码角度修补因内存函数越界复制而引发的IOF问题.这些研究基本上是静态分析源代码获取指针或对象的有效区间,静态分析的不完备性会引发漏报,同时也无法覆盖进程中实时产生的动态代码.
动态检测依赖于污点分析技术,对指向内存对象的指针实施污点标记,通过污点传播构建指针的传播链,当指针越界或者对象失效时检测到内存越界访问.TaintCheck[10]采用Socket数据作为污点源,检查污点源与JMP/Format String/System call argument等是否关联,从而检测注入代码攻击.Dytan[11]建立一种通用的污点分析框架,该框架支持基于数据流和控制流的多污点标记和传播.Panorama [12]从硬件级别(QEMU 环境)和操作系统级别,跟踪污点信息的访问,根据安全策略,检测信息泄露和恶意代码.文献[13]提出一种多项式时间的路径敏感的污点分析方法,利用污点分析确定精确的程序执行路径,减少分析时间,并验证这些路径是否符合安全策略.TaintDroid[14]跟踪敏感数据的传播,保护手机用户的隐私.Undangle[15]在TEMU平台结合污点分析实现了DP的检测,并引入指令窗口检测可疑的DP.
另外,在数据结构理解上,Reward[16]利用内存访问行为和数据结构的关联,推断数据结构的类型;TIE[17]在执行轨迹上进行类型约束求解,与Reward一样都是从数据结构类型理解二进制程序的语义.
Valgrind(http://valgrind.org/)的memcheck利用虚拟CPU技术,借助全进程空间的Value表和Address表记录每个地址是否已经初始化以及地址是否有效.每当CPU执行涉及内存相关指令时,通过Address表检查该内存地址是否已经被申请,通过判断内存地址对应Value表的值检测该地址是否已经被初始化.通过检查Address表和Value表,memcheck可以检测包括内存越界、内存泄漏、内存释放后引用和内存未初始化等各种内存错误.但由于缺少错误的详细信息,往往无法对产生错误的根源进行分析.同时,由于memcheck依赖于Value表与Address表,一般被开发者用于提高代码质量.使用memcheck检测exploit时,由于exploit往往会将已释放的内存空间重新申请,因而使得Value表与Address表都生效,这也导致memcheck无法检测出exploit.
本文借助污点分析技术,在Pin平台上跟踪堆块指针的生命轨迹,包括堆块指针的产生、传播、使用和消亡等过程.通过对堆块指针的合法性进行检查,从而检测出内存释放后引用、内存越界访问等两类常见错误.与memcheck相比,本文提出的检测方法能用于检测精心构造的exploit,并为检测者提供详细信息;与Undangle相比,本文提出的框架不仅能检测出UAF攻击,同时还能检测IOF这一类内存错误.
2 问题描述C和C++是非类型安全语言,由程序员负责内存的管理,包括指针和对象的管理.对象是一个基本的数据结构或者实例,占据一定长度的内存空间.当一个内存对象创建后,一个初始指针会指向该内存对象,实现指针与对象的关联.当指针在程序间传播时,会得到一组指针与对象的关联.当程序员使用一个对象指针销毁该对象时,该对象关联的所有指针不一定被程序员释放.因此,使用指针时,由于缺少对象属性,导致指针的非法使用.
对象具有下列三类基本属性:
1) 空间范畴:起始地址、对象大小、有效地址区域;
2) 时间范畴:创建时间、作用域区域、销毁时间;
3) 语义范围:对象各成员的使用语义.
缺乏对象大小信息,可能导致指针移位时越界,这是缓冲区溢出的根源;缺乏对象时间性信息,可能导致使用指针访问未初始化对象、越出作用域访问对象或访问已释放对象,而访问已释放对象造成了释放后再引用漏洞.
栈和堆是程序运行的重要基础.栈保存函数调用的上下文,包括调用参数、返回地址、栈帧基址以及局部变量.栈是函数之间调用的桥梁.攻击者喜欢覆盖栈中的返回地址和局部变量中的函数指针,实现控制流的劫持.内存操作函数的内存操作很容易覆盖栈中的数据,从而引发IOF攻击.堆是程序运行时动态分配的内存,程序员编写代码负责堆内存的申请和释放.由于程序员编程能力水平的差异以及程序逻辑的复杂性,使得堆成为DP的重灾区.图1展示了因对象和对象访问在时间和空间上的不一致而引发DP和IOF的机理.
![]() |
图 1 悬浮指针和索引溢出的机理 Figure 1 The principle of DP and IOF |
在操作系统中,堆管理系统存在三类操作:堆块分配、堆块释放以及堆块合并.空闲堆块由链表组织在一起,实现有效查找.如果攻击者能够伪造链表节点的指针,那么在释放和合并过程中就有机会读写内存.而发生堆溢出时,溢出的数据可能会覆盖下一个堆块的块首,改写块首中的前向指针和后向指针,刚好满足以上条件.因此在分配、释放或合并等操作发生时将获得一次往内存任意地址写入任意数据的机会[18].为此,操作系统增加了堆块首部的完整性保护,包括添加cookie、元数据加密、链表指针校验[18],从而有效抑制这种直接修改堆块首部的攻击.但UAF(释放后引用)可以绕过这种保护,该攻击独立于堆块的完整性保护,是目前主流的攻击方式,例如CVE-2012-1876PoC[19]、CVE-2013-3918PoC[20]、CVE-2014-0322PoC[21].
攻击者利用漏洞进行越界访问(包括读操作和写操作)且不发生链表操作时(比如不进行堆块的释放、合并等),基于堆块首部完整性保护的防护机制将无法发挥作用.随着DEP、ASLR等安全防护机制的部署,漏洞攻击的难度越来越大,同时也促进漏洞利用技术的发展.在漏洞攻击中,攻击者为了提高攻击成功率,往往通过泄露内存信息来绕过ASLR机制的保护.而位于堆块中的越界访问(读内存操作)能达到泄露内存信息的目的.更进一步,如果能够进行堆越界写内存操作,那么就能通过修改类实例虚表指针的方式控制程序执行.当前流行的漏洞利用方式中,很常见的一种方式就是通过漏洞修改内存中数据结构的长度标示或数据类型,进而获取越界访问的权限来开展攻击.例如,CVE-2012-1876PoC[19]、CVE-2013-3918PoC[20]、CVE-2014-0322PoC[21]、CVE-2014-0160PoC[22]和CVE-2014-6332PoC[23]等.
3 检测方法本节首先阐述DP及IOF的检测原理,接着给出检测框架原型系统的具体实现.
3.1 检测原理内存越界访问检测包括初始指针捕获、指针传播、失效指针捕获、指针使用的异常检测,以及对象访问的上下文捕获.图2描述了内存越界访问的基本检测框架.
![]() |
图 2 内存越界访问的检测框架 Figure 2 Detection Framework of memory out-of-bounds |
通常,内存越界访问难以检测的原因在于内存访问错误触发的条件比较苛刻以及访问错误的根源点和异常点的距离比较远.另外,从异常点难以追踪根源点.为此,从对象创建开始获取初始指针,然后根据相应指令捕获指针的传播,根据指令的解引用和对象的销毁检测内存访问越界.因此,检测的基本原理以内存对象为中心,从指针传播中构建指针与对象的关联,从指针使用中检测DP和IOF.
检测的总体思路是:当加载一个可执行模块时,若此模块中包含有内存分配和内存释放函数,则在函数开始处和函数结束处插入分析代码,用以在对象创建和对象销毁时提取信息.对模块中执行的每一条二进制汇编指令进行实时分析,若属于对象指针传播指令或访问内存指令,则在指令开始前插入分析代码.
在程序运行过程中,插入的分析代码随着程序代码一同被执行.当拦截到内存对象创建时,会填充一个自定义的数据结构,用以保存对象的状态信息,包括对象ID、所在堆的句柄、对象地址、申请内存大小和存活状态等.并建立寄存器和内存中对象指针与内存对象的关联信息.当拦截到对象销毁时,通过使用的对象指针设置内存对象存活状态.当拦截到对象指针的传播时,首先判断源对象指针是否还指向关联对象,然后依据源对象指针设置目的对象指针的信息.当拦截到访问内存对象操作时,若对象已销毁或访问越界,则表示检测到UAF或者IOF,发出警报.
为了检测内存越界访问,需要使用污点分析技术追踪指针的产生、存储、运算、传播以及消亡的整个过程,并在指针使用时检测可能出现越界访问的指针.根据污点指针存储位置的不同将污点分为三种类型:存储于寄存器的污点指针、存储于栈的污点指针和存储于堆的污点指针.存储于寄存器中的污点指针往往生命周期较短,一旦存储污点指针的寄存器作为指令操作的目的寄存器,则代表该污点指针生命周期的终结.而栈中的污点指针则在栈顶改变时需要判断该污点指针的位置是否依然有效.此外,基于栈的污点指针和基于寄存器的污点指针与线程密切相关.基于堆的污点指针的生命周期比较长,且指令传播比较多,这类污点指针也容易被攻击者控制和利用.
表1描述了部分与指针操作相关的二进制汇编指令,这些指令可以分为源寄存器到目的寄存器、源寄存器到目的内存、源内存到目的寄存器以及源内存到目的内存等四种类型.当内存对象创建时,初始指针指向该对象.将初始指针标记为污点源,发生污点传播时,将目的寄存器或者目的内存地址加入污点集合.当污点集合中的元素被当作目的操作数时,依据源操作数的性质,确定是否修改或者清除污点集合中的元素.例如,干净值传给寄存器或者污点内存时,需要清除该寄存器或内存地址的污点标记.
指令 | 含义 |
mov,movs,push,pop | dst←src; |
xchg | t←src;src←dst;dst←t; |
add | dst←src1+src2; |
inc | srcdst←srcdst+1; |
sub | dst←src1-src2; |
dec | srcdst←srcdst-1; |
lea | dst←(disp+index)+base; |
为了检测DP,检测程序使用一个数据结构来描述内存对象,包括内存对象独一无二的ID、对象所在堆的堆句柄、首地址、大小、存活状态、申请内存的函数调用链、释放内存的函数调用链和对象被引用次数.同时创建四个集合分别用来保存所有对象、指向对象的初始指针、内存中的对象指针以及寄存器中的对象指针.当使用内存申请函数申请堆块内存时,程序将自动填充一个数据结构,将数据结构中对象存活状态设置为真.将该内存对象的指针作为污点源进行追踪.每当发生污点指针传播时,将生成相应的污点指针或污点寄存器的结构保存到相应集合中,同时将数据结构中的被引用次数加1.如果污点指针或污点寄存器被清除,那么将数据结构中的被引用次数减1.当此内存对象被释放后,将数据结构中存活状态修改为死亡状态.同时检查对象被引用次数是否为0,如果为0,说明不存在指向该对象的悬浮指针,直接将数据结构删除.如果不为0,则说明仍然存在指向该对象的悬浮指针.如果此时悬浮指针被使用,通过与之关联的ID便可以检测出UAF.
为了检测IOF,一个内存对象可以标记为(baseAddr,size),将指向该对象的指针标记为p,将指针操作记为Operand(p),其中Operand∈{mov,xchg,sub,inc,add,dec,lea}.如果p ∈[baseAddr,baseAddr + size)且Operand(p) ∉[baseAddr ,baseAddr + size),则Operand(p)的目的地址将成为污点指针,在此污点指针上进行读写操作,便可以确认发生越界读写错误.
在IA-32指令集架构下,内存地址的表示由基址寄存器baseRegister、变址寄存器indexRegister、偏移量displacement以及比例值Scale组成.其中有效内存地址可以表示如下:
Effective Address =baseRegister + Scale * indexRegister + displacement
二进制汇编指令中,Opcode指示baseRegister、indexRegister以及displacement是否存在,因此形成了IA-32体系下的各种寻址模式.算法1(Algorithm 1)给出指令“lea”的检测算法CheckInstruction_Lea.其余指令的检测算法类似,由于篇幅关系,不再一一列出.
3.2 系统实现动态二进制插桩平台包括Pin[24]、DynamoRIO (http://www.dynamorio.org)、Valgrind等.本文采用的动态二进制插桩平台Pin为检测框架原型系统实现环境.Pin是一个强大的动态二进制指令框架,由Intel公司开发和维护,目前支持Linux平台下的IA-32、Intel64、Itanium(R)处理器架构以及Windows平台下的IA-32、Intel64架构.Pin分为Pin本身框架和插件两个部分.其中框架部分由Intel公司开发和维护,通常称为Pin.插件称为Pintools,由使用者开发.Pin提供IMG、SEC、RTN、TRACE、BBL和INS等系列编程接口,方便分析人员从可执行模块、节、函数、基本代码块以及指令等层次对二进制代码进行动态分析[25].
在Windows环境下,系统提供了LocalAlloc()、GlobalAlloc()、HeapAlloc()、malloc()、new等动态内存的分配函数.根据微软的堆管理架构,所有的堆分配函数最终都将调用位于ntdll.dll中的RtlAllocateHeap()函数进行分配,同时这个函数也是一般应用程序能访问的最底层的堆分配函数.通过拦截RtlAllocateHeap()函数,可以获得对象初始化的指针以及内存对象的大小.
Algorithm 1 CheckInstruction_Lea |
Input:Inst /*the instruction eippoint to */ |
HeapChain /*the live heap block chain */ |
Output:bRtn /* if Instruction maybe access heap out of bounds,bRtn is TRUE,else FALSE */ |
1:bRtn← FALSE |
2:baseReg←INS_MemoryBaseReg(Inst) |
3:indexReg←INS_MemoryIndexReg(Inst) |
4:scale←INS_MemoryScale(Inst) |
5:disp←INS_MemoryDisplacement(Inst) |
6:if REG_valid(baseReg) then |
7:baseAddr←GetRegValue(baseReg) |
8:HeapInfo←FindNodeFromHeapChain(HeapChain,baseAddr) /* HeapInfo contain baseAddr,size */ |
9:if HeapInfo !=NULL then |
10:index←0 |
11:if REG_valid(indexReg) then |
12:index←GetRegValue(indexReg) |
13:endif |
14:srcAddr←baseAddr + index * scale + disp |
15:if srcAddr>HeapInfo->baseAddr + HeapInfo->size then/*!!! access heap out of bounds*/ |
16:bRtn← TRUE |
17:endif |
18:endif |
19:endif |
20:return bRtn |
为了释放各种类型的堆块,系统提供了对应的内存释放函数和操作,这些函数和操作包括LocalFree()、GlobalFree()、HeapFree()、free()、delete等.所有的这些堆释放函数和操作将调用位于ntdll.dll中的RtlFreeHeap()函数进行释放操作.这个函数也是普通应用程序能够直接访问的最底层的堆释放函数.拦截RtlFreeHeap()函数,可以获得对象指针的失效以及对象的消亡信息.
通过对RtlAllocateHeap函数的参数及返回值进行分析,检测程序可以自动获取新申请堆块的大小、指针及堆块属性.与RtlAllocateHeap函数类似,通过对RtlFreeHeap函数的参数及返回值进行分析,检测程序可以自动获取准备释放的堆块指针.这些操作通过Pin提供的函数接口来完成.
Pin平台提供INS系列编程接口,通过这些编程接口,检测程序可以在每一条用户态指令执行前和执行后获取该指令的上下文信息,包括寄存器的值、进程空间中的内存数据、本条指令的指令类型、源操作数、目的操作数等.通过Pin平台提供的这些功能,检测程序对每条即将执行的指令进行分析,完成污点传播分析、DP检测、IOF检测等功能.
检测信息输出包括对象创建信息、污点指针传播信息、对象销毁信息、悬浮指针信息、越界读写相关信息和释放后再引用信息等部分.表2 展示了检测信息输出各部分的详细细节.其中对象创建、污点指针、越界读写用于IOF漏洞检测;除污点指针及越界读写外其余部分用来展示DF检测的信息输出.
输出要点 | 说明 |
对象创建 | 获取对象创建时的上下文时信息,包括函数调用栈、调用对象创建的指令、指令所在的函数和模块,以及对象的创建时间、对象内存区域、对象内存所在的堆、对象大小、对象的有效地址区域 |
对象指针 | 获取对象销毁之前所有指向对象的指针的信息,包括对象创建之时产生的初始指针,以及由初始指针传播得来的各个对象指针 |
污点指针 | 获取出现IOF的指针信息,包括对象创建时产生的初始指针、对象的大小和指令所在的函数和模块 |
越界读写 | 获取越界读写时的上下文信息,包括函数的调用栈、出现越界读写的指令、指令所在的函数与模块、越界读写时使用的内存对象信息,以及污点指针的传播路径等 |
对象销毁 | 获取对象销毁时的上下文时信息,包括函数调用栈、调用对象销毁的指令、指令所在的函数和模块、对象销毁时使用的对象指针的信息,以及此对象指针的生成时间、生成指令、传播路径等 |
对象悬浮指针 | 获取对象销毁之后所有指向对象的指针的信息,包括所有悬浮指针、各个悬浮指针的传播路径、销毁悬浮指针的指令和使用悬浮指针的指令等 |
使用悬浮指针访问销毁对象 | 输出所有使用悬浮指针访问已销毁对象的详细信息,包括使用悬浮指针的指令、函数调用栈、指令所在的函数和模块、访问对象的地址和内容和判断悬浮指针使用方式的危险级别 |
对象被重写内容的使用 | 输出使用悬浮指针获取到的对象重写内容的使用信息,包括重写值的类型、使用重写值的指令和由重写值获取的其他内容及其使用情况 |
为了验证检测框架和检测方法的可行性,编写了基于Pin平台的检测工具DemisPtr(Detector of Misuse Pointer),并对部分DP及IOF类型漏洞PoC进行了检测.
4.1 实验结果表3展示了6个软件漏洞PoC指针误用的检测结果.从表3可以看出,检测工具能准确检测出UAF漏洞,但是在检测IOF类型漏洞时,对CVE-2014-0322漏洞的检测出现漏报的情况.通过分析,发现漏报的原因在于存在IOF的代码运行在Adobe Flash插件的私有堆中.而私有堆的内存分配与释放没有采用标准的RtlAllocateHeap及RtlFreeHeap函数进行处理,导致检测程序在初始指针获取环节无法获取其内存对象的初始指针,最终出现了漏报的情况.通过人工分析Abobe Flash Player插件的私有内存管理类MMgc的系列成员函数,在其模块加载时将其私有内存分配与释放函数纳入初始指针信息截获模块来消除这种漏报情形.CVE-2014-0322中同时存在误报的情形,通过分析,发现误报的原因在于Adobe Flash Player插件使用了大量的浮点指令.原型系统未能对复杂的浮点指令制定准确的污点传播规则,导致了检测结果中出现了误报的情形.在后续的研究中,将通过引入中间语言的方式来精简污点传播规则,降低误报的概率.
CVE编号 | UAF | IOF | 漏报 | 误报 | 检测环境 | ||
存在UAF | 检测结果 | 存在IOF | 检测结果 | ||||
CVE-2012-1876 | - | - | √ | √ | × | × | Windows7;IE9 |
CVE-2012-4969[26] | √ | √ | - | - | × | × | Windows7;IE9 |
CVE-2013-3918 | - | - | √ | √ | × | × | Windows7;IE9 |
CVE-2014-0160 | - | - | √ | √ | × | × | Windows7;OpenSSL 1.0.1 |
CVE-2014-0322 | √ | √ | √ | × | √ | √ | Windows7;IE10;Adobe Flash Player |
CVE-2014-6332 | - | - | √ | √ | × | × | Windows8;IE11 |
在检测过程中,检测工具保存了内存对象初始化、污点传播以及漏洞触发的相关信息,通过这些信息,可以对漏洞形成原理进行分析.本节将以CVE-2012-1876PoC以及CVE-2012-4969PoC为例展示漏洞形成的原因.
CVE-2012-1876漏洞存在于Microsoft Internet Explorer 6至9版本中,该漏洞源于未正确处理内存中的对象,造成堆溢出.远程攻击者可利用该漏洞通过试图访问不存在的对象执行任意代码.该漏洞又名“col元素远程代码执行漏洞”[27].
图3展示了CVE-2012-1876PoC的部分检测结果.在内存对象初始化时,程序申请了一个大小为0x100的内存空间,系统为这次内存申请分配了0x565c288的起点的空间.通过栈回溯技术,发现这个对象为CTableLayout对象的成员.在CalculateMinMax函数中,由于程序未对内存空间大小进行验证,导致出现越界的污点指针并发生污点指针传播.在AdjustForCol函数中,程序使用污点指针进行了内存越界写操作.除图3展示的检测结果外,还检测到另外的一些IOF,其中程序在某次memcpy函数调用时也出现内存越界读的操作:memcpy函数源操作地址所在的堆块长度只有0x100,而程序调用memcpy函数需要拷贝0x12c长度的数据.通过对记录的调用函数链进行分析,发现这个堆块为一个BSTR对象.由于图3展示的内存越界写错误改写了BSTR对象的长度域,导致在memcpy函数处发生了IOF.
![]() |
图 3 CVE-2012-1876 PoC部分检测结果 Figure 3 Detection results of CVE-2012-1876 PoC |
CVE-2012-4969存在于Microsoft Internet Explorer 6至9版本中.由于mshtml.dll中的CMshtmlEd::Exec函数中存在释放后使用漏洞,远程攻击者可利用该漏洞通过特制的网站,执行任意代码[28].
图4展示了CVE-2012-4969 PoC的部分检测结果.内存对象初始化时,程序申请了一个大小为0x88的内存空间,系统为其分配以0x238960的起始地址的空间.同时检测程序为其分配一个ID为0x48d.在指针传播时,以0x238960为污点,检测到了污点指针的多次传播.此后,程序调用了内存释放函数,将0x238960所在的堆块内存释放.此时ID为0x48d的内存状态为死亡状态,由于其被引用数不为0,说明其存在悬浮指针.当程序再次申请内存时,系统分配了以0x238960为起点的内存,检测程序为其分配ID为0x650.当程序使用悬浮指针访问内存时,由于指针ID为死亡状态,所以可以检测出此UAF.
![]() |
图 4 CVE-2012-4969 PoC部分检测结果 Figure 4 Detection results of CVE-2012-4969 PoC |
使用Pin+DemisPtr进行动态插桩分析时,由于大量分析代码的存在,给机器性能带来巨大压力,造成了程序运行效率下降,程序运行时间将大幅度增加.表4描述了检测程序在Pin平台以及Pin+DemisPtr情形下的运行效率对比.对于使用内存较小的程序,Pin+DemisPtr检测的时间消耗是程序在Pin平台上运行时间的40倍左右;对于频繁操作内存的程序,检测花费的时间大幅度增长,需要对检测模块进行性能优化处理.
测试程序 | Pin下运行时间/s | Pin+DemisPtr运行时间/s |
OpenSSL | 6.42 | 109.75 |
Iexplore | 15.89 | 638.43 |
Iexplore+ Flash.ocx | 17.39 | 823.67 |
Iexplore.exe+ HeapSpraying | 23.20 | 1 235.77 |
理论上,检测程序需要对执行的每条二进制指令进行分析.实际检测中,大量的系统提供的通用函数可以通过函数参数感知函数行为,无需对函数的每条执行指令进行分析.在检测过程中,查询堆块信息的时间取决于链表中节点的个数.如果能够排除链表中无关节点,就能提高性能.将堆块信息节点加入链表时,利用栈回溯技术分析调用内存申请函数的上层函数,可以排除某些无关节点.
5 结论内存越界访问,特别是指针的越界访问,严重威胁到软件和系统的安全性.本文根据对象和对象访问在时间和空间上的不一致性,利用动态污点分析,设计和实现了指针误用的内存越界访问的检测框架.该框架既可以检测传统的悬浮指针,也可以检测索引溢出的指针访问.实验结果证实了该检测框架和检测方法的有效性.该框架原型系统在Pin平台上实现,一方面检测时间稍长;另一方面,由于代码编译时采用了编译优化,会丢失污点导致漏报,也会因过度污点引发误报.因此,下一步的研究重点在于污点传播的精确捕获、检测算法的优化以及检测工具的运行优化,旨在提高检测的效率和精准度.
[1] | ALEXANDRE P. Advanced Exploitation of Internet Explorer Heap Overflow (Pwn2Own 2012Exploit)[EB/OL]. [2014-12-12].http://www.vupen.com/blog/20120710.Advanced_Exploitation_of_Internet_Explorer_HeapOv_CVE-2012-1876.php. |
[2] | 中文文献 DARIEN K,DAN C,MIKE S, et al. Operation SnowMan: DeputyDog Actor Compromises US Veterans of Foreign Wars Website[EB/OL]. [2014-12-12]. https://www.fireeye.com/blog/threat-research/2014/02/operation-snowman-deputydog-actor-compromises-us-veterans-of-foreign-wars-website.html. |
[3] | MO63160363. Heart Bleed[EB/OL]. [2014-12-12]. http://baike.baidu.com/view/12769298.htm. |
[4] | MICROSOFT Tech Net. Data Execution Prevention[EB/OL].[2014-12-12]. http://technet.microsoft.com/en-us/library/cc738483(WS.10).aspx. |
[5] | MICROSOFT TechNet. Enhanced Mitigation ExperienceToolkit[EB/OL].[2013-09-20]. http://technet.microsoft.com/en-us/security/jj653751. |
[6] | NAGARAKATTE S, ZHAO J, MARTIN M M K, et al. SoftBound: Highly compatible and complete spatial memory safety for C[J]. ACM Sigplan Notices , 2009, 44 (6) : 245–258 DOI:10.1145/1543135 |
[7] | AKRITIDIS P, CADAR C, RAICIU C, et al. Preventing memory error exploits with WIT[C]//Security and Privacy. Washington D C: IEEE,2008:263-277. |
[8] | AKRITIDIS P,COSTA M,CASTRO M, et al. Baggy bounds checking: An efficient and backwards-compatible defense against out-of-bounds errors[C]//Proceedings of USENIX Security Symposium. Berkeley: USENIX, 2009:51-66. |
[9] | DONGSEOK J, ZACHARY T, SORIN L.SAFEDISPATCH: Securing C++ Virtual Calls from Memory Corruption Attacks[EB/OL]. [2014-12-12]. http://cseweb.ucsd.edu/~d1jang/paper/ndss14.pdf. |
[10] | NEWSOME J, SONG D. Dynamic taint analysis for automatic detection, analysis, and signature generation of exploits on commodity software[DB/OL].[2015-01-30]. http://repository.cmu.edu/ece/3/?utm_source=repository.cmu.edu%2Fece%2F3 & utm_medium=PDF & utm_campaign=PDFCoverPages. |
[11] | CLAUSE J, LI W, ORSO A. Dytan: A generic dynamic taint analysis framework[C]//Proceedings of the ACM/SIGSOFT International Symposium on Software Testing and Analysis. New York: ACM,2007:196-206. |
[12] | YIN H, SONG D, EGELE M, et al. Panorama: Capturing system-wide information flow for malware detection and analysis[C]//Proceedings of the 2007 ACM Conference on Computer and Communications Security( CCS 2007). New York: ACM,2007:116-127. |
[13] | 李佳静, 王铁磊, 韦韬, 等. 一种多项式时间的路径敏感的污点分析方法[J]. 计算机学报 , 2009 (9) : 1845–1845 LI J J, WANG T L, WEI T, et al. A polynomial time path-sensitive taint analysis method[J]. Chinese Journal of Computers , 2009 (9) : 1845–1845 |
[14] | ENCK W, GIBERT P, BYUNG-GON C, et al. TaintDroid: An information-flow tracking system for realtime privacy monitoring on smartphones[J]. Communications of the ACM , 2010, 57 (3) : 393–407 |
[15] | CABALLERO J, GRIECO G, MARRON M, et al. Undangle: early detection of dangling pointers in use-after-free and double-free vulnerabilities[C]// Proceedings of the 2012 International Symposium on Software Testing and Analysis. New York: ACM, 2012:133-143. |
[16] | LIN Z, ZHANG X, XU D. Automatic Reverse Engineering of Data Structures from Binary Execution[DB/OL].[2015-01-20]. https://www.utdallas.edu/~zxl111930/file/Rewards_NDSS10.pdf |
[17] | LEE J H, AVGERINOS T, BRUMLEY D. TIE: Principled Reverse Engineering of Types in Binary Programs[DB/OL].[2015-02-01]. http://repository.cmu.edu/ece/235/. |
[18] | 王清. 软件漏洞分析技术.[M] 北京: 电子工业出版社, 2011 . WANG Q. Software Vulnerability Analysis Technology.[M] Beijing: Publishing House of Electronics Industry, 2011 . |
[19] | K33NTEAM. CVE-2012-1876 PoC[CP/OL]. [2014-12-12]. https://github.com/k33nteam. |
[20] | METASPLOIT. Internet Explorer - CardSpaceClaimCollection ActiveX Integer Underflow (MS13-090)[CP/OL].( [2014-12-12]. http://www.exploit-db.com/exploits/29857/. |
[21] | ROWP. cve-2014-0322 ie10poc[CP/OL]. [2014-12-12]. http://www.binvul.com/viewthread.php?tid=387 & extra= & page=1. |
[22] | FITZL C. OpenSSL 1.0.1f TLS Heartbeat Extension-Memory Disclosure (Multiple SSL/TLS versions)[CP/OL]. [2014-12-12]. http://www.exploit-db.com/exploits/32764/. |
[23] | QIU J Z. Win95+IE3 - Win10+IE11 All DVE Exploit [EB/OL]. [2014-12-12]. http://www.freebuf.com/articles/system/51501.html. |
[24] | INTEL. Pin[EB/OL].[2014-12-12]. https://software.intel.com/en-us/articles/ pin-a-dynamic-binary-instrumentation-tool. |
[25] | INTEL. Pin User Manual[EB/OL].[2014-12-12]. https://software.intel.com/sites/landingpage/pintool/docs/61206/Pin/html. |
[26] | METASPLOIT. Microsoft Internet Explorer - execCommand Use-After-Free Vulnerability (MS12-063)[CP/OL].(2012-10-10)[2014-12-12]. http://www.exploit-db.com/exploits/21840/. |
[27] | MITRE. CVE-2012-1876[EB/OL].(2012)[2014-12-12]. http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-1876. |
[28] | MITRE.CVE-2012-4969[EB/OL]. [2014-12-12]. http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-14969. |