4.1.2 二进制漏洞挖掘

4.1.2 二进制漏洞挖掘

相比源码分析,二进制分析更具备商业价值。本节介绍模糊测试、动态污点分析、智能灰盒测试和二进制代码对比。虽然源代码漏洞分析具有分析范围大、语义信息丰富等特点,但在实际应用中大量使用的商业软件均以二进制代码形式存在,因此研究针对二进制代码的漏洞分析技术具有很强的实用价值。

二进制漏洞分析技术是一种面向二进制可执行代码的软件安全性分析技术,通过对二进制可执行代码进行多层次(指令级、结构化、形式化等)、多角度(外部接口测试、内部结构测试等)的分析,发现软件中的安全漏洞。因此,二进制漏洞分析的对象是源代码程序编译后生成的二进制代码,分析主要涵盖的技术环节包括反汇编逆向分析、汇编代码结构化、中间表示、漏洞建模、动态数据流分析/污点分析、控制流分析/符号执行等。通过对二进制代码进行反汇编逆向分析,能够得到与之对应的汇编代码,借助汇编代码,分析人员能够在一定程度上获取目标程序的控制流程、逻辑过程等,再通过动态执行二进制程序还能进一步获取程序对各种数据的处理过程。二进制漏洞分析的一般原理如图4.13所示。

图4.13 二进制漏洞分析一般原理

现有的二进制漏洞分析技术种类繁多,从操作的自动化角度,可分为手工分析和自动/半自动化分析;从软件的运行角度,可分为静态分析和动态分析;从软件代码的开放性角度,可划分为白盒测试、黑盒测试和灰盒测试。由此可见,二进制漏洞分析是一个涉及诸多技术的复杂工程。在本章中,我们将从软件运行的角度出发将二进制漏洞分析技术分为三类:静态分析、动态分析和动静结合分析,以此发现二进制程序中的安全漏洞。其中静态分析技术包括基于模式的漏洞分析和二进制代码比对;动态分析技术主要是模糊测试技术,包括随机模糊测试和智能模糊测试等;动静结合分析技术包括智能灰盒测试和动态污点分析等。整体分类结构如图4.14所示。

图4.14 二进制漏洞分析技术分类

1.Fuzzing测试

(1)基本概念

模糊测试(fuzz testing或fuzzing)是软件漏洞分析的代表性技术,在软件漏洞分析领域占据重要地位。从漏洞分析的角度出发,模糊测试是一种通过构造非预期的输入数据并监视目标软件在运行过程中的异常结果来发现软件故障的方法。模糊测试在很大程度上是一种强制性的技术,简单且有效,但测试存在盲目性。典型的模糊测试过程是通过自动的或半自动的方法,反复驱动目标软件运行并为其提供构造的输入数据,同时监控软件运行的异常结果。其基本思想是:向待测程序提供大量特殊构造的或是随机的数据作为输入,监视程序运行过程中的异常并记录导致异常的输入数据,辅助以人工分析,基于导致异常的输入数据进一步定位软件中漏洞的位置。

(2)基本原理

模糊测试的具体方法随着测试元素的不同而变化,没有一种对所有测试都适用的模糊测试方法。模糊测试方法的选择完全取决于目标应用程序、研究者的技能,以及被测试数据的格式。一般来说,无论针对何种对象进行测试,也无论选择了哪种方法,模糊测试总要经历5个基本阶段,如图4.15所示。

图4.15 模糊测试的5个基本阶段

1)识别目标。在选择目标应用程序时,通常可以通过浏览安全漏洞收集网站(如CNNVD、Exploit-db或Secunia)来考察软件开发商的安全漏洞相关历史。如果某开发商在安全漏洞历史记录方面表现不佳,很可能是由于其编码习惯较差,故针对该开发商的软件进行模糊测试从而发现更多安全漏洞的可能性也较大。选择了目标应用程序之后,还可能需要选择应用程序中具体的目标文件或库。如果需要选择目标文件或库,就应该选择那些被多个应用程序共享的库(这些库的用户群体较大)。

2)识别输入。几乎所有可能被利用的漏洞,都是由于应用程序接受了用户的输入,并且在处理输入数据时未正确地处理非法数据或执行确认例程而导致的。未能定位可能的输入源或预期的输入值对模糊测试将产生严重的影响,因此枚举输入向量对模糊测试的成功至关重要。尽管有一些输入向量很明显,但是大多数还是难以捉摸,因而在查找输入向量时应该运用发散式思维。值得注意的是,发往目标应用程序的任何输入都应该被认为是输入向量,因此都应该是可能的模糊测试变量。如输入向量包括消息头、文件名、环境变量、注册键值等。

3)构建模糊测试用例。在识别出输入向量后,紧接着就需要构建模糊测试用例。如何使用预先确定的值,如何变异已有的数据或动态生成数据,这些决策将取决于目标应用程序及其数据格式。不管选择了何种方法,在这个过程中都应该尽量自动化实现。

4)监视执行并过滤异常。在得到测试用例后,就可以在特定的监视环境中执行目标程序,并对测试过程中出现的异常情况进行过滤,即仅记录预先指定的(通常是最有可能暴露漏洞的)异常情况。该步骤可以在前一个阶段结束之后启动,也可以和前一个阶段形成一个反馈回路(图4.15属于该方法)。

5)确定可用性。在识别漏洞之后,根据审核的目标不同,还可能需要确定所发现的漏洞是否可被进一步利用。一般情况下这是一个人工确认的过程,需要具备安全领域的相关专业知识,因此执行这一步的人员可能不是最初进行模糊测试的人员。

不管采用什么类型的模糊测试,或者出于研究者的特殊目的,可能各个阶段的顺序和侧重点会有所不同,上述各阶段都应该被认真考虑。

(3)举例说明:文件Fuzz

文件模糊测试(如图4.16所示)是一种针对特定目标应用的特殊模糊测试方法,这些目标应用通常是客户端应用。其中的例子包括媒体播放器、Web浏览器以及Office办公套件。然而,目标应用也可以是服务器程序,如防病毒网关扫描器、垃圾邮件过滤器以及常用的邮件服务程序。文件模糊测试的最终目标是发现应用程序解析特定类型文件的缺陷。

图4.16 支持文件和网络协议的模糊测试框架

文件模糊测试与类型的模糊测试是不同的,因为它通常是在一个主机上完整地执行测试过程。当执行Web应用或网络协议模糊测试时,通常至少会使用两个系统,即一个目标系统和一个模糊器所运行的系统。通过在一个单独的机器上进行模糊测试来提高性能,这使得文件模糊测试成为一种很有吸引力的漏洞发现方法。

对于基于网络的模糊测试而言,在许多情况下,服务器将关闭或者立即崩溃,并且将不能够再连接。而对于文件模糊测试而言,模糊器将会继续重新开始运行并且结束目标应用,因此如果不使用适当的监视机制,那么模糊器将不可能识别出一个有效的漏洞触发。这是文件模糊测试比网络模糊测试更加复杂的一个重要原因。对于文件模糊测试而言,模糊器通常必须要监视目标应用的每次执行以发现异常,这通常可以通过使用一个调试库来动态地监视目标应用中已处理和未处理的异常来实现,同时要将结果记为日志以便于后续分析。

文件模糊测试使用的测试用例可以通过生成和变异两种方法来构造,并且这两种方法在实际应用中都很有效。虽然基于变异方法的模糊测试更加易于实现,而基于生成方法的模糊测试需要花费更多的时间来实现,但后者可能会发现前者所不能发现的一些漏洞。

本实例演示通过“文件模糊测试插件”对pict文件进行模糊测试,并重现Apple视频播放器(Quick Time)中一个已公开漏洞(CNNVD-201108-266)的简要过程。该实例阐述了文件模糊测试的主要流程。

1)漏洞介绍

AppleQuick Time在处理特定pict文件的实现上存在栈缓冲区溢出漏洞,编号为:CNNVD-201108-266。远程攻击者可利用此漏洞以当前用户权限执行任意代码或造成拒绝服务。

此漏洞源于Quick Time处理pnsize操作码的流程中将pnsize后两个字节(有符号16位值)符号扩展为4字节。此值后续用作从文件复制到桟的内存复制函数的size参数,结果导致允许远程代码执行的栈缓冲区溢出。下面代码是Quick Time存在的栈缓冲区溢出漏洞细节。

注:[esp+124h+var_10C]为pnsize后两个字节值,对其进行有符号4字节扩展,如果其是负数,扩展后仍为负数。

注:a3为前述eax,当包含a3的表达式用作memcpy的第3个参数时,可能导致复制数量过大从而产生栈溢出。v3指向为局部变量分配的小内存空间,v7值过大会导致栈溢出,且v4为可控的文件内容。

2)Fuzz测试流程

①准备测试用例。在进行文件模糊测试时首先要准备测试用例文件,可以通过生成和变异构建。其中,文件读取模块包括“overwrite”和“replace”两种原始数据变异模式。“overwrite”对指定位置使用畸形数据直接覆盖正常数据,不改变文件的大小。畸形数据包括“随机数”、“经验值”和“穷举”等。“随机数”产生器可以设置产生随机数的次数;“经验值”产生器可以自己设定经验值,默认的经验值已经包括了常见的边界经验值等;“穷举”产生器将枚举所有可能值,如一个字节将穷举产生0x00~0x FF共256个畸形数据。上述畸形数据产生规则可以组合使用,通用模糊测试框架将按顺序选取产生规则构建畸形数据。“replace”通过搜索特征串并进行替换,可以改变文件大小,支持正则表达式。用户可以有针对性地进行数据替换,进行更“精确”的模糊测试数据构造。

测试用例也可以根据文件格式自动生成,通用模糊测试框架支持python等多种语言编写模糊测试filter插件(用户可以实现数据生成规则)生成测试用例,进而利用通用模糊测试框架进行模糊测试。

本实例通过对原始样本fuzz.mov进行“overwrite”变异产生测试用例,使用默认经验值数据“ff”就能触发漏洞。具体地,首先读取原始文件,然后逐字节变异产生模糊测试用例,逐字节变异每次顺序选取一个字节进行变异,本实例采用“经验值”畸形数据产生规则,将选取字节依次替换为如下经验值:0x00、Ox5A、0x80、0x A5、Ox FF。

逐字节变异模糊测试是一种盲目的模糊测试技术,其弊端是会产生大量的无效测试用例,难以发现复杂文件格式深层的逻辑漏洞,因此智能模糊测试技术便应运而生。智能模糊测试需要根据文件格式解析原始样本,对特定元素依其数据类型产生畸形测试用例。

②部署目标应用并指示其加载测试用例。在测试用例生成后,需要配置打开测试用例的待测程序。本实例配置要测试的媒体播放器为Quick Time,设置好测试任务名称、程序、程序路径、程序参数等。

然后,使用多种条件对某些特定的异常信息进行过滤(即不记录这些异常信息到最终的结果中),可以指定“异常号码”、“异常地址”、“进程名”、“异常模块”、“SecondChance”、“异常处理状态”、“寄存器值”等过滤条件。例如,0xE06D7363异常号码,其被用于描述任何由MicroSoft Visual C++编译器通过调用“throw”而产生的错误。

最后,通过运行时间超时等方式来决定测试目标程序是否终止运行,对于长时间未终止的进程可以根据超时主动杀死;对于弹出对话框,可以通过查找窗口句柄的方式进行关闭。这样整个测试过程中出现的交互操作就无须人工干预,能够自动完成所有的测试任务。

上述准备工作完成后,模糊测试框架对pict文件进行模糊测试的任务设置已经完成,启动任务将运行Quick Time播放器打开测试用例文件,为了监控Quick Time运行时产生的异常,测试框架将以调试方式创建Quick Time播放器进程。

③监控异常。当有异常发生时,通过调试器记录堆栈、寄存器等信息,保存触发异常的测试用例,如表4.1所示。

表4.1 文件模糊测试异常记录

通过异常指令可以初步判断存在内存复制异常,此类异常出现漏洞的概率较大,还可以使用windbg插件msec.dll辅助判断异常是否存在可以被利用的漏洞。选定一条异常,利用模糊测试框架的“回归测试”功能产生异常的mov文件,然后运行Quick Time,将其附加到调试器,使用Quick Time打开产生异常的mov文件,异常发生后通过上下文环境分析产生异常的原因,进一步追溯产生异常的数据来源,最终确定漏洞产生原因。

(4)典型工具:Peach

1)Peach基本概念

Michael Eddington等人开发的Peach是一个遵守MIT开源许可证的模糊测试框架,最初采用Python语言编写,发布于2004年,第二版于2007年发布,最新的第三版使用C#重写了整个框架。

同其他可用的模糊测试框架相比,Peach是最为灵活的一个框架,并且在最大程度上促进了代码的重用。Peach框架允许研究者关注于一个给定对象的单独的子部分,然后再将它们结合在一起创建一个完整的模糊器。这种开发模糊器的方法,尽管在开发速度上可能不如基于模块的方法快捷,但是它能够促进在任意模糊测试框架中的代码重用。例如,如果已经开发了一个gzip转换器以测试某个反病毒解决方案,那么稍后就可以将其用于测试某个HTTP服务器对压缩数据的处理能力。这就是Peach的优势:你使用它越多,那么它就会变得越智能。

另外,通过利用现有的一些接口,例如微软的COM或者.NET包,Peach可以直接对ActiveX控件和托管代码实施模糊测试。目前也有直接使用Peach对微软的Windows DLL进行模糊测试的例子,同时也可以将Peach嵌入到C/C++代码中以生成被操纵的客户端和服务器。

Peach目前仍处于动态发展之中,虽然在理论上来说是非常先进的,但是不幸的是它没有完整的文档且没有被广泛应用。相关参考资料的缺乏导致了其学习过程比较困难,这可能会阻碍该框架的广泛应用。但是Peach的开发者提出了一些新颖的思想,并且创建了一个坚实的可供扩展的基础。

2)Peach架构组成

Peach的主要组件包括初始化、引擎、解析器、状态机、状态、操作、代理、监视器、变异策略、变异器等。其执行过程可以简单描述如图4.17所示。

图4.17 Peach组成及其执行过程

首先,初始化Peach,包括解析命令行选项并检测Peach依赖的软件是否被正确地安装,然后启动Peach引擎。Peach引擎使用Peach解析器解析输入配置文件,根据配置文件的指示创建相应的组件并进行附加的初始化,然后Peach引擎进入执行测试案例的主循环。

在每个测试案例中,Peach运行一个确定性有穷状态机,其状态基于用户配置确定,且其中的一个状态需要标记为初始状态。每个状态包含一个或多个操作。当状态机进入一个状态时,会顺序地执行每个操作(用户可以为每个操作指定其执行的特定条件)。Peach可用的操作类型包括:连接到远程主机(connect)、接受连接(accept)、发送数据(output)、接收数据(input)、调用特定的方法(call)、改变状态(changeState)等。如果一个状态中的所有操作执行后并不改变状态,则状态机执行结束。

每个输出操作需要一个称为数据模型的模板来表示需要发送的数据的结构。当Peach执行output操作时,首先是在特定的模板上执行变异,然后将模板上所有的值串联起来作为一个发送数据。变异由变异策略驱动执行,变异策略使用其内部逻辑从数据模型中选择元素,并在这些元素上执行变异器。变异器为元素提供代替原始值的变异值。

在整个过程中,Peach与Peach代理交互来维持对被测程序的控制,并接收关于被测程序当前状态的信息。用户需要为Peach代理指定一个Peach监视器处理(启动/停止)和监视被测的程序。每次迭代后,Peach请求代理检测是否有错误(典型的错误是程序崩溃)出现。如果Peach接收到一个肯定的回答,如程序崩溃,其将请求Peach代理发送与错误相关的可见信息。为了满足Peach的要求,Peach监视器需要实现特定的获取这些信息的方法。

Peach框架中最后一个组件是Peach结构。这些结构包括Peach字符串、Peach数字等类型,这些类型一般都包括一些有用的方法使得Peach代码更简洁。

Peach框架还提供了一些基本的构件以创建新的模糊器,包括:生成器、转换器、发行器以及群组。

①生成器。生成器负责生成从简单的字符串到复杂的分层的二进制消息范围内的数据。可以将生成器串接起来以简化复杂数据类型的生成。将数据生成抽象到自己的对象中就可以很容易地在所实现的模糊器中进行代码重用。例如,在分析SMTP服务器的过程中开发的一个邮件地址生成器,显而易见是可以在需要生成邮件地址的模糊器中重用的。

②转换器。转换器以一种特定方式来改变数据。常见的转换器可能包括:base64编码器、gzip编码器以及URL编码器等。转换器可以被串接起来使用,也可以将其绑定到一个生成器。例如,生成的邮件地址可以通过一个URL编码器来进行传递,然后再通过一个gzip转换器传递。将数据转换抽象到自己的对象中就可以很容易地在所实现的模糊器中进行代码重用。一旦一个给定的转换被完成,那么很明显它也可以被所有以后开发的模糊器重用。

③发行器。发行器通过一个协议实现了针对所生成数据的一种传输形式。常见的发行器包括文件发行器和TCP发行器。同样,将此概念抽象到自己的对象中也促进了代码的重用。发行器可供开发人员使用,使用人员根据需要将自己创建的生成器连接到不同的输出通道,Peach对发行器的最终期望是可以为任意的生成器提供透明的接口。例如,由于GIF图像通常会嵌入到一个Word或者Web表单中,假定你创建了一个GIF图像生成器,那么通过使用一个特定的发行器,就可以将生成的GIF图像发布到一个文件或者传递到一个Web表单中。

④群组。群组包含一个或者多个生成器,它是对生成器可以产生的值进行遍历的一种机制。Peach包含一些常用的群组实现。

2.动态污点分析

(1)基本概念

污点分析技术最早由Dorothy E.Denning于1976年提出,他的主要原理是将来自于网络、文件等非信任渠道的数据标记为“被污染的”,则作用在这些数据上的一系列算术和逻辑操作而新生成的数据也会继承源数据的“被污染的”属性。通过对数据属性进行分析,便能够得出程序的某些特性。根据污点分析时是否运行程序,可以将其分为静态污点分析和动态污点分析。

静态污点分析技术能够快速定位污点在程序中的所有出现情况,但是其缺点是精度较低,因此需要人工对分析结果进行复查确认。二进制静态污点分析技术主要是指利用IDA&Hex-Rays反汇编与反编译框架对二进制代码反汇编与反编译的基础上使用静态污点分析技术。使用静态污点分析技术的代表性工具主要有LCLint、Satumra等。

动态污点分析技术是近年来逐渐流行的另一种污点分析技术。该分析技术是在程序运行的基础上,对数据流或控制流进行监控,从而实现对数据在内存中的显式传播、数据误用等进行跟踪和检测。动态污点分析技术可以从流和使用范围两个方面来分类。

根据流的不同,动态污点分析技术可以分为基于数据流的分析技术和基于控制流的分析技术。基于数据流的动态污点分析技术,主要是通过标记来自外部的污点数据并跟踪数据在内存中显式传播的方法,来检测程序执行的特征,主要的代表工具有TaintCheck和Flayer等。基于控制流分析的动态污点分析技术是对数据流分析的补充。在外部污点数据标记、数据显式传播跟踪、数据误用检测等操作的基础上,通过分析控制流建立程序的控制流图(Control Flow Graphics,CFG),并设计特定的算法实现对隐式信息流传播过程的监控和分析。使用基于控制流分析的动态污点分析技术的代表性工具主要有Dytan等。

根据使用范围的不同,动态污点分析技术可以分为用户进程级的动态污点分析和全系统级的动态污点分析。用户级的污点分析主要是追踪并监视进程中的数据使用和程序执行流,代表工具有Taint Check、flayer和Vigliame。全系统级的污点分析不同于用户级的污点跟踪,它还需要跟踪操作系统内核,进行系统级的监视。对于这方面的研究,主要有基于软件和基于硬件的全系统跟踪工具。基于软件的动态污点分析大多建立在虚拟机之上,是对虚拟机的一个扩展,通过一一对应的位图映射来标识相应的物理内存和寄存器是否被污染,每个字节的内存和每个寄存器用一个位或者一个字节(两种实现都可以)来标志其是否干净,代表性工具主要有Argos和Bitblaze等。基于软件的工具的缺点就是效率较低,为了提高运行的效率,研究人员设计了基于硬件的动态污点分析工具。基于硬件的分析工具的目的是对软件级的某些功能提供硬件支持,从而提高程序运行和分析的效率,代表性的工具主要有Minos等。

(2)基本原理

进行污点分析首先需要定义一个污染源(Source点),即污染数据的来源,也就是引入外部数据的代码。污染数据通常由用户输入、文件或者网络引入。能够引入外部数据的污染源函数有许多,如read()、fscanf()、get Parameter()等。通过污染源可以确定污染数据何时被引入到程序中。其次还需要定义一个污染触发点(Sink点),即可能触发潜在安全问题的危险代码。如果只引入了污染数据但是没有被触发,那么这样的污染数据是无法构成威胁的。常见的Sink函数一般都是一些系统函数,如命令执行、SQL操作、strcpy等。在Source点和Sink点之间,污染数据的传播分析即是简单的数据流分析过程,其通过变量之间的相互赋值进行传播。为了确定引入的污染数据是外部可控的,还需要进行污染净化分析,通过收集输入验证等约束,进行简单的约束求解,以移除外部不可控的污染数据。动态污点分析技术和静态污点分析技术的基本原理是相同的,唯一区别在于静态污点分析技术在检测时并不真正运行程序,而是通过模拟程序的执行过程来传播污点标记;而动态污点分析技术需要运行程序,同时实时传播并检测污点标记。

如图4.18所示是为对命令行注入漏洞进行污点分析的实例。在实例中外部的污染数据从①中的fgets()函数引入,存在数据结构,图中②表示数据流分析对buf的跟踪,在③处被污染的数据结构buf通过strcpy()函数传递给other这一数据结构,导致other被污染。图中④表示数据流分析对other的跟踪,在⑤的位置处由于system()的参数other已被污染,因此system()的参数是外部可控的,将导致潜在的危险,分析程序将此分析结果记录并报告给用户。

图4.18 污点分析算法实例

(3)举例说明

本节介绍一个使用动态污点分析的方法检测缓冲区溢出漏洞的典型实例。图4.19实现代码中函数的一个字符串复制函数strncpy(),该函数负责将源数组的内容复制到目标数组。因为目标数组是放置在栈上的临时变量,且事先规定了大小,因此在没有对源数组长度进行有效判断的情况下将其复制给目标数组可能存在缓冲区溢出风险。本实例就针对该代码中的缺陷,进行动态污点分析,以期挖掘出该缓冲区溢出漏洞。

程序的main()函数中使用gets(source)代码接受键盘输入的字符串,并在该字符串长度小于20个字节时进入fun()函数,将字符串复制到一个本地变量temp数组中。由于temp数组的大小只有15个字节,所以在复制时,可能出现缓冲区溢出。例如,从键盘输入19个字节的Source字符串(字符串结束符‘\\0’亦需要占用一个字节),则可以成功运行fun()函数,将其复制到temp数组中,溢出了4个字节。程序中接受外部输入字符串语句的二进制代码如图4.20代码所示。

程序中fun()函数中复制语句(1)(图4.19)的二进制代码如图4.20所示。

图4.19 缓冲区溢出示例程序

图4.20 接受外部输入字符串语句的二进制代码

使用动态污点分析技术对该程序进行分析,首先需要确定程序攻击面。在介绍动态污点的基本原理时,已经给出了程序攻击面的定义,即程序接受输入数据的接口集,一般由程序的入口点和外部函数调用组成。在扫描该程序的二进制代码时,首先能够扫描到图4.20中的语句(3)处的gets()函数。很显然,该函数是一个外部数据引入函数,其能够接受用户从键盘输入的一串字符串,因此,该函数符合程序攻击面的定义,是输入设备的输入。

在确定程序攻击面之后,分析污染源数据并进行污点标记。二进制代码语句(3)处的gets()函数(图4.21)调用对应于源码中语句(2)处的get(source)代码(图4.19),该数组Source的内容由外部输入。因为一般把外部输入的内容都认为是不可靠的数据,所以需要为该数组做污染标记。在二进制代码分析中,可以根据程序攻击面中函数参数信息的格式,确定该函数的参数个数和类型以及返回值的类型。在本实例中,gets()函数只接受一个字符数组的起始地址为参数,并返回输入的字符串的起始地址。因此从图4.20中的语句(4)处,查看到该语句将gets()返回值地址放到eax寄存器中,则此时eax寄存器的内容被标记为“污染的”。

程序继续运行时,该污染标记会伴随着该值的传播而一直传递。在进入fun()函数时,该污染标记通过形参实参的映射传递到参数str上,然后运行到Sink点函数strncpy()。该函数的第二个参数即是str。在为strncpy()函数压入参数时,图4.21所示语句(5)处将eax寄存器中的str地址压入栈上,此时污染标记会传递到栈上该地址处。最后在运行strncpy()函数时,若设定了相应的违背规则,则该规则会被触发,报出缓冲区溢出漏洞。

图4.21 复制语句的二进制代码

使用动态污点技术对程序进行分析,可以检测出程序中的缓冲区溢出漏洞。假设该程序编译后的可执行程序命名为Sample.exe,从函数main()开始启动插粧,直到程序运行结束,表4.2是实验测试的结果数据。

表4.2 动态分析实例程序的结果

表4.2中的4个输入均能准确地定位程序中的strncpy()危险调用点,并给出污染的来源由gets()函数引入。由gets()函数引入Userlnput Data值,经函数调用,污染值传递到函数fun()中的strncpy()系统函数调用点,导致危险。插桩指令数在不同输入时有略微变化,而分析指令数在不同输入下变化较大,输入增长,插桩指令数变化不大,而分析指令数增加较多,说明存在部分代码被多次执行。

如图4.22所示是输入三次不同长度字符串时程序的正常运行结果;图4.23是程序在输入一个18个字符长度的串“aaaaabbbbbcccccddd”后产生的异常结果。

图4.22 缓冲区溢出示例程序正常运行结果

该缓冲区溢出漏洞的问题存在于strncpy()复制函数的源数组是由用户从外部输入,因此该数据是用户可控的,同时由于目标数组可能会小于源数组的大小,因此复制字符串时可能产生溢出。普通的超过目标数组长度的字符串会导致程序的异常,如图4.23所示,由攻击者精心构造的字符串可以覆盖fun()函数的返回地址,从而改变其控制流。当攻击者能够成功地在内存中加载shellcode,控制fun()函数返回到shellcode处,则可以成功运行攻击。

图4.23 缓冲区溢出异常运行结果

(4)典型工具

TaintDroid是Intel实验室、宾夕法尼亚州立大学和杜克大学的研究人员共同研究发布针对安卓应用的动态监测软件。目前该工具已经可以支持Android 4.3版本,并且需要在安卓系统下运行。该检测工具重点在于保护用户隐私,防止用户信息泄露。在研究人员自测的30款流行应用中可以找到半数的应用有自动发送用户信息的行为。

TaintDroid工具的结构如图4.24所示。

TaintDroid采用动态污点分析技术,针对被检测应用的数据流进行跟踪分析。其核心思想为:将被检测应用中使用到的敏感数据加上污染标签,在数据流传播过程中将所影响到的数据(按一定的规则)加上污染标签,在应用程序产生向外发送污染数据的行为时报告该危险行为。该检测软件启动后,在DVM中保持着一个记录污染标签的虚拟污染映射表。

从图4.24中可以得出TaintDroid工具的整个结构和运行过程。首先,在(1)处某个信息成为该可信应用的污染源,该值被标记为“污染的”,并保存了相关信息;在(2)处,通过Native code调用,该污染源的值被保存在Dalvik虚拟机的Virtual Taint Map内,并传播给(3)处的值;在(4)、(5)和(6)处,通过Android提供的IPC机制将该污染值传播到另外一个Dalvik虚拟机的Virtial Taint Map内;然后该污染值继续传播,并被一个不可信的应用通过库函数(8)和(9)调用;此时到达Sink点,TaintDroid判定发生了危险行为。

图4.24 TaintDroid的结构图

该工具在数据流跟踪上采用4种不同程度的层次进行分析:①变量级,根据JVM提供的变量语义和上下文情景来判别变量是否受到污染;②函数级,针对系统库函数进行污染传播分析,分为本地库函数和JNI函数;③消息级,针对应用之间的消息通信进行污染传播分析;④文件级,针对文件进行污染传播分析。

在数据流分析中涉及的数据主要有5种,分别是:函数本地变量、函数参数、类静态字段、类实例和数组。

该工具的优点是使用动态污点分析技术,在记录变量污染标签时,采用将栈扩大一倍并在栈上将变量和污染标签相邻存储的方法,方法简单易行。它的缺点是该动态分析只检测了数据流,并未对程序的控制流进行分析,使得漏报率较大。