二进制程序中的use-after-free漏洞检测技术
韩心慧 1 , 魏爽 1 , 叶佳奕 1 , 张超 2 , 叶志远 1     
1. 北京大学 计算机科学技术研究所, 北京 100080;
2. 清华大学 网络科学与网络空间研究院, 北京 100084
摘要:Use-after-free漏洞(简称UaF漏洞)是当前最流行的高危内存破坏漏洞。目前针对UaF漏洞的检测工作并不完善,原因是UaF漏洞产生的特征是分配内存、释放内存、使用已释放的内存并按顺序出现,而这3种事件可能出现在程序的任何位置,需要跟踪较长的执行序列并搜索潜在的危险事件序列才能检测到该漏洞,这很大程度上提高了检测的难度。该文针对UaF漏洞,分析了漏洞的产生原因、利用方式、带来的安全威胁以及漏洞检测技术面临的挑战,并设计和实现了一个基于静态分析和动态符号执行的面向二进制文件的UaF漏洞检测系统。经测试,该系统能够检测出已公开的UaF漏洞。通过该系统检测软件中的UaF漏洞,及时对软件进行修复或防护,可以有效提高软件的健壮性,减少UaF漏洞带来的安全隐患。
关键词use-after-free    静态分析    动态符号执行    
Detect use-after-free vulnerabilities in binaries
HAN Xinhui1, WEI Shuang1, YE Jiayi1, ZHANG Chao2, YE Zhiyuan1     
1. Institute of Computer Science and Technology, Peking University, Beijing 100080, China;
2. Institute for Network Science and Cyberspace, Tsinghua University, Beijing 100084, China
Abstract: Use-after-free (UaF) vulnerabilities are one of the most common and risky memory corruption vulnerabilities. However, UaF vulnerabilities are difficult to detect. A UaF vulnerability is triggered if and only if three operations occur on the same memory region, in an order of allocating memory, freeing memory, and using the freed memory. These three operations may be conducted anywhere in the program in any order, so the analysis must track a long execution sequence and search for potential vulnerable event sequences to detect UaF vulnerabilities. This study analyzes the root causes of UaF vulnerabilities, ways to exploit them, the severity of the threat and the challenges in detecting them. A solution is then given based on a static analysis and dynamic symbolic execution to detect UaF vulnerabilities in binaries. Tests show that this solution can detect known vulnerabilities in a benchmark. Thus, this detection system can be used to identify and fix bugs to improve application security.
Key words: use-after-free     static analysis     dynamic symbolic execution    

随着信息化的快速推进,计算机技术已经深入应用到了人民生活、国防建设的各个领域。在这种情形下,软件的安全性问题就愈加突出[1]。目前,很多软件都包含漏洞,而漏洞的存在会带来安全隐患。这些包含漏洞的软件在通常情况下可以正常运行,然而一些边界条件或特殊输入可能导致这些软件崩溃或者被攻击者利用。软件漏洞的存在通常会导致恶意代码执行或者信息泄露,进而导致机密信息或者隐私泄露、软件可用性遭到破坏或关键内容遭篡改等危害,并可能导致直接的经济损失及其他严重的后果。

近年来,use-after-free漏洞[2](简称UaF漏洞)的流行度和受关注度变高。UaF漏洞广泛存在于操作系统内核以及其他应用程序中,例如Flash、Internet Explorer、Firefox、Chrome等。UaF漏洞的危害性很高,可被攻击者利用。远程攻击者可以绕过现有的系统防御机制,利用UaF漏洞劫持控制流,执行远程代码,或者获取敏感信息。

鉴于UaF漏洞的高危害性,在攻击者利用漏洞之前发现并修复UaF漏洞,有利于加强计算机软件的稳定性、健壮性、安全性,减少UaF漏洞可能带来的损失。目前针对UaF漏洞检测的研究工作并不完善,尚不能满足安全需求,因此,设计检测方法从而有效检测软件中的UaF漏洞是本文的重点内容。

本文通过将静态分析和动态符号执行结合,设计并实现了面向二进制文件的UaF漏洞检测系统。经测试,本系统能够检测出已被美国的公共漏洞和披露数据库(common vulnerabilities and exposures, CVE)[3]公开的UaF漏洞。

1 相关工作

近年来随着UaF漏洞的流行度变高, 该漏洞的利用技术也有了突破,例如,堆喷射(heap spraying)[4]、堆风水(heap fengshui)[5]等技术可被用于对堆实施有效且稳定的攻击。于是,与UaF漏洞有关的研究工作也相应增多,对UaF漏洞的检测按照是否需要执行代码可分为静态分析和动态分析两类。

静态分析指的是不实际运行程序而评估代码的过程[6]。通过静态分析,可以较全面彻底地审查代码,并且不需要根据隐蔽的边界条件构造能触发软件缺陷的复杂样本。此外,静态分析不会增加程序执行时的额外开销,因为静态分析不需要程序运行时的信息。

但是,静态分析也有其不足之处。一般而言,静态分析报告中会包含误报(false positive)以及漏报(false negative)[7]

动态分析指的是通过实际运行程序,以获取运行时的信息,进而判定代码是否存在安全缺陷的过程。采用动态分析技术检测代码中是否存在安全漏洞的好处是分析结果中误报少甚至没有误报,缺点是由于构造的测试数据很难覆盖程序的所有分支而易产生漏报。

1.1 静态分析的相关工作

Bugalyze.com[8]基于反编译和数据流分析检测二进制文件中的UaF漏洞,但是它只能检测二进制文件中模式简单的UaF漏洞。Bugalyze.com并没有进行别名分析,会导致漏报;同时也没有进行可达路径分析,会导致误报。

GUEB (graph of use-after-free to exploit binary)[9]面向二进制文件基于数值分析/控制流分析检测UaF漏洞。它会跟踪对堆的操作和地址转换,并且对别名问题也进行了考虑。此外,它采用了专用数值分析,静态地识别程序中的UaF漏洞。最后它会为检测到的UaF漏洞生成一张子图,该子图描述了悬挂指针创建、释放和使用情况。然而,GUEB只分析了控制流图上的可达性,而在控制流图上可达的路径在实际运行时不一定是可达的,因此会产生误报。另外,该工具只能检测规模较小的程序。

AODA (available object definition analysis)是Dewey等[10]设计的用于发现C++二进制文件中UaF漏洞条件的分析方法。根据AODA,可以判断某个指针被引用时是否具有有效的定义。该工作的重点在于发现了满足文[10]中定义的UaF漏洞条件的代码。实际上部分疑似代码在程序实际运行中不会被触发,存在误报。

1.2 动态分析相关工作

Undangle[11]是一个动态分析二进制文件中的UaF漏洞的工具。它基于污点跟踪技术跟踪指针如何被拷贝,当内存被释放时,它可以确定指针是否还是指向被释放的内存。它可以报告在一个指定时间窗口内指向某一个特定内存位置的所有悬挂指针。然而,使用Undangle需要构造输入以得到包含UaF漏洞的路径。

Purify[12]、Valgrind[13]和AddressSanitizer[14]等工具能够监测解引用指针时,指针指向内存的有效性。当已释放的内存没有被重新分配的时候,悬挂指针指向的内存是无效的,因此这些工具能够检测出这种情况下的UaF漏洞。但是如果已释放的内存已经被重新分配,那么此时悬挂指针指向的内存是有效的,这些工具将会错过这种情形下的UaF漏洞。此外,这些工具需要修改目标程序来达到监测程序执行的目的,这会导致运行时的性能损失。其中,利用Valgrind来监测程序会使程序的运行速度减慢大约10倍;而使用AddressSanitizer需要向程序中插入代码,用运行库中的malloc和free替换原来的函数,从而使程序的运行速度减慢大约73%。

2 UaF漏洞检测方法的设计

本文设计了静态分析和动态符号执行相结合的UaF漏洞检测方法。采用静态分析方法的目的是尽可能全面地发掘源代码或二进制文件中的疑似UaF漏洞;而采用基于动态符号执行的分析技术,一方面可以通过执行以尽量探测能实际触发UaF漏洞的路径,另一方面通过监测程序实际运行状态以挖掘真正存在的UaF漏洞。静态分析和动态符号执行两者是相辅相成的关系:静态分析能够为动态符号执行提供关键信息,以驱使包含漏洞可能性大的路径得以执行;而动态符号执行能够消除静态分析得到的结果中的误报。两者相互协作以更好地完成UaF漏洞检测工作。

本文设计的检测方法整体框架如图 1所示。

图 1 UaF漏洞检测系统整体框架

3 面向二进制文件的UaF漏洞静态分析技术

本文对二进制文件反汇编后,对二进制文件执行数据流分析、控制流分析,以跟踪释放点、使用点,并结合过程间分析来挖掘过程间的UaF漏洞。

图 2展示了面向二进制的UaF静态分析框架。它以二进制文件为输入,对二进制文件进行数据流分析监测释放点、得到指向已释放内存的指针集、监测指针集中指针的使用情况;对二进制文件进行控制流分析遍历控制流图上可达的路径;对二进制文件进行过程间分析来检测过程间的疑似UaF漏洞。经过一系列分析后,将得到释放点/使用点信息、疑似路径信息等。

图 2 二进制文件检测框架

通过静态分析得到的信息包括释放点信息、使用点信息以及部分疑似路径信息。这些信息将用于辅助下一步的动态符号执行,因为静态分析得到的疑似漏洞误报率较高,通过对二进制文件进行动态符号执行可以降低误报率,而静态分析所得的结果又可以促进动态符号执行更好地进行。

3.1 数据流/控制流分析

本文通过分析二进制文件的控制流图对控制流图上可达的路径进行监测。

UaF漏洞检测的基本点即检测释放点和使用点。当检测到释放点后,本文对已知的指向被释放内存的指针作前向的数据流分析,从而得到其他指向被释放内存的指针。后向数据流分析指的是数据流方向与控制流方向相反的分析。后向数据流分析算法的伪代码如图 3所示。

图 3 后向数据流分析算法

该算法根据已知的指向已释放内存的指针以及到达释放点的逆向路径信息,获得指向已释放内存的指针的集合。该算法所需定义如图 4且算法描述如下。

图 4 后向数据流分析算法所需定义

逆向遍历到达释放点的路径,如果语句S是赋值语句,则对赋值语句的两边进行检查,因为赋值语句会影响指针指向的内容。如果赋值语句的左边在toCheckVals集合中,则说明还没有遇到对该变量的赋值,那么算法将检查赋值语句的右边。赋值语句的右边如果在checkedVals集合中,则说明该指针变量在之后会指向其他内存,因此不用将其放入指向已释放内存的指针变量集合中,不过依旧要将其放入toCheckVals集合中以寻找最近的赋值;而如果不在,则将其同时放入freedMemoryPointers集合和toCheckVals集合中。此外,该算法会将赋值语句的左边加入checkedVals集合,以维护已被赋值过的变量。

在完成后向数据流分析后,将得到该路径上所有指向已释放内存的指针集合,接着进行前向数据流分析,看是否存在使用指向已释放内存的指针的情况。与后向数据流分析相反,前向数据流分析指的是数据流和控制流方向一致的分析。

前向数据流分析检测是否有解引用指向已释放内存的指针的操作,如果有,则报告疑似UaF漏洞。如果原来指向已释放内存的指针指向了其他内存空间,则对其的解引用不会出现问题。因此,在进行前向数据流分析时,也要更新freedMemory-Pointers集合。

本文根据控制流图对每条路径进行数据流分析,即数据流分析的过程和控制流分析的过程是相结合的。

3.2 过程间分析

过程间分析可简化为检测具有释放内存行为的函数和跟踪全局/静态指针2个过程。

3.2.1 检测具有释放内存行为的函数

显然,二进制文件中存在过程间的UaF漏洞,即漏洞的释放点和使用点不在同一个函数中。一般看来,二进制文件的过程间分析比源代码的过程间分析困难。

本文采取一个简化的措施来实现过程间分析。以已知的释放函数为基础,寻找其他有释放内存行为的函数。有释放内存行为的函数指的是调用了已知的释放函数,且任一函数参数指向被释放内存的函数。找到所有有释放内存行为的函数后,过程间的分析就转换为过程内的分析。该算法可通过图 5描述。

图 5 具有释放内存行为的函数检测

该算法根据基本释放函数的信息,得到具有释放内存行为的函数的集合。基本释放函数的信息包括释放函数的名称、地址、参数等。

算法首先找到调用基本释放函数的函数F,然后检查F的函数参数与基本释放函数的参数是否存在别名关系。如果存在别名关系,则说明函数F实现的功能的一部分是对内存进行释放,即函数F释放了其参数指向的内存,于是算法将函数F加入具有释放内存行为的函数集合。

找到所有调用基本释放函数并且其参数与基本释放函数的参数存在别名关系的函数后,将以这些新找到的函数的集合S为基础进行下一轮迭代,也即寻找所有调用集合S中的函数并且其参数与集合S中的函数的参数存在别名关系的函数。如此循环,直到找到所有具有释放内存行为的函数集合。

该算法不仅记录具有释放内存行为的函数信息(地址、释放函数对应的参数等),还记录具有释放内存行为的函数与基本释放函数的对应关系。这个对应关系将在动态符号执行部分被用到。

3.2.2 跟踪全局/静态指针

对于可能改变指针别名关系的函数调用,需要进入函数内部才能相对准确地分析指针别名。本文重点关注了全局指针或静态指针指向内存的情况,因为全局指针或静态指针的生命周期较长,如果释放了全局指针或静态指针指向的内存,那么引入UaF漏洞的可能性较大。

通过遍历所有与全局指针或静态指针有关的赋值语句,本文对全局指针或静态指针指向内存的情况进行了跟踪,得到一个全局指针或静态指针可能指向的内存的集合,一旦该内存被释放,则进行报警,并且跟踪是否在释放内存后对全局指针或静态指针进行了解引用。

3.3 循环的处理

根据本文对已被报告UaF漏洞的分析,如果循环中存在释放函数,那么有可能存在UaF漏洞,而只将循环展开一次的静态分析是无法检测出此种UaF漏洞的,因此本文采取一旦循环中存在释放函数,即将对循环中的释放函数调用点进行标记、报告。而是否真的会引发UaF漏洞,将利用选择性符号执行技术进行进一步判断。对于不包含释放函数的循环,本文采取简单的策略,只将循环展开一次,这会导致误报,不过误报可以通过下一步的选择性符号执行消除。

4 基于动态符号执行的UaF漏洞分析技术

基于静态分析技术得到的疑似漏洞分析报告中包含误报。对于面向二进制文件的静态分析结果,误报原因主要是只分析了控制流图的可达性,而根据控制流图可达的路径在程序实际运行时可能是不可触发的。为了排除误报,本文设计了基于动态符号执行的UaF漏洞检测技术。

4.1 Concolic测试

Concolic测试指用具体(concrete)的赋值来辅助符号执行(symbolic execution),以减缓单纯的符号执行的路径爆炸问题以及单纯的具体赋值的代码覆盖率低问题。

如果要应用符号执行技术来检测软件缺陷,就要考虑如何减缓路径爆炸,可从以下3个方面筛选路径。

1) 排除不可达路径。在每个分支点,通过约束求解器判断分支是不是可达的。

2) 关注感兴趣的路径。针对不同的软件缺陷,设计不同的剪枝策略,排除不感兴趣的路径,只对感兴趣的路径进行分析。

3) 对路径深度进行限制,设置最大路径分支数和最长路径分析时间。

符号执行的效率受路径选择策略的影响。也就是说,当到达某个分支点时,先选择哪个分支执行会影响到符号执行的效率。有的情况下,需要设计选择策略以尽可能地覆盖程序代码,发现程序代码中的安全缺陷,例如,KLEE[15]设计了基于代码覆盖率的路径选择策略和随机路径选择策略。而有的情况下,需要设计选择策略使得感兴趣的路径得以执行,基本思想是选择更有可能触发某种特定安全缺陷的路径。

本文首先通过具体的输入得到一条程序的完整执行路径,该具体输入可以获取程序公开的测试用例,或者结合Fuzz技术生成程序的测试用例。在符号执行的过程中对某些部分代入具体的赋值可以加快符号执行的速度。

4.2 关键路径选择

利用符号执行技术时,本文不追求代码的覆盖率,而是尽可能选择能触发UaF漏洞的路径,这就需要结合静态分析中获取的释放点、使用点及路径信息。

对二进制文件进行静态分析后,可以得到可能引发UaF漏洞的释放点信息、使用点信息以及疑似UaF漏洞的路径信息,本文将以静态分析得到的信息为基础来选择关键路径。

首先,根据静态分析中疑似路径信息,在状态选择时优先选择在疑似路径中的状态。然后,选择在释放点之前的状态时,优先选择在控制流图中接近释放点的状态;选择在释放点和使用点之间的状态时,尽量选择在控制流图中接近使用点的状态。

本文根据控制流图预先计算释放函数调用点所在基本块与其他基本块之间的距离值,以此来作为关键路径选择的依据。计算距离值的算法参照单源最短路径算法。到达释放点以后,以类似的方法计算释放点和使用点之间的基本块到使用点的距离。有时候静态分析的结果中仅仅提供了可疑的释放点信息,而没有跟踪使用点信息。如果没有使用点的信息,则随机选择状态。

4.3 释放后使用

当程序解引用已释放的内存时,可能存在以下情况。

1) 该块内存未被重新分配。

a)如果读取该块内存,将获得内存释放之前程序所写入的内容,或者系统内存回收机制向已释放的内存填入的无意义的内容,在这种情况下,读取操作可能引起程序崩溃,也可能不影响程序的正常运行。

b)如果对该块内存实行的是写操作,只要操作的不是堆元数据,程序将正常运行。

2) 该块内存已被重新分配。

a)如果通过悬挂指针读取该块内存,将获得新对象的信息。

b)如果对该块内存实行写操作,将影响新对象,可能导致程序控制流发生变化。

根据以上分析,当程序动态执行时,仅仅从程序是否正常运行的表征是无法判断是否存在UaF漏洞的。为了判断是否使用了已释放的内存,可以记录已被释放的内存,在程序访问该块内存时报错。然而,如果内存被重新分配,那么被重新分配的部分将从已被释放的内存集合中移除,这时通过悬挂指针访问内存将不会产生错误报告,这会导致错过部分UaF漏洞。

为了更全面、直观地判断是否使用了已释放的内存,本文对疑似UaF漏洞中被标记为释放点的释放函数进行特殊处理。对程序进行静态分析可以得到疑似UaF漏洞的信息,包括疑似UaF漏洞中的释放函数地址、调用释放函数的指令的地址等信息。根据这些信息,本文可以跟踪与释放内存有关的操作。本文采用跳过释放函数的处理方式,以阻止系统真正释放内存。

具体地说,当关键的释放函数被调用时,本文会阻止系统释放内存的操作真正发生,并记录被释放内存的区间,包括被释放的内存的起始地址和长度。做了这样的处理后,该块内存实际上并没有被释放,也就不会随着程序的运行被重新分配。接着,本文将跟踪程序对内存的访问情况,如果程序访问的内存地址在已被释放的内存范围内,那么即存在释放后使用的情况,因此可以据此生成错误报告,并生成相应的触发UaF漏洞的测试用例。

因为本文阻止了系统释放内存操作,在系统内存有限的情况下,可能会导致内存不足的风险。但考虑到本文的处理并没有阻止所有的内存释放操作,而是针对性地只阻止了疑似路径中可能为释放点的释放函数,这种阻止操作的影响相对微小。此外,本文的目的是检测UaF漏洞,检测后进行修复,不会影响实际运行的程序,因此并不需要考虑阻止内存释放操作是否会导致内存泄露。通过这种方式检测到的漏洞是确实存在的。

5 系统实现与实验分析 5.1 面向二进制文件的UaF漏洞静态分析实现

本文利用IDA Pro对二进制文件进行反汇编,得到汇编形式的代码后,通过静态分析插件对汇编代码进行分析,最后得到疑似UaF漏洞。下文简单介绍IDA反汇编器,着重介绍基于IDA的UaF漏洞静态分析实现。

5.1.1 IDA反汇编器

IDA Pro[16]全名为交互式反汇编器专业版,是逆向工程二进制文件的利器。IDA功能强大,但其本身并不是一个漏洞发现工具。研究人员需要利用自身的技能,利用IDA对二进制文件完成更复杂的分析任务。IDA软件开发工具包为研究人员提供了分析二进制文件的接口。基于此,研究人员能够结合自身知识来开发插件,分析、挖掘二进制文件中的漏洞。

5.1.2 基于IDA的UaF漏洞静态分析实现

本文基于IDA Pro实现了对二进制文件的UaF漏洞的静态分析BUaFChecker,BUaFChecker的整体框架如图 6所示。首先,通过IDA Pro对二进制文件反汇编,能够得到汇编语言形式的代码;其次,BUaFChecker结合抽象语法树和控制流图,对汇编语言形式的代码进行静态分析;最后,输出经分类后的分析结果。

图 6 基于IDA的UaF漏洞分析

5.2 基于动态符号执行的UaF漏洞分析实现

本文结合静态分析的结果,通过设计关键路径选择策略来减缓符号执行可能引发的路径爆炸问题,并采用基于S2E[17]的动态符号执行技术进一步分析二进制文件,以排除实际不能被触发的疑似UaF漏洞,并且发掘切实存在的UaF漏洞。如图 7所示,本文根据S2E提供的选择接口和分析接口,实现了关键输入符号化、路径选择、释放函数处理、释放后使用检测等模块,当检测到UaF漏洞时将报告错误,并且生成触发UaF漏洞的用例。

图 7 基于S2E的UaF漏洞分析

5.3 面向二进制文件的UaF漏洞检测结果

本文从CTF (capture the flag)实例和实际项目两方面对UaF漏洞检测系统面向二进制文件的检测能力进行测试。

5.3.1 CTF实例检测

本文搜集了与UaF漏洞有关的CTF实例,例如DEF CON CTF 2014年资格赛中的一道名为“shitsco”的题以及Plaid CTF 2015中一道名为“Prodmanager”的题。本文的检测系统能够检测出这2道题中的UaF漏洞。下面结合shitsco中漏洞的情况介绍检测系统检测漏洞的分析过程和结果。

1) 漏洞成因。

首先介绍shitsco产生UaF漏洞的原因。shitsco维护了一个双向链表结构,其中链表的节点类型定义如图 8所示。可以看到,链表节点包含了指向名字的指针name、指向值的指针value、指向下一个链表节点的指针next、指向前一个链表节点的指针prev这4个域。除了链表的头节点以外,链表的其他节点都被分配在堆上。而链表的头节点位于程序的BSS段,是静态变量或者全局变量。

图 8 链表的节点类型定义

当程序想清除链表头节点的内容时,会释放name指针指向的内存以及value指针指向的内存,并把name指针和value指针设为空指针,但指向下一个链表节点的指针next并不会被更新。在这种情况下,如果程序在清除链表头节点内容后,又执行了将原来链表的第2个节点删除的操作,也就是说第2个链表节点所在内存会被释放,那么头节点的next指针就指向了已释放的内存。也就是说,链表头节点的next指针成了悬挂指针。

如果在这之后解引用了该悬挂指针,那么就会触发UaF漏洞。而链表头节点存在于BSS段,生命周期长,因此程序很可能会解引用该指针。实验证明,某些输入确实可以使shitsco程序解引用该悬挂指针。

2) 检测分析。

对于shitsco程序的检测过程包含了面向二进制的静态分析以及动态符号执行2部分。

本文先对shitsco程序进行静态分析。为了分析过程间的UaF漏洞,检测系统根据基本释放函数free得到了具有释放行为的函数的列表。此外,检测系统搜集了全局指针信息,包括shitsco程序中链表头节点的next指针以及prev指针。检测系统会对全局指针指向的内存的释放情况进行跟踪。

检测系统通过数据流分析/控制流分析定位了几个关键的释放函数调用点。如图 9所示,这几个释放函数调用点的指令地址分别是0x08048F75、0x08048F77、0x08048F75,分别释放了name指针指向的内存、value指针指向的内存以及链表节点。经分析有全局指针指向这几个释放函数调用点释放的内存,因此检测系统对这几个函数调用点进行重点标记。此外,这几个释放函数调用点都位于循环中,检测系统也会对循环中的释放函数调用点进行标记。因为释放操作在循环内,所以检测系统不再跟踪可能的使用点,而是将释放函数调用点地址以及到达释放函数调用点的路径信息提供给之后的动态符号执行过程。

图 9 Shitsco程序中的关键释放函数调用点

进行完静态分析后,检测系统通过动态符号执行来挖掘、确认UaF漏洞。检测系统跟踪几个关键的释放函数调用点,跳过实际的释放内存操作,并记录释放内存的区间。检测系统会对程序访问内存的情况进行监测。程序的输入先是随机产生,接着检测系统的符号执行部分会根据已有的路径扩展出更多的程序路径,并根据关键路径选择策略选择经过关键释放函数调用点的路径执行。

最后,检测系统发现如果先清除了链表的头节点的内容,接着地址为0x08048F75的指令释放了头节点的next指针指向的内存,那么链表的头节点的next指针就成为了悬挂指针,而该悬挂指针会在打印链表所包含内容的函数中被解引用,从而引发UaF漏洞。

5.3.2 基于实际项目检测分析

本文将检测系统应用到检测实际项目的UaF漏洞上。虽然以下项目的源代码也是公开可获得的,但为了测试检测系统面向二进制文件的检测能力,仍以二进制文件为输入对象。

1) libxml2。

本文利用检测系统对libxml2(v2.5.10) 进行检测。首先,对二进制文件进行静态分析,静态分析报告了5个疑似漏洞;其次,结合动态符号执行对疑似漏洞进行验证,排除了2个误报,剩下3个是确实存在的UaF漏洞。检测出的漏洞已被披露在CVE-2009-2416中。

2) Privoxy。

本文利用检测系统对Privoxy(v3.0.17) 进行检测。首先,对二进制文件进行静态分析,静态分析报告了3个疑似漏洞;其次,结合动态符号执行对疑似漏洞进行验证,排除了1个误报,剩下的2个是确实存在的UaF漏洞。检测出的漏洞已被披露在CVE-2015-1031中。

6 结论

本文以实现UaF漏洞检测系统为总体目标,设计了基于静态分析和动态符号执行的检测方法。通过设计面向二进制文件的UaF漏洞静态分析技术,对释放内存的操作、疑似使用指向内存的指针的操作进行了跟踪,能够得到疑似UaF漏洞的释放点、使用点、路径等信息。通过设计基于动态符号执行的UaF漏洞分析技术,结合静态分析的结果,根据关键路径选择策略选择触发漏洞可能性高的路径执行,一方面可以减缓路径爆炸问题,另一方面可以提高触发漏洞的几率。当UaF漏洞被触发时,检测系统将会报错,从而检测到切实存在的UaF漏洞。本文使用该系统检测CTF实例和实际项目中的UaF漏洞,能够检测出其中已被公开的UaF漏洞。

参考文献
[1] 李舟军, 张俊贤, 廖湘科, 等. 软件安全漏洞检测技术[J]. 计算机学报, 2015, 38(4): 717–732. LI Zhoujun, ZHANG Junxian, LIAO Xiangke, et al. Survey of software vulnerability detection techniques[J]. Journal of Computers, 2015, 38(4): 717–732. (in Chinese)
[2] Afek J, Sharabani A. Dangling pointer-smashing the pointer for fun and profit[J]. A Whitepaper from Watchfire Citado na, 2007, 41(1): 1–21.
[3] Corporation M. Common vulnerabilities and exposures (CVE) [Z/OL]. [2016-5-10]. http://cve.mitre.org.
[4] Daniel M, Honoroff J, Miller C. Engineering heap overflow exploits with JavaScript [C]// USENIX Workshop on Offensive Technologies. San Jose, CA, USA: USENIX, 2008: 1-6. https://www.usenix.org/event/woot08/tech/full_papers/daniel/daniel_html/
[5] Sotirov A. Heap feng shui in JavaScript [C]//Black Hat Europe 2013. Amesterdam, Netherlands: Black Hat, 2013: 1-20. https://www.blackhat.com/presentations/bh-europe-07/Sotirov/Presentation/bh-eu-07-sotirov-apr19.pdf
[6] Chess B, McGraw G. Static analysis for security[J]. IEEE Security & Privacy, 2004, 2(6): 76–79.
[7] Pistoia M, Chandra S, Fink S J, et al. A survey of static analysis methods for identifying security vulnerabilities in software systems[J]. Ibm Systems Journal, 2007, 46(2): 265–288. DOI:10.1147/sj.462.0265
[8] Cesare S. Bugalyze.com-detecting bugs using decompilation and data flow analysis [C]//Black Hat USA 2013. Las Vegas, NV, USA: Black Hat, 2013: 1-9. https://www.slideshare.net/SilvioCesare/detecting-bugs-in-binaries-using-decompilation-and-data-flow-analysis
[9] Feist J, Mounier L, Potet M L. Statically detecting use after free on binary code[J]. Journal of Computer Virology and Hacking Techniques, 2014, 10(3): 211–217. DOI:10.1007/s11416-014-0203-1
[10] Dewey D, Reaves B, Traynor P. Uncovering use-after-free conditions in compiled code [C]// 2015 10th International Conference on Availability, Reliability and Security. Reggio Calabria, Italy: IEEE, 2015: 90-99. http://dl.acm.org/citation.cfm?id=2849099
[11] 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. Minneapolis, MN, USA: ACM, 2012: 133-143. http://software.imdea.org/~juanca/papers/undangle_issta12_av.pdf
[12] Hastings R, Joyce B. Purify: Fast detection of memory leaks and access errors [C]// Proceedings of the Winter 1992 USENIX Conference. San Antonio, TX, USA: USENIX, 1991: 125-136. http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.88.9102
[13] Nethercote N, Seward J. Valgrind: A framework for heavyweight dynamic binary instrumentation[J]. Acm Sigplan Notices, 2007, 42(6): 89–100. DOI:10.1145/1273442
[14] Serebryany K, Bruening D, Potapenko A, et al. AddressSanitizer: A fast address sanity checker [C]//2012 USENIX Annual Technical Conference (USENIX ATC 12). Boston, MA, USA: USENIX, 2012: 309-318. https://research.google.com/pubs/pub37752.html
[15] Cadar C, Dunbar D, Engler D R. KLEE: Unassisted and automatic generation of high-coverage tests for complex systems programs [C]// 8th USENIX Symposium on Operating Systems Design and Implementation. San Diego, CA, USA: USENIX, 2008: 209-224.
[16] Eagle C. The IDA Pro Book: The Unofficial Guide to the World's Most Popular Disassembler[M]. San Francisco, CA, USA: No Starch Press, 2011.
[17] 王学, 李学新, 周智鹏, 等. S2E测试平台及并行性能分析[J]. 信息网络安全, 2012(7): 16–19. WANG Xue, LI Xuexin, ZHOU Zhipeng, et al. Analysis of the software testing platform: S2E[J]. Netinfo Security, 2012(7): 16–19. (in Chinese)