1、组件拒绝服务漏洞自动挖掘技术 周敏 周安民 刘亮 贾鹏 谭翠江 四川大学电子信息学院 摘 要: 针对 Android 应用对获取到的数据没有进行空数据和异常数据的安全验证, 会发生崩溃导致组件拒绝服务的问题, 提出了一种组件拒绝服务漏洞自动化挖掘框架。通过逆向分析和静态数据流分析技术获取安卓应用的包名和组件信息, 同时跟踪应用对 Intent 对象的数据访问, 提取 Intent 对象携带的数据信息, 并且识别公开组件启动私有组件的路径信息, 辅助动态模糊测试挖掘漏洞。为了增大测试用例的覆盖范围和实现自动化, 该框架增加了对 Intent 的Action、Category、Data 和 Ext
2、ra 属性的畸变, 并且采用 Accessibility 技术自动关闭应用崩溃弹窗, 大幅提高了检测效率。为了验证框架的有效性和实用性, 利用所提的框架设计实现了工具DroidRVMS, 并与 Intent Fuzzer 工具进行了对比。实验结果表明, DroidRVMS 能够有效地发现动态广播组件的拒绝服务漏洞和大部分类型异常导致的拒绝服务攻击。关键词: 组件通信; 拒绝服务; 静态分析; 动态分析; 漏洞挖掘; 作者简介:周敏 (1994) , 女, 陕西汉中人, 硕士研究生, 主要研究方向:移动安全、恶意代码分析、漏洞挖掘;作者简介:周安民 (1963) , 男, 四川成都人, 研究员,
3、 主要研究方向:安全防御管理、移动互联网安全、云计算安全;作者简介:刘亮 (1982) , 男, 四川成都人, 讲师, 硕士, 主要研究方向:漏洞挖掘、恶意代码分析;作者简介:贾鹏 (1988) , 男, 四川成都人, 博士研究生, 主要研究方向:病毒传播动力学、二进制安全、恶意代码分析;作者简介:谭翠江 (1991) , 男, 四川成都人, 硕士研究生, 主要研究方向:移动安全、恶意代码分析、漏洞挖掘。收稿日期:2017-05-12Mining denial of service vulnerability in Android applications automaticallyZHOU
4、Min ZHOU Anmin LIU Liang JIA Peng TAN Cuijiang College of Electronics and Information, Sichuan University; Abstract: Concerning the fact that when the receiver of an Intent does not validate empty data and abnormal data, the process will crash and cause denial of service, an automated Android compon
5、ent vulnerability mining framework based on static analysis techniques and fuzzing test techniques was proposed. In this framework, reverse analysis techniques and static data flow analysis techniques were used to extract package name, component, Intent with the data of a traffic and data flow paths
6、 from exported component to private component to assist fuzzing test. In addition, more mutation strategy on the attributes of Intent ( such as Action, Category, Data and Extra) were added while generating Intent tests and the Accessibility technology was adopted to close the crash windows in order
7、to realize automation. Finally, a tool named DroidRVMS was implemented, and a comparative experiment with Intent Fuzzer was designed to verify the validity of the framework. The experimental results show that DroidRVMS can find denial of service vulnerability resulting from dynamic broadcast receive
8、r and most types of exceptions.Keyword: component communication; denial of service; static analysis; dynamic analysis; vulnerability mining; Received: 2017-05-120 引言移动平台上各式各样的应用程序呈指数式爆炸增长, 其中, 基于 Android 系统的应用程序更是占据了绝对优势。但是, 由于 Android 应用开发门槛较低, 开发者水平良莠不齐, 一些应用往往会因为开发者的安全意识不强、逻辑不够严谨或者没有对应用进行异常处理而存在安
9、全漏洞。因此, 安卓应用很容易出现安全漏洞。安卓应用是由功能丰富的组件构成的。这些组件可以实现用户交互界面、后台服务、广播通知等功能。同时, 安卓系统提供了 Intent 机制来帮助这些组件实现应用间通信和应用内通信。借助 Intent, 开发者研发的任意应用可以和软件中的开放组件进行通信并传递任意类型的数据。但是如果应用在解析通过Intent 获取的数据时没有进行异常捕获, 畸形数据就可能使应用崩溃, 导致应用的部分功能不可用, 更甚者会导致整个应用瘫痪。利用这种漏洞, 攻击者对应用发起拒绝服务攻击, 不仅能够直接影响用户体验, 而且还能导致安全防护应用的防护功能被绕过或失效, 进而危害用户
10、的隐私和财产安全。如何有效地实现对该类漏洞的挖掘, 保障 Android 用户的权益是亟待解决的问题1。在漏洞挖掘方面, 目前通用的方法是静态挖掘和动态挖掘。静态挖掘方法主要是使用了代码审计的思想, 结合应用的函数调用图、控制流图以及敏感数据的传播路径对 Android 程序的源代码进行静态分析来进行漏洞挖掘, 具有速度快, 覆盖率全的优点, 可用于组件劫持漏洞挖掘2、隐私泄露漏洞挖掘3-5、Intent 注入漏洞挖掘6等研究工作, 但是误报率比较高。动态挖掘方法目前主要以 fuzzing 技术7为主, 广泛地应用于挖掘应用组件间通信机制和系统短信应用的漏洞, 如 Yang 等8使用模糊测试技
11、术对应用的功能泄露漏洞进行挖掘, Mulliner 等9使用模糊测试技术对系统短信的漏洞进行挖掘。与静态挖掘方法相比, 动态挖掘方法提高了准确率和自动化程度, 但存在样本覆盖率低、效率不高的缺点。本文在研究 Intent 机制的基础上提出了一种面向 Android 组件拒绝服务漏洞的自动化漏洞挖掘框架。该框架主要由动态分析模块和静态分析模块两部分组成。其中, 静态分析使用了逆向分析技术和数据流跟踪技术, 提取应用的组件相关信息 (如公开组件信息、动态注册的广播等) , 以及公开组件启动私有组件的路径信息来辅助模糊测试。而动态分析则基于静态分析获取的结果, 使用模糊测试技术, 对应用进行空 In
12、tent 测试、类型错误 Intent 测试、畸变 Intent 测试和异常 Intent 测试, 进一步挖掘拒绝服务漏洞。此外, 本文在模糊测试过程中采用 Accessibility 技术将应用崩溃弹窗进行关闭, 实现了完全自动化。1 背景知识与相关工作1.1 公开组件Android 应用程序通常由一个或多个基本组件共同完成其功能。而 Android 应用程序的组件主要有活动 (Activity) 、服务 (Services) 、广播 (Broadcast Receiver) 和内容提供者 (Content Provider) 。其中, 前三者可以通过Intent 对象启动。本文将应用组件分
13、为公开组件和私有组件两类:公开组件指应用对外暴露, 能被其他应用通过 Intent 启动的组件;而只能被应用内部其他组件访问的组件称为私有组件1。应用程序组件在 Android Manifest.xml 文件中被声明为公开组件有两种方式:第一种是显式声明, 即直接将组件的 Android:exported 属性设为 true1;第二种是隐式声明, 即为组件添加一个或者多个 Intent-filter 过滤器的方式1。1.2 启动私有组件当私有组件被公开组件启动时, 也存在发生崩溃的情况。如下所示, 应用中存在一个私有组件 Private Activity 和一个公开组件 Main Activi
14、ty。Main Activity 能够根据对外传入的 Intent 中的内容启动私有组件 Private Activity, 且启动私有组件的 Intent 的内容来自于启动公开组件的 Intent 的内容。但是, 私有组件 Private Activity 没有对从 Intent 中获取的内容进行异常处理, 故攻击者精心构造的 Intent 内容可以通过公开组件 Main Activity让私有组件 Private Activity 崩溃, 从而发生拒绝服务攻击。1.3 本地拒绝服务漏洞介绍本文将产生本地拒绝服务的原因分为 5 种:空指针异常导致的漏洞、类型转换异常导致的漏洞、数组访问越界异
15、常导致的漏洞、类未定义异常导致的漏洞以及其他异常导致的漏洞10。1) 当应用程序没有对 get Action () 、get String Extra () 等函数获取到的数据进行空指针判断, 从而产生空指针异常而导致应用崩溃, 这称为空指针异常拒绝服务漏洞 (Null Pointer Exception) 10以 get Action () 函数为例, 产生漏洞的伪代码如下所示:2) 当应用程序没有对 get Serializable Extra () 等函数获取到的数据进行类型判断就直接进行强制类型转换, 从而产生类型转换异常而导致应用崩溃, 这称为类型转换异常拒绝服务漏洞 (Class
16、Cast Exception) 10。以 get Serializable Extra () 函数为例, 产生漏洞的伪代码如下所示:3) 当应用程序没有对 get Integer Array List Extra () 等函数获取到的数据数组元素大小进行判断, 从而产生数组访问越界而导致应用崩溃, 这称为数组访问越界异常拒绝服务漏洞 (Index Out Of Bounds Exceptio) 10。以get Integer Array List Extra () 函数为例, 产生漏洞的伪代码如下所示:4) 当应用程序无法找到从 get Serializable Extra () 、get P
17、arcelable () 等函数中获取到的序列化类对象的类定义, 从而发生类未定义的异常而导致应用崩溃, 这称为类未定义异常拒绝服务漏洞 (Class Not Found Exception) 10。以 get Serializable Extra () 函数为例, 产生漏洞的伪代码如下所示:5) 其他异常导致的拒绝服务漏洞:如 Illegal Argument Exception、Unsupported Operation Exception 异常等导致的拒绝服务。1.4 相关研究及挑战目前, 针对应用组件拒绝服务漏洞, 主流的漏洞挖掘工具是 i SEC Partners 公司设计的 Int
18、ent Fuzzer 工具11。该工具运行在 Android 设备上, 其工作原理是通过 Package Manager 获取系统中所有安装应用的信息, 以及各应用中Activity、Service 和 Broadcast Receiver 这 3 类暴露组件的信息, 然后通过调用 start Activity (Intent) 、start Service (Intent) 、send Broadcast (Intent) 等启动函数发送 Intent 消息到 Activity、Service 和Broadcast Receiver 组件中进行测试。但该工具不支持对 Activity 类组件
19、进行大量的测试。此外, Intent Fuzzer 工具还存在不能完全实现自动化测试、没有日志输出和仅发送空 Intent 消息等问题。Maji 等12对 Intent Fuzzer 工具进行了改进, 设计了 Jar Jar Binks 工具。该工具通过 start Activity ForResult (Intent, int) 函数来启动应用程序的 Activitiy, 进而可以通过调用 finish Activity () 函数来关闭 Actvity, 回收系统资源, 实现了对大量的 Activity 进行测试。与 Intent Fuzzer 相比, Jar Jar Binks 工具不仅
20、实现了对 Activity 类组件的测试, 而且 Intent 测试用例增加了随机的 Extras, 实现了更广泛的测试。但 Jar Jar Binks 工具不能准确获得应用中 Extras 的 type 和 name 信息, 因而仍然不能对组件进行深入有效的 Fuzzing 测试。实际上, 现有的工具都不能自动关闭应用崩溃时的系统弹窗, 不能完全实现自动化测试;而且现有工具都是只对 Android Manifest.xml 文件中注册的公开组件进行测试, 不能对应用中动态注册的 Broadcast Receiver 组件进行测试, 也不能对通用型的 Android 应用组件拒绝服务进行测试。
21、因此, 为了实现基于 Fuzzing 的 Android 组件拒绝服务漏洞的自动化挖掘, 其挑战在于如何在扩大测试用例覆盖范围的基础上, 自动关闭应用崩溃时弹出的窗口, 实现批量应用的漏洞挖掘, 提高漏洞挖掘效率。1) 如何扩大测试用例的覆盖范围?不仅要考虑到应用中动态注册的 Broadcast Receiver 组件的测试和私有组件的测试, 还要考虑对 Intent 更多属性的随机测试, 以及畸变策略的多样化。2) 如何自动关闭应用崩溃窗口, 实现完全自动化?使用 Android Accessibility 机制实现对程序崩溃时系统弹窗的实时监视和关闭, 让模糊测试在每次被弹窗阻塞后得以继续
22、执行, 实现完全自动化的应用组件拒绝服务漏洞挖掘。2 基于逆向工程和数据流的静态分析静态分析主要进行逆向分析和数据流分析的工作, 挖掘私有组件导致的拒绝服务漏洞。2.1 逆向分析首先, 需要检测 APK 文件是否进行了加固处理, 由于经过加固处理的 APK 文件逆向分析得不到应用程序真正的代码, 所以本文不对其进行考虑。对于未经过加固处理的 APK 文件, 该工作主要是将 APK 文件进行解压缩, 提取APK 中的 Dex、Android Manifest.xml 等文件。然后使用 AXMLPrinter2.jar 工具将二进制的 Android Manifest.xml 文件解析成文本形式的
23、 xml 文件, 提取应用的组件和包名等信息。同时使用 dex2jar 工具将 DEX 文件转换成 jar 文件, 再使用 cfr 工具将 jar 文件反编译成 Java 源码文件, 得到代码中动态注册的Broadcast Receiver 信息, 组件间的调用关系以及各组件接收 Intent 消息的Extra 数据名称和类型。将获取的这些信息保存于 info.txt 文件中, 后面用于动态分析时确定待测组件以及根据变异策略生成测试用例。另外, 还要使用baksmali.jar 工具将 DEX 文件反编译成 Smali 文件, 用于 2.2 节的数据流分析工作。2.2 数据流分析数据流分析工作
24、主要是构建控制流图来表示公开组件对私有组件的启动情况, 从而得到存在拒绝服务的私有组件名称和发生崩溃的启动路径。控制流图的构建是基于 DEX 文件反编译后的 Smali 代码上的, 避免了用现有工具生成控制流图时引入大量无关系统库函数的问题, 提高了分析效率。构图时, 以每个公开组件的入口函数作为入口点, 分别构造组件内控制流图和组件间控制流图, 然后将各部分控制流图连接成一个完整的控制流图13。生成组件内控制流图时, 根据 if-else, try-catch 这类跳转指令对 Smali 代码进行分块, 每一个代码块作为控制流图的一个节点, 代码块之间的跳转作为控制流图的有向边, 指向程序的
25、执行方向, 其中, Intent 的接收者对接收数据时可能发生的异常进行了正确处理的, 其有向边用实线表示, 若没有, 就用虚线表示。为了保证完整, 需要同时对与组件生命周期内各种状态相关的系统回调函数进行控制流图的构建。然后再根据回调函数的执行顺序, 在回调函数之间添加对应的有向边 (实线还是虚线) 。生成组件间控制流图时, 根据应用组件间的 Intent 调用关系, 包括显式Intent 调用和隐式 Intent 调用, 将组件内控制流图连接起来, 成为一个完整的控制流图。由于控制流图是一个连通图, 本文采用深度优先搜索 (Depth First Search, DFS) 14算法对得到的
26、完整控制流图进行路径搜索, 获取控制流图中全部的未经 try-catch 指令异常处理的调用路径。以图 1 为例, 说明使用深度优先搜索算法搜索符合条件的路径的过程, 其中, v0 节点表示公开组件入口函数所在节点, v4 节点表示私有组件入口函数所在的节点。图 1 深度优先搜索算法搜索路径的过程 Fig.1 Algorithm of searching path by DFS 下载原图1) 首先建立一个存储节点的栈空间, 将起始节点 v0 入栈, 并将其标记为入栈状态。2) 从 v0 节点出发, 找到下一个相邻的非入栈节点 v1、v2 或 v3, 假设先访问v1 节点, 将 v1 节点入栈,
27、 并标记为入栈状态。然后判断 v0 和 v1 之间的有向边是否为虚线:若是, 继续向下来到 v1 节点, 重复步骤 2) ;若不是, 从栈顶弹出 v1 节点, 并将 v1 节点标记为非入栈状态, 继续对 v0 的下一个除 v1 节点外相邻的非入栈节点重复步骤 2) 。3) 由于这里 v0 和 v1 之间是实线, v0 和 v2 之间是虚线, 所以此时栈顶是 v2节点。4) 从 v2 节点出发, 找到 v2 节点的下一个相邻的非入栈节点 v4, 将 v4 节点入栈, 并标记为入栈状态。然后, 判断 v2 和 v4 之间的有向边是否为虚线:若是, 继续向下来到 v4, 由于 v4 是终止节点, 因
28、此得到一条路径;若不是, 从栈顶弹出 v2 节点, 并将 v2 节点标记为非入栈状态, 继续对 v0 下一个除 v1, v2 节点外相邻的非入栈节点重复步骤 2) 。5) 由于这里 v2 和 v4 之间是实线, v0 和 v3 之间是虚线, 所以此时栈顶是 v3节点。6) 从 v3 节点出发, 重复步骤 4) , 由于 v3 与 v4 之间是虚线且 v4 是终止节点, 所以找到一条路径 v0v3v4。7) 此时栈顶是 v4 节点。从栈顶弹出 v4 节点, 并将 v4 节点标记为非入栈状态。8) 此时栈顶是 v2 节点, 由于 v2 节点没有除刚出栈的 v4 节点外的相邻非入栈节点, 因此将 v
29、2 节点出栈, 并标记为非入栈状态。9) 此时栈顶是 v0 节点, 由于与 v0 节点相邻的节点都已被访问过, 因此弹出v0 节点, 并标记为非入栈状态。此时栈空间为空, 结束整个路径搜索过程。利用深度优先算法获取到所有未经异常处理的公开组件启动私有组件的路径后。将这些路径以及对应的公开组件和私有组件名称保存于 Vul Path.txt 文件中, 后续结合相关的 Extra 信息用来生成特殊的造成系统服务级联崩溃的测试用例, 用来测试私有组件。3 基于 Fuzzing 和 Accessibility 的动态分析3.1 模糊测试3.1.1 测试用例生成策略为了增大测试用例的覆盖范围, 本文针对
30、Intent 的Component、Action、Category、Data、Extra 这 5 种属性构造测试用例。其中, 采用的 Intent 构建策略有六类。第一类测试用例是不携带任何额外数据的 Intent 消息, 即直接使用 Android Manifest.xml 文件和 DEX 文件分析得到的组件包名和组件名设置 Intent 的Component 属性, 而其他属性为空15。第二类测试用例只设置了 Action、Category、Data 三种属性中的任意一种, 其余两种属性为空或随机值。其中 Action16和 Category16属性的值均取自Android 提供的标准值,
31、 Data 属性的值取自构建的几类典型的 Data URL。构建的几类经典的 Data URL 如表 1 所示15。表 1 几类典型 Data URL Tab.1 Typical Data URL 下载原表 第三类测试用例设置了 Action、Category、Data 三种属性中的任意两种, 另一种属性为空或随机值。同样, Action 和 Category 属性的值均取自 Android 提供的标准值, Data 属性的值取自构建的几类典型的 Data 值15。第四类测试用例则同时设置了 Action、Category、Data 三种属性。其中Action 和 Category 属性的值取
32、自 Android 提供的标准值, Data 属性的值取自构建的几类典型的 Data 值15。第五类测试用例是在前四类测试用例的基础上增加对 Extras 属性的设置。此时, 测试用例需指定 Extras 属性中的数据名称15, 其值已在 DEX 文件分析时获取, 数据值则是随机的。为了提高效率, 针对 Extras 属性常携带的几类数据类型, 本文对不同的数据类型分别设计了典型的数值进行数据填充, 如表 2 所示17。表 2 Extras 属性各数据类型的数值填充模板 Tab.2 Padding template of extras attribute for each data type
33、下载原表 第六类测试用例是针对 Vul Path.txt 文件中信息和 Extras 属性构建的, 用来测试通过级联反应引起的私有组件的崩溃。其中, Extra 属性名称已在 DEX 文件分析时获取到, 数据值是随机的。通过上述的测试用例生成策略, 可以生成与目标组件所接收的 Intent 的属性完全匹配、半匹配以及完全不匹配的 Intent 测试用例, 最大限度地提高了测试用例的覆盖范围。3.1.2 测试过程模糊测试的流程为:1) 基于测试用例生成策略构造大量的多类型的 Intent。2) 将待测试的应用安装到 Android 模拟器上。3) 借助 ADB 工具将测试用例发送给目标应用的每一
34、个公开组件和动态注册的广播组件。4) 使用 Logcat 收集测试期间的各类日志信息。5) 通过对日志信息中“Fatal Exception:main”字符串的监控来获取应用程序崩溃时的相关的日志信息, 以及保存造成崩溃的测试用例。这里仅将测试用例以显式 Intent 的方式发送给公开组件和动态广播组件。但是表 1 构建的几类经典的 Data URL 数据中 content:/*数据值可以测试Content Provider 组件, 测试用例生成策略六可以用来测试私有组件。3.2 应用崩溃监控在实际测试过程中, 当应用程序发生崩溃时, Android 系统会进行系统级别的弹窗以提示用户应用程序
35、已停止运行, 需要手动关闭, 这在很大程度上影响了自动化的实现。本文利用 Android Accessibility 机制能够实时获取当前 Android 设备窗口状态改变信息和窗口元素信息的特点, 通过监听窗口状态的改变来判断系统弹窗, 获取窗口的元素信息, 采用模拟点击技术关闭弹窗, 成功解决了该问题, 实现了完全自动化。其具体实现步骤为:步骤 1 创建一个 My Accessibility Service 类继承 Accessibility 机制提供的Accessibility Service 接口类。步骤 2 注册崩溃窗口监听事件。根据应用崩溃时, 窗口上通常会出现“很抱歉, xxx
36、已停止运行。”的提示特征对应用崩溃弹窗进行监听。步骤 3 实现关闭窗口事件。如果出现了应用崩溃弹窗, 则通过 find Accessibility Node Info By Text 函数找到窗口中的“确定”按钮, 执行点击操作将弹窗进行关闭。自动化关闭窗口事件的伪代码如下:4 实验与分析为了验证框架的有效性, 本文基于该框架设计实现了工具DroidRVMS, 使用该工具对从 Android 应用市场下载的 300 个应用程序进行了组件拒绝服务漏洞挖掘实验, 同时, 使用开源的组件拒绝服务漏洞挖掘工具 Intent Fuzzer11对相同的测试样本进行漏洞挖掘, 并将测试结果与 DroidRVMS 的测试结果进行对比, 表明 DroidRVMS 具有有效性和实用性。4.1 实验数据和实验环境