NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

摘要:覆盖率指导的模糊测试是发现漏洞的最流行,最有效的技术之一,因为它具有高速和可扩展性。但是,现有技术通常集中于代码覆盖范围,而不是易受攻击的代码。这些技术旨在覆盖尽可能多的路径,而不是探索更容易受到攻击的路径。在选择要测试的测试用例时,现有的模糊测试通常会平等对待所有测试用例输入,而忽略了不同测试用例输入所执行的代码路径并非同等易受攻击(漏洞)的事实。这导致浪费时间测试无关紧要的路径而不是易受攻击的路径,从而降低了漏洞检测的效率。在本文中,旨在模糊测试中使用深度神经网对测试用例进行选择,以减轻上述限制。特别是,利用深度神经网络LSTM从大量有漏洞的和没有漏洞的程序路径中学习隐藏的易受攻击模式,训练一个预测模型来分类路径是否存在漏洞。然后,模糊器对能够覆盖可能是易受攻击的程序路径的测试用例进行优先排序,并为这些测试用例分配更多的变异方法产生更多的变异输入(mutation energy)(即,要生成的输入数量)。我们基于现有的模糊器PTfuzz实现了NeuFuzz的原型,并在两个不同的测试套件上对其进行了评估:LAVA-M和九个实际应用程序。实验结果表明,NeuFuzz可以在更短的时间内找到比现有Fuzzer更多的漏洞。我们在这些应用中发现了28个新的安全漏洞,其中21个已经被分配为CVE IDs。

一:介绍

程序中隐藏的安全漏洞可能导致系统受损,信息泄漏或服务被拒绝。正如我们从许多最近备受关注的漏洞利用中所看到的,例如Heartbleed攻击[1]和WannaCry勒索软件[2],这些漏洞通常会在经济和社会上造成灾难性的影响。因此,程序中的安全漏洞必须在被攻击者发现之前加以识别,并予以消除以避免潜在的攻击。自1990年代初引入[3]以来,模糊测试已成为发现商业软件中的漏洞,错误或崩溃的最有效,可扩展的测试技术之一。它也已被主流软件公司广泛使用,例如Google [4],和Microsoft [5],以确保其软件产品的质量。

模糊测试的关键思想是为被测程序(PUT)提供大量精心设计的输入,以触发意外的程序行为,例如崩溃或挂起。基于对PUT内部结构知识的了解,模糊器可以分为白盒,黑盒和灰盒。白盒模糊测试工具(例如[6],[7])通常可以访问PUT的源代码或中间表示。他们通常使用繁重的程序分析方法(例如符号执行和路径遍历)来指导模糊测试,因此存在可伸缩性(以更大的规模来做现在所做的事)问题。黑盒模糊器(例如[8],[9])完全不了解程序的内部结构,通常会盲目执行随机测试,因此效率极低。灰盒模糊器(例如[10][11][12])旨在做出妥协,采用轻量级程序分析方法(例如,仪器)从程序中获取反馈以指导模糊,这通常比黑盒模糊器更有效,比白盒模糊器更具可扩展性。

近年来,使用代码覆盖率作为指导来进行模糊测试的覆盖率指导的灰盒模糊测试(CGF)已成为最成功的漏洞发现解决方案之一。这种类型的模糊器逐渐增加了代码覆盖范围,并放大了发现漏洞的可能性。最先进的灰盒模糊器AFL[10],libFuzzer [11]和honggfuzz [12]引起了业界和学术界的关注,并发现了数百个引人注目的漏洞[13]。(通常,这些方法对PUT进行轻量级分析,为每个执行的测试用例提取覆盖率信息。如果测试用例使用新分支,则该用例被标记为引起注意的;否则,该案例将被标记为无用。)在以下模糊测试迭代中,引起注意的测试用例,进行变异并生成新的测试用例以进行模糊测试。

尽管在漏洞发现方面取得了相当大的成功,但我们发现CGF模糊测试(例如AFL)具有一个关键限制。当前的模糊测试旨在覆盖尽可能多的路径,而不是探索更容易存在漏洞的路径。他们按照添加顺序从测试用例集中选择测试用例,并且测试能量( testing energy)平均分配给所有程序路径。但是,不同的路径具有不同的漏洞存在(可能可以换个词)概率。如[14]中所述,程序中的漏洞分布通常是不平衡的,即大约80%的错误位于大约20%的程序代码中。结果,现有的CGF模糊测试器浪费了相当多的时间来测试不易存在漏洞的路径,从而降低了模糊测试的效率。直观地讲,测试用例执行更容易出现漏洞的程序路径时,更有可能触发漏洞,因此我们应该在这些路径上花费大部分的测试工作,以增加在测试期间触发漏洞的可能性。

在本文中,我们提出了一种新颖的路径敏感的灰盒模糊解决方案NeuFuzz,以减轻上述限制。它采用了一种新的测试用例选择策略,对那些更有可能存在漏洞的路径的测试用例进行优先排序,并相应地对这些测试用例分配更高的测试能量(如要生成的输入数量),以提高漏洞检测的效率。核心挑战是如何确定路径是否容易存在漏洞。受图像和语音识别取得巨大成功的启发,我们使用深度神经网络来学习存在漏洞的程序路径的隐藏模式,并识别易受攻击的路径以指导测试用例的选择。特别,具体来说,(1)从大量的存在漏洞的和干净的程序路径中学习一个预测模型,然后(2.)在模糊过程中使用该预测模型来分类在执行路径中是否存在漏洞。因此,(3.)模糊器对训练由神经网络确定的漏洞路径的测试用例进行优先排序,并分配更多的变异能量给这些测试用例,以最大限度地提高错误发现效率。

NeuFuzz基于深度神经网络的测试用例选择比最先进的模糊器更有效。我们基于AFL的特定扩展(即PTfuzz [15])实现了NeuFuzz的原型。我们在两个不同的测试套件上评估了NeuFuzz。第一个是LAVA-M [16]基准测试,其中包含四个带有手动注入的漏洞的Linux程序。另一个是一组具有不同复杂性和功能的实际应用程序(即,libtiff,binutils,libav,podofo,bento4,libsndfile,auditfile,nasm),这些应用程序经常被某些广泛使用的程序用作第三方库。实验结果表明,NeuFuzz可以在更短的时间内找到比PTfuzz和QAFL [17]更多的漏洞。

总而言之,我们做出以下贡献:

1.我们提出了一种新的测试用例选择策略,该策略优先考虑使用存在漏洞路径的测试用例以提高漏洞发现的效率。

2.我们提出了一种新颖的深度神经网络解决方案,以预测哪些程序路径易受攻击(存在漏洞)。

3. 我们实现了NeuFuzz原型来模糊二进制程序,并使用精心制作的基准测试和实际应用程序对其进行评估,从而表明该解决方案是有效的。

4. 在一些广泛使用的实际应用程序中,我们发现了21个CVE和7个未知的安全漏洞,并有助于提高供应商产品的安全性。

本文的其余部分安排如下。第2节介绍了背景和相关工作。第3节介绍NeuFuzz的概述。第4节详细介绍了NeuFuzz的技术细节。第5节介绍了NeuFuzz的实现和实验评估结果。第六部分总结了论文。

二:背景及相关工作

最近的研究已经实施了许多增强技术,例如程序分析和机器学习,以从不同方面(包括覆盖范围跟踪,测试用例选择和测试用例变异)提高CGF的效率和有效性。

A.模糊化硬件反馈(Hardware Feedback)

PTfuzz是具有硬件反馈的灰盒模糊器,也是基于AFL的。它使用CPU硬件机制Intel PT(Intel Process Tracer)[18]来收集程序分支信息,而不是编译时检测(例如,AFL的gcc模式使用的一种)或运行时检测(例如,AFL的QEMU模式使用的一种,表示为QAFL)。Intel PT是Intel架构的扩展,能够以最小的性能开销准确地跟踪程序控制流信息。因此,与QAFL相比,模糊器不依赖源代码,并且可以获得更高的性能,而不会丢失覆盖范围跟踪精度。KAFL最近的另一项研究[19]它还基于AFL和Intel PT实现,它们可以支持ring0中的内核模糊测试。我们的原型基于PTfuzz实现,因为我们的测试目标是ring3中的二进制程序。

B.测试用例选择策略

(以前的方法)测试用例选择对于模糊测试至关重要。好的测试用例选择策略可以提高路径遍历和漏洞检测的能力。AFL采用一种简单的种子选择策略,偏爱更小和更快的种子,为了在给定的时间内生成和测试更多的测试用例。Rawat等。 [20]优先考虑使用更深路径的种子,不优先考虑使用错误处理块(error-handling blocks)和高频路径(high-frequency)的种子,因此可以测试难以到达的路径,并且可以避免无用的错误处理路径。AFLFast [21]优先考虑行使低频路径并被较少选择的种子,因此很可能可以对冷路径进行彻底测试,并且在热路径上浪费的能量更少。AFLGo文献[22]对接近预定目标位置的种子进行优先排序,从而实现定向模糊的目的。CollAFL [23]使用三种新的种子选择策略来将模糊器直接驱动到未探索的路径,并更快地提高代码覆盖率。Angora [24]优先考虑其路径包含带有未探索分支的条件语句的种子,这使得在探索高频路径之后可以专注于低频路径。

 现有的测试用例选择策略主要集中在执行速度,路径频率,路径深度和未遍历的路径分支上。从本质上讲,它们专注于代码覆盖范围,而不是指导模糊器测试易于出现错误的路径或代码。结果,当前的测试用例选择策略浪费了相当多的时间来测试不易存在漏洞的路径,从而降低了模糊测试的效率。我们在本文中着重解决这个问题。

C.测试用例变异策略

AFL将输入视为连续的二进制字节序列,因为无法感知输入的结构。这种随机突变策略可以完全依靠运气找到新的路径或分支。为此,研究人员根据各种程序分析方法提出了许多改进措施。斯蒂芬斯等。 [25]结合了模糊测试和符号执行的优势来检测漏洞。具体地说,当模糊器卡在某些复杂的比较分支(例如,魔术字节)中时,使用condicolic执行来生成测试输入,该输入可以使执行通过检查,但仅限于符号执行的可伸缩性差,因此,模糊器很难应用于实际应用。Li等。 [26]使用轻量级运行时检测方法来监视比较指令或功能,并根据进度反馈信息引导变异,以生成可通过分支的输入字节。T-fuzz [27]通过直接修改二进制程序来删除一些复杂的分支检查(例如,魔术字节,校验和和哈希)。发现崩溃后,将使用符号执行技术来消除误报并在原始程序中重现错误。Rawat等。 [20]使用静态分析来推断出有趣的值,然后使用污点分析(taint analysis)来确定比较指令的输入偏移量,以便这些偏移量被推断值所突变,以通过一些复杂的条件。陈和陈[24] 使用梯度下降算法而不是符号执行来指导突变以实现覆盖率改善。

我们可以看到,所有这些方法都旨在通过突破程序中的某些复杂条件检查来提高代码覆盖率。这些方法与本文介绍的方法正交,我们可以将它们集成到我们的工具中。

D.基于学习的漏洞检测

最近,研究人员已开始应用人工智能技术来辅助漏洞检测。Learn&Fuzz [28]使用神经网络来学习基于语法的模糊化的文件输入格式的生成模型。Augmented-AFL [29]使用神经网络从过去的模糊学习功能,以预测种子文件中执行突变的良好位置。Böttinger等。 [30]使用最先进的Q学习算法来优化奖励并学习为特定程序输入选择高奖励突变操作。Li等。 [31]提出了VulDeePecker,这是一种基于深度学习的漏洞检测方法,不需要手动定义漏洞功能,但是该方法需要源代码,并且可以检测仅与库函数相关的漏洞。VDiscover [32]提取程序的动态和静态API序列作为功能,并使用传统的机器学习算法来训练模型以预测未知程序中的漏洞。但是,由于API序列不能很好地表示漏洞功能,因此准确性不高。

总而言之,一方面,当前用于模糊测试的机器学习方法主要用于指导种子生成和种子突变以实现高代码覆盖率。另一方面,机器学习直接应用于漏洞预测,这本质上是一种静态分析方法,并且需要手动验证。我们的工作使用漏洞预测结果来指导模糊测试过程的测试用例选择。据我们所知,我们是第一个使用深度神经网络来学习模糊过程中测试用例输入所涵盖的漏洞路径的人。

三:总览

在本节中,我们将通过一个激励性的例子进一步说明我们需要解决的问题,并概述我们的方法。

A.激励示例

如上所述,当前模糊器的测试用例选择大大降低了漏洞检测的效率。为了清楚地说明种子选择策略如何使AFL难以在PUT中找到漏洞,请考虑图1中的代码。图1(a)是源代码,图1(b)是相应的进程间控制流程图。如果输入字符串的长度超过32个字符(以字符'2'开头),则会在代码的第9行导致堆栈溢出。请注意,此示例相对简单,为方便起见,我们在源代码中显示了该示例。实际上,我们的方法可以直接应用于二进制程序。

    NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)    图1。一个激励人心的例子。(a)源代码。(b)控制流程图。(c)模糊种子队列。

给定初始种子输入“fuzz”,AFL可以迅速找到其他三个路径,如图1(b)的控制流程图所示,假设使用这些路径的测试用例为“0aaa”,“1bbb”和“2ccc”。然后,AFL将按照每个测试用例的添加顺序对它们进行变异,如图1(c)所示。但是,种子输入0aaa,1bbb和3ddd行使的三个路径(分别由红色,黄色和黑色线表示)显然不太可能具有漏洞。因此,对它们进行模糊处理的重要性非常低,并且种子输入2ccc行使的路径(用蓝线表示)存在潜在漏洞,对模糊处理更有意义。而且,为了触发路径中的潜在漏洞,测试用例应比其他测试用例变异更多次。实际上,现实世界中的程序比本示例要复杂得多,并且通常包含更多的健全性检查和路径分支。假设一个程序执行了n(n> 1000)条路径,并且只有一条路径存在漏洞,那么我们方法的优势显而易见:

我们的想法是使用测试用例优先级策略,以确保首先测试更好的测试用例并使用更多的变异方法对其进行突变,从而提高了模糊测试仪的效率和有效性。因此,我们需要考虑并解决以下三个启发式问题。

  1. 如何推断测试用例输入是否比其他输入更好?

测试用例的重要性取决于对其执行路径的兴趣(指这条路径更容易受到攻击)程度。例如,AFLFast认为,从代码覆盖率的角度来看,触发低频路径的种子更好。但是,在本文中,如果它的执行路径更有趣(即,该路径更容易受到攻击),那么我们认为此输入更好,也更有意义,首先进行测试。显然,专注于测试可能执行(存在漏洞)的路径的测试用例将增加触发漏洞的可能性。例如,在图1所示的示例中,测试用例2ccc覆盖的路径很容易受到攻击(???存在漏洞),因此应首先对测试用例进行多次变异,并对其进行多次测试。值得注意的是,给定一个测试用例处于易受攻击的路径,无法保证此路径中的隐藏漏洞一定会因该种子的变异而触发。但是,从该种子进行突变肯定会增加触发隐藏漏洞的可能性。

  1. 如何确定一条感兴趣的路径(易受攻击的路径),也就是更容易受到攻击的路径?

我们知道示例中的蓝色路径(2ccc)中存在一个缓冲区溢出漏洞,该漏洞在没有任何安全检查的情况下调用了危险的strcpy函数。自动发现此漏洞的传统方法需要依赖功能数据库。但是,在实践中,漏洞情况比我们可以直接建模的情况要复杂得多。提取特征来表征漏洞具有挑战性;因此,我们不依赖于基于专家经验的传统漏洞功能来确定路径是否存在漏洞。取而代之的是,我们依靠深度神经网络来自动提取漏洞的特征,从大量的训练数据中学习漏洞模式以及确定其他易受攻击的漏洞路径。

  1. 如何在不失去性能优势的情况下将方法应用于现有的二进制模糊测试器?

我们可以基于学习的神经网络模型指导灰盒模糊测试的测试用例选择。因为CGF的吸引力在于它的速度,所以在实施此策略时应限制对模糊器的性能开销的引入。因此,我们已经考虑了几种支持二进制模糊测试的模糊器,包括AFL-PIN [33],Vuzzer和PTfuzz,但是前两个工具的实现依赖于动态工具Pin [34]。这可能会导致大量费用,从而违反了我们的原则。如上所述,事实证明,与QAFL相比,PTfuzz在性能上有很大提高,它依靠Intel PT支持执行控制流跟踪,这有助于在不依赖昂贵方法的情况下将程序执行路径作为模型的输入进行恢复。 例如动态检测。因此,我们选择实施基于PTfuzz的方法。

B.方法概述

为了解决上面提到的现有灰盒模糊器的局限性,我们提出了NeuFuzz,如图2所示,它由两个阶段组成,即离线模型训练和在线指导模糊,分别对应于图2(a)和图2(b)。这两个阶段都需要具有Intel PT的CPU的硬件支持。在离线模型训练阶段,输入的是大量二进制程序,其中包括易受攻击的程序和不易受攻击的程序。易受攻击的程序表示它包含一个或多个已知漏洞,而不易受攻击的程序意味着已应用补丁程序修复了该漏洞。输出是一个学习型预测模型,用于对未可见的程序路径中是否存在漏洞进行分类。在线指导的模糊测试阶段,我们将预测模型集成到模糊测试器中,通过对测试用例执行路径的漏洞预测,确定测试用例的优先级和突变方式,然后将测试用例加入测试用例集。然后,模糊器根据测试用例的优先级从测试用例集中选择测试用例输入。

NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

图2。提议的NeuFuzz方法概述。(a)离线模型训练。(b)在线指导的模糊测试。

应注意两点。首先,我们不会丢弃那些没有覆盖我们的模型所识别的易受攻击路径的测试用例。我们仍然将这些测试用例添加到测试用例集中有两个原因:一方面,从模糊器代码覆盖的角度来看,这些测试用例仍然很有价值,因为对它们进行模糊处理可以发现新路径,并且一方面我们的模型预测精度无法保证达到100%,因此即使发生的可能性低得多,我们也不能错过寻找这些漏洞的机会。其次,尽管训练神经网络需要时间,但在将其应用于所有应用程序之前,我们只需要训练一次即可。此外,我们在主模糊循环中添加了路径恢复(这是个啥?????)和漏洞预测功能,这当然会导致额外的开销,但我们的实验表明,这两个模块的开销非常低,可以接受。

四:方法

在本节中,我们将介绍基于PTfuzz的NeuFuzz的关键技术细节。我们的方法与以前的研究人员提出的提高代码覆盖率的方法正交。

A.离线模型训练

如图2(a)所示,此阶段包含四个步骤。首先,我们收集训练程序,包括易受攻击的版本和不易受攻击的版本,以进行神经模型训练。其次,概念证明proofs of concept (POC)用作这些程序的输入。借助Intel PT的控制流跟踪功能,我们可以获得两种类型的程序路径:易受攻击和干净。第三,在训练之前,将这些路径转换为向量表示形式,作为对神经网络的输入。第四,基于神经网络的特征学习能力,从大量训练数据中,建立了在线指导模糊阶段指导种子选择的预测模型(这句话不通顺)。

  1. 收集训练程序

鉴于程序的复杂性和多样性,需要大量的训练样本来训练机器学习模型,以直接从代码中有效地学习安全漏洞的模式。另外,由于我们需要动态运行程序来获取执行路径,因此我们需要一个可以执行的二进制程序和相应的测试用例。但是,据我们所知,不存在可公开获得的完整二进制程序数据集。尽管VulDeePecker提供了数据集[35],但仅包含源代码,而没有提供用于程序执行的测试用例。

我们构造了从三个数据源派生的第一个二进制数据集:NIST SARD项目[36],GitHub [37]和Exploit-DB [38]。NIST SARD项目包含许多综合程序,每个程序都有一个好程序(补丁程序)和坏程序(补丁程序),并涵盖各种类型的CWE(通用弱点枚举),我们选择诸如堆栈之类的内存损坏漏洞。溢出(CWE121),堆溢出(CWE122),整数溢出(CWE190),UAF(CWE416)等。我们最终从SARD收集了26,080个二进制程序,每个程序都有补丁程序前和补丁程序后的版本。但是,SARD中的程序是综合程序,因此它们的漏洞可能与实际应用程序中的漏洞有所不同,这可能会限制我们模型的可伸缩性。因此,我们还从GitHub和Exploit-DB收集实际应用程序(例如ImageMagick,libtiff和Bintuils)。GitHub可以跟踪对源文件的更改,并且我们可以通过检索应用程序主分支的提交历史记录并从公共存储库(例如bugtracker和GitHub问题)中检索POC来识别每个错误的补丁前和补丁后版本,以验证并保留测试用例可以触发该错误。我们最终从Github收集了560个应用程序。此外,我们从Exploit-DB收集了1039个易受攻击的程序和相应的POC。

如表1所示,我们在数据集中收集了28,475个易受攻击的二进制程序和27,436个非易受攻击的二进制程序,用于神经网络训练和测试,以及可触发易受攻击的路径和干净路径的测试用例。

表1 用于培训和测试的数据集

NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

  1. 提取执行路径并标记真实数据

利用构建的数据集和POC,我们可以通过在训练和预测方法的两个阶段中执行程序来提取程序执行路径。最常见的方法是使用Pin动态工具提取执行路径,但是这种方法会导致高开销并降低Fuzzer的执行速度,尤其是在在线指导的Fuzzing阶段。因此,我们使用Intel PT技术提取路径,这将在B部分的第一部分中详细说明。程序执行路径的每条指令均以字节码形式记录,并且我们没有在系统库函数中跟踪这些指令。并在路径跟踪过程中保留与漏洞功能相关的库函数名称(例如,strcpy和memcpy),

接下来,我们标记真实数据。VulDeePecker保守地认为,只要在代码中未发现漏洞,代码就不会受到攻击;实际上,这种假设不成立。我们假设代码在修补后是不易受攻击的。因此,相比之下,基本事实更为准确。可以使用POC作为输入执行易受攻击和不可受攻击的程序,以获得易受攻击的路径(标记为“ 1”)和干净的路径(标记为“ 0”)。最终,从我们构建的数据集中总共提取了27,820条脆弱路径和26,871条干净路径,其中4/5用于训练,其余1/5用于测试。

  1. 将执行路径转换为向量表示

在将包含指令字节码的程序执行路径作为输入到深度神经网络之前,需要将该路径转换为矢量表示,同时保留尽可能多的执行路径原始语义信息。我们可以通过文本处理方法来学习:一条路径可以看作一个句子,而路径中的每条指令都可以看作是句子中的单词。因此,我们必须执行词嵌入,为此,一词[39]和word2vec [40]是常见的方法。一个热点将第i条指令表示为向量,其ith元素设置为1,其他元素设置为0。例如,如果有5条指令,则第二条指令的矢量表示为[0,1,0, 0,0]。但是,此方法不包含任何语料库信息,并且所有单词之间的距离是相同的。Word2vec表示根据上下文的向量,具有较高相关性的单词之间的距离更近;因此,word2vec在数据的固有特性方面更具表现力。因此,我们选择word2vec进行单词嵌入。每条指令的十六进制字节码被视为令牌,例如,0×890424 代表mov [ebp],eax;然后用word2vec训练字节码序列。输出是每个指令在256维空间中的矢量表示。然后,我们可以将执行路径转换为每个指令的向量表示,如图3所示。

NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

显然,不同路径的长度不相等且变化很大。这样的路径很难馈入深度神经网络架构。因此,我们将路径的最大长度设置为n。短于n的路径从其后端开始填充0。长度大于n的路径从它们的前端被截断,因为新的触发路径分支通常位于路径的末端。当填充到固定长度的元素时X1,X2,…,Xñ(Xi是每条指令的向量表示),一条路径的输入序列可以表示为X1:n=X1⊕X2⊕...⊕Xñ,哪⊕是连接器。

  1. 训练神经网络模型

  在此步骤中,从包含大量易受攻击且干净的路径的训练数据中学习了隐藏的漏洞模式。我们选择长短期记忆(LSTM)神经网络进行训练,以输出可用于分类模糊处理过程中触发的看不见路径的模型。一方面,LSTM擅长处理顺序数据:程序路径与自然语言中的语句非常相似,并且一段代码是否易受攻击取决于上下文。另一方面,LSTM具有适合处理长依赖性的存储功能,因为与漏洞关联的代码可能位于路径中相对较长的距离处。

  我们基于LSTM的神经网络总共包括4个层,如图4所示。第一层是嵌入层,它将序列中的所有元素映射到固定维向量。第二层和第三层是堆叠的LSTM层,每个层包含双向形式的64个LSTM单元,并且堆叠的LSTM模型可以学习更高级别的时域特征表示。最后,我们使用具有单个神经元和S形激活函数的密集输出层来对任务中的两个类别进行预测。我们的损失函数是binary_crossentropy,优化器是adam,并且由于LSTM经常遭受过度拟合的困扰,因此我们使用dropout来解决此问题。当dropout  rate设置为0.6时,我们的模型可获得最佳结果。

  NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

               图4。神经网络架构。

B.在线指导的模糊测试

  在线指导的模糊测试是基于PTfuzz的主要模糊测试循环实现的。当模糊器生成一个新输入并达到新的分支覆盖率时,它将被添加到测试用例集中。为了实现我们的测试用例优先级策略,我们在添加测试用例到测试用例集之前添加了执行路径恢复和漏洞预测模块,如图2(b)中的灰色框所示。我们可以根据Intel PT捕获的控制流信息和目标二进制文件来恢复程序的完整执行路径。然后,我们使用预测模型来确定一条看不见的道路是否易受攻击。根据预测结果标记测试用例(将易受攻击的测试用例标记为“ 1”,将不易受攻击的测试用例标记为“ 0”),然后将其添加到测试用例集中。最后,在下一个测试用例选择过程和测试用例变异过程中,将优先考虑易受攻击的测试用例并分配更多的变异方法。

  1. 恢复执行路径

当使用PTfuzz进行模糊测试时,执行控制流信息以数据包的形式实时收集,如表2所示(Intel PT将可以更改程序流的指令称为流程更改指令(COFI)并存储在指定的内存空间。通过解码这些数据分组来更新表示代码分支覆盖范围信息的位图。但是,在此过程中,模型输入并不需要完整的执行路径信息。因此,在使用我们的预测模型之前,我们必须通过对存储在特定内存中的跟踪进行解码,来基于控制流包和目标程序二进制文件恢复执行路径。

NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

表2 PT数据包类型说明

算法1详细描述该过程。具体来说,首先,加载二进制程序,并从主入口函数(第1,2行)获得指令地址。然后,根据当前指令类型获得下一条指令,并将其字节码添加到路径中,直到跟踪的最后一条指令为止(第4-8行)。如果指令是有条件的跳转,则跳转方向是根据TNT包确定的(第11-18行)。如果指令是间接跳转或转移,则根据TIP数据包获得跳转目标地址(第19-21行)。如果指令是无条件的直接跳转,则从指令中获得跳转目标地址(第22,23行)。如果该指令不是跳转指令,则根据当前指令的大小获得下一条指令(第24,25行)。[41]由Intel提供。

算法1程序执行路径恢复

NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

  1. 用神经网络模型指导模糊

我们的最终目标是利用学习的模型来指导模糊测试器对执行易攻击路径的测试用例进行优先级排序,以尽早暴露出该漏洞,并为它们分配更多的突变能量,而为测试用例执行不易受攻击的路径分配更少的能量,以增加触发漏洞的可能性。因此,我们需要修改PTfuzz的原始算法,以将经过训练的模型集成到模糊器。

NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

算法2显示了我们的NeuFuzz实现,其中的灰色框表示我们的方法与PTfuzz的算法之间的差异。具体地说,当一个变化的输入触发一个新的分支时(第14行),首先根据PT捕获的控制流信息和二进制程序文件(第15行)恢复执行路径。然后,使用该模型对路径是否易受攻击进行分类,并根据预测结果标记种子并将其添加到队列中(第16-20行)。从队列中获取种子时,第一步是检查是否有标记为易受害的种子。如果存在易受伤害的种子,则将其优先处理(第5-9行)。我们执行的AssignEnergy(第10行)将最多的突变能量(PTfuzz的默认最大值为1600)分配给易受伤害的种子,将更少的能量(原始分配能量的50%)分配给易受害的种子,等式(3) ,VUL(小号) 如果种子被标记为易受攻击,则返回true。公式(2)表示PTfuzz的原始能量计算方法,它使用执行时间Fš p È Ë d ,阻止过渡覆盖 FÇ Ò v ,创作时间 FŤ我中号Ë ,以及种子的路径深度 FdÈ p吨ħ ,如公式(1)所示。值得注意的是,在极端情况下,在相当长的一段时间内都不会发现任何易受攻击的路径。在这种情况下,NeuFuzz遵循PTfuzz的原始种子选择策略,直到找到一条易受攻击的路径。

带有学习的算法2 NeuFuzz模糊循环

NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

第五节:实施与评估

   我们的模型训练包含大约180行python代码,是基于以TensorFlow作为后端的keras开发的。模糊器模块基于PTfuzz实现,它添加了大约600行C / C ++代码,包括与路径恢复,模型预测,种子选择和能量分配等相关的模块实现。

A.评估设置

为了评估我们提出的方法的有效性,我们在不同的测试套件上进行了多次实验,以与当前的先进工具进行比较。

 我们选择一个通用的基准数据集LAVA-M,其中包含四个易受攻击的Linux程序,即base64,md5sum,uniq和who,并且每个应用程序都手动注入了多个错误。此外,为了验证NeuFuzz在实际软件中的可扩展性和有效性,我们选择了9种广泛使用的实际应用进行测试,它们分别是libtiff,binutils,libav,podofo,bento4,libsndfile,audiofile和nasm,它们可以处理多种文件格式,包括图片,elf,文档,视频,音频和asm文件。、

 我们将NeuFuzz与PTfuzz和QAFL进行了比较,它们都支持二进制模糊测试,而无需依赖源代码。我们在配备ubuntu16.04系统,32 GB RAM,Intel酷睿i7 8700k处理器和一个Nvidia 1080Ti GPU的计算机上运行所有实验。在所有比较实验中,我们只运行一个带有CPU内核的模糊实例,每个实例运行24小时。实验旨在回答以下三个研究问题。

  • RQ 1.我们学习的神经网络模型的有效性如何?
  • RQ 2. NeuFuzz的漏洞检测能力如何?
  • RQ 3. NeuFuzz模糊循环的开销如何?

B.结果

1)神经网络模型(RQ 1)的有效性

     该模型的有效性对NeuFuzz至关重要,因为测试用例选择是基于模型预测结果进行的。因此,在本节中,我们评估训练模型的结果。如图5所示,经过5个时期的训练,我们模型的训练准确性达到91%,训练损失减少到22%。当前,没有公开的工具可用于二进制程序的漏洞预测,并且与我们的方法最紧密相关的工作是VDiscover,它提取API序列作为特征,然后使用传统的机器学习算法对其进行训练。相比之下,我们将执行路径作为输入,并使用LSTM进行训练。为了说明我们方法(称为PATH + LSTM)的优势,我们实现了VDiscover的方法,该方法提取动态API序列,然后使用随机森林算法(称为API + RF)进行训练。

  NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

我们考虑在机器学习中使用以下广泛使用的指标来评估我们的方法:误报率(FPR),误报率(FNR),真率(TPR)或召回率,精度(P)和F1量度(F1) 。设TP为具有正确检测到的漏洞的样本数,FP为具有错误检测到的漏洞的样本数,FN为具有未检测到的真实漏洞的样本数,TN为无未检测到的漏洞的样本数。FPR=FP/(FP+Ťñ)是假阳性漏洞与不容易受到攻击的整个样本群体的比率。FñR = Fñ/(ŤP+ Fñ)是假阴性漏洞与易受攻击的样本总数的比率。ŤPR = TP/(ŤP+ Fñ)是真实的正漏洞与易受攻击的样本总数的比率。精确P=ŤP/(ŤP+ FP)测量检测到的漏洞的正确性。F1=2⋅P⋅ŤPR/(P+ ŤPR 同时考虑了精度和TPR。

 我们在两个数据集上评估我们的方法。第一个是我们构建的数据集的一部分(称为测试数据),包括5,564条易受攻击的路径和5,356条干净的路径,占提取的所有执行路径的1/5。另一个是LAVA-M数据集。尽管此数据集提供了触发这些漏洞的易受攻击的程序和输入,但没有补丁程序版本,因此我们仅考虑TPR和FNR。结果列于表3。我们可以看到,对于两个数据集,NeuFuzz的方法都优于VDiscover,这可以用以下事实来解释:漏洞不仅反映在API调用序列中,而且反映在约束条件中。例如,使用完整的安全检查调用strcpy可能很安全。此外,尽管就FPR和TPR而言,我们的模型在LAVA-M上的性能要比在Test-Data上的性能差,但它仍可达到82.5%的正比率,即,在区分易受攻击的代码和不受攻击的代码方面非常有效。

NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

  1. 漏洞检测功能(RQ 2)

独特的碰撞次数是衡量Fuzzer有效性的重要因素。某些崩溃可能是由相同的根本原因(即重复的原因)引起的,或者可能与安全性无关;但是,通常,发现的崩溃数量越多,可以识别出更多漏洞的可能性就越高。在本节中,我们将比较LAF-M和实际应用中NeuFuzz,PTfuzz和QAFL的结果。表4显示在LAVA-M中使用不同模糊器发现的错误数量。第一列是目标程序的名称,第二列是目标程序中的已知错误的数量,最后三列分别是NeuFuzz,PTfuzz和QAFL发现的错误的数量。NeuFuzz发现了19个错误,超过了PTfuzz和QAFL。md5sum实验无法由NeuFuzz,PTfuzz和QAFL完成,因为方法在第一个输入上崩溃了,并且在[15]中出现了相同的现象。

NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

表5中显示了实际应用程序的结果。这些应用程序在测试时均为最新版本。NeuFuzz比PTfuzz和QAFL可以发现更多的崩溃,因此NeuFuzz更有可能发现漏洞。测试这些程序24小时后,总共发现1290个崩溃。然后,我们使用AddressSanitizer [42]执行重复数据删除并进一步确定42个漏洞。这些漏洞中的14个以前是由其他研究人员发现并披露的,但是供应商尚未发布补丁程序。其他28个未知,CVE确认其中21个。发现的漏洞主要包括缓冲区溢出,UAF,空指针取消引用,被零除,无效的内存访问和其他内存损坏问题。此外,该表的最后一列显示NeuFuzz可以在24小时内找到所有CVE,比PTfuzz和QAFL快得多。PTfuzz和QAFL缺少某些漏洞,“ N”表示该工具找不到相应的CVE。

NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

图6显示了24小时内在9个实际应用中不同模糊器的独特崩溃的增长情况。从增长趋势来看,我们发现NeuFuzz发现的唯一崩溃数量的增长速度比PTfuzz和QAFL增长得更快,而NeuFuzz发现的崩溃数量更多。例如,当对libsndfile库的sndfile-deinterleave程序进行模糊处理时,NeuFuzz发现5个崩溃,而PTfuzz和QAFL没有崩溃。进一步的分析表明,这是一个堆栈溢出漏洞。此外,由于QAFL基于QEMU,并且在执行速度方面比基于硬件实现的PTfuzz低得多,因此QAFL在这三个模糊器中表现最差。简而言之,NeuFuzz在漏洞检测的效率和有效性方面均表现更好。

NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

  1. NeuFuzz(RQ 3)的开销

  灰盒模糊器的有效性在很大程度上取决于其执行速度,而在很大程度上,执行速度仅受执行一个测试用例所需时间的限制。因此,我们需要确保我们的方法不会导致较大的性能开销,从而降低了模糊测试的效率。我们通过每秒执行的测试用例数(exe / s)来衡量每个模糊器的执行速度,以证明模糊测试的开销。如图7所示,NeuFuzz的执行速度比QAFL快约2.5倍,但比PTfuzz慢约8%,这是因为NeuFuzz需要花费更多时间进行路径恢复和漏洞预测。实际上,我们发现时间开销主要来自路径恢复,它与执行路径的长度成比例。在简单的神经网络中预测结果的时间复杂度仅为O(Ñ 1.⋅n 2 + n 2.⋅n 3 + … ) [43]。因此,当节点数在较小范围内时(如在我们的模型中一样),与实际运行目标程序一次相比,预测过程所需的时间要少得多。简而言之,对于模糊测试任务,开销是相对可以接受的。此外,尽管训练神经网络模型需要一些时间,但是训练过程是离线进行的,对于所有后续测试,我们只需要训练一次模型;因此,我们不考虑花费在模型训练上的时间。

NeuFuzz:Fuzzy testing based on deep neural network(使用深度神经网络进行有效的模糊测试)

C.讨论

评估结果表明,NeuFuzz以合理的成本实现了比当前最先进的Fuzzer更好的漏洞检测,但是仍有一些局限性值得进一步研究。

首先,NeuFuzz需要特定硬件和操作系统的支持,因为我们的方法基于PTfuzz。PTfuzz使用Intel PT,这是第五代Intel CPU的新功能,并且PTfuzz只能在Linux内核版本至少为4.1.x的Linux平台上运行。

其次,当我们使用执行路径作为训练模型的输入时,NeuFuzz无法处理一些超出我们阈值的程序路径,这限制了我们模型的能力。将来,我们将考虑其他表示路径的方法,例如动态程序切片。此外,我们应该考虑应用其他神经网络模型来学习漏洞模式。

第三,NeuFuzz无法找到隐藏在复杂的完整性检查(例如魔术字节)后面的漏洞,因为神经网络模型只能预测用于指导种子选择的运动路径。这个问题是提高代码覆盖率的热门话题,我们可以将一些当前的高级方法集成到我们的工具中,以增强漏洞检测功能。

第四,即使在模糊过程中发现种子可以触发新路径并且学习的模型准确地标识了该路径,NeuFuzz仍然可能不会触发漏洞。这是由于模糊的随机性,因为通过变异产生的新种子可能无法触发原始的易受攻击的路径。提出了通过动态仪器解决此问题的方法。将来,我们可以考虑应用深度强化学习,以确保尽可能多地测试指定的路径,从而进一步提高模糊测试能力。

六:结论

  当前的灰盒模糊测试技术主要集中在如何提高代码覆盖率,而忽略程序中易受攻击的代码的分布。也就是说,他们试图覆盖尽可能多的程序路径,而不是探索可能是易受攻击的路径。在本文中,我们提出了一种基于深度神经网络的新种子选择策略,并实现了路径敏感的二进制模糊测试工具NeuFuzz。我们构建了一个大型数据集来训练我们的神经网络模型以学习隐藏的漏洞模式,然后使用生成的模型来预测在模糊过程中触发的新路径。最后,我们优先考虑易受攻击路径的种子,并根据预测结果为这些种子分配更多的突变能量。我们对精心制作的基准测试和实际应用进行了实验。结果表明,我们提出的方法在崩溃发现和漏洞检测方面既有效又高效。与PTfuzz和QAFL相比,NeuFuzz可以在更短的时间内发现更多的漏洞。我们在9个广泛使用的实际应用程序中发现了28个新的安全漏洞,其中21个已由CVE确认。

上一篇:论文笔记:(CVPR2019)Relation-Shape Convolutional Neural Network for Point Cloud Analysis


下一篇:图神经网络(Graph Neural Networks,GNN)综述与相关应用概述