全文字数:10729 全文字符数:5146 正文字数:10048 正文字符数:4925 图片数:2 表格数:3
摘 要
网络安全环境日益严峻的当下,尤其是软件漏洞问题的危害越来越严重,不符合预期行为的漏洞会导致系统的崩溃、数据泄漏等严重后果。为了快速、准确地发现软件漏洞,漏洞挖掘技术逐渐成为了软件安全评估的重要手段之一。基于动态分析技术的漏洞挖掘方法包括模糊测试(fuzz)、符号测试等。其中,fuzz作为一种经典的漏洞挖掘方法被广泛使用。本文首先介绍了fuzz技术的发展历史,从最早的随机测试到现代化的基于代码覆盖率的变异测试,分析了AFL和Driller两种著名的fuzz工具的源代码,讨论了fork server、instrumentation等AFL组件及其优化策略,并对它们的作用进行了解释。随后论文介绍了一种新的借鉴了AFL和Driller思想的fuzz工具DigFuzz,并提供了一种全新的测试用例生成方法。DigFuzz的核心思想是使用混合的符号执行和fuzz技术来优化测试用例的生成。最后论文详细阐述了DigFuzz的实现原理和性能评估结果,证明了它在漏洞挖掘中的有效性。
关键词:fuzz技术 漏洞挖掘 DigFuzz工具 代码覆盖率 符号执行 优化策略
Abstract
In today's increasingly challenging cybersecurity landscape, software vulnerabilities pose a significant threat, leading to consequences such as system crashes and data breaches. To swiftly and accurately detect software vulnerabilities, vulnerability discovery techniques have become an essential means of software security assessment. Dynamic analysis-based vulnerability discovery methods include fuzz testing and symbolic testing. Among them, fuzzing, as a classic vulnerability discovery technique, has been widely adopted.
This article first introduces the historical development of fuzzing techniques, from early random testing to modern code coverage-based mutation testing. It analyzes the source code of two famous fuzzing tools, AFL (American Fuzzy Lop) and Driller, discussing the AFL components, such as fork server and instrumentation, as well as their optimization strategies, and provides explanations for their roles. Subsequently, a novel fuzzing tool called DigFuzz is proposed, drawing inspiration from AFL and Driller, along with a new approach for test case generation.
The core idea of DigFuzz lies in optimizing test case generation using a hybrid of symbolic execution and fuzzing techniques. Finally, the article elaborates on the implementation principles and performance evaluation results of DigFuzz, demonstrating its effectiveness in vulnerability discovery.
Key words: fuzzing technique vulnerability discovery DigFuzz tool code coverage symbolic execution optimization strategies.
目 录
第1章绪论1第2章模糊测试技术分析32.1测试工具分类及研究现状52.2原理分析62.3混合测试的分析7第3章Digfuzz理论基础93.1策略优势93.2算法原理9第4章源码分析114.1AFL源码分析114.1.1工作流程以及模块分析114.1.2插桩分析114.1.3核心源码分析124.1.4覆盖率信息记录134.2Driller源码分析144.2.1原理概述144.2.2调用方法144.2.3约束求解过程分析154.2.4流程分析结论16第5章实验分析175.1实验环境和样本175.2实验过程185.3实验结果18第6章结束语20参考文献21致 谢22
DigFuzz工具在漏洞挖掘中的应用研究
绪论
近年来,随着互联网技术的不断发展,网络安全问题日益突出,尤其是软件漏洞问题。软件漏洞是指应用程序中存在的不符合预期行为的漏洞,因其会导致系统的崩溃、数据泄露等严重后果,被黑客和病毒作者广泛利用,造成了严重的安全威胁。因此,软件漏洞的挖掘和修复成为了当今网络安全领域的重要任务之一。
为了快速、准确地发现软件漏洞,漏洞挖掘技术逐渐成为了软件安全评估的重要手段之一。基于动态分析技术的漏洞挖掘方法包括模糊测试、符号执行等。其中,模糊测试作为一种经典的漏洞挖掘方法,通过随机的输入观察程序的反应来判断程序的状态,从而发现应用程序中存在的漏洞。模糊测试具有简单易用、覆盖面广、发现范围广、进行的速度快等优势。
DigFuzz是一种基于模糊测试的漏洞挖掘工具,它的模型更具有优势,并探索应用程序中可能存在的漏洞。DigFuzz具有自适应、高效、易扩展等优点,因此在漏洞挖掘领域中得到了广泛的关注和应用。然而,DigFuzz工具的原理、优势、不足等方面仍需要进行深入研究和探索。
本研究的主要目的是探究DigFuzz工具在漏洞挖掘中的应用效果和局限性,以期为漏洞挖掘领域的研究提供一定的参考价值。具体来说,本研究将从以下几个方面展开:
分析DigFuzz工具的原理和设计特点,以深入了解该工具的工作原理。
探究DigFuzz工具在不同应用场景下的漏洞挖掘效果,对比其与其他漏洞挖掘工具的优势和劣势,并对其进行评估和总结。
研究DigFuzz工具在漏洞挖掘中的应用方法和技巧,以期为漏洞挖掘研究者和安全工程师提供一定的参考和指导。
本研究的意义在于:
为漏洞挖掘领域的研究提供新的思路和方法,提高漏洞挖掘的效率和准确性。
对DigFuzz工具的研究和评估,能够更好地了解该工具的优劣势和适用范围,对其进一步的改进和优化提供参考。
为安全工程师和漏洞挖掘研究者提供实用的指导和技巧,帮助其更好地应对软件漏洞挖掘和修复工作。
模糊测试技术分析
模糊测试根据测试对象的透明度(transparency)和测试人员对其内部结构的了解程度,可以分为白盒测试和黑盒测试两种类型。还有一种测试方法为灰盒测试,是指测试人员部分了解测试对象的内部结构,但并不完全了解的测试方法。从研究对象形态的角度可分为基于中间语言和基于底层指令集的漏洞分析技术。
二进制白盒模糊测试(Binary White Box Fuzzing)是一种基于程序源代码或汇编代码分析的模糊测试技术。它的目标是发现二进制程序中的漏洞和缺陷。以下是二进制白盒模糊测试的发展历史:
2007年:第一个二进制白盒模糊测试工具 BitBlaze 发布。它是由加州大学伯克利分校的研究人员开发的。BitBlaze通过动态污点分析来跟踪二进制代码中的数据流,并通过符号执行和约束求解来探索新的执行路径。
2010年:Google发布了一个名为“Syzkaller”的模糊测试工具,它是针对Linux内核的。Syzkaller使用了类似BitBlaze的符号执行技术,并具有自动化测试用例生成和执行的能力。
2011年:由斯坦福大学的研究人员开发的Mayhem二进制模糊测试框架发布。Mayhem使用了一种称为“多项式模糊”的技术,能够更快地生成高覆盖率的测试用例。它也利用了约束求解来探索新的执行路径。
2014年:Codenomicon发布了一个名为“Defensics”的商业模糊测试工具,它使用了二进制白盒模糊测试技术。Defensics能够生成高质量的测试用例,并能够发现很多常规模糊测试工具无法发现的漏洞。
2017年:由加州大学圣地亚哥分校的研究人员开发的AFLSmart工具发布。AFLSmart是一个扩展了AFL(American Fuzzy Lop)的工具,它使用了符号执行和基于模式的搜索来生成测试用例,并能够发现更多的漏洞。
总之,二进制白盒模糊测试技术的发展始于2007年。随着时间的推移,越来越多的研究人员和公司投入到了这个领域,开发了许多优秀的工具和框架,为二进制程序的安全提供了更加有效的保障。
在白盒模糊测试快速发展的同时,黑盒模糊的优势和特点也日益被安全研究人员所重视。不需要源代码或内部结构:黑盒测试不需要程序的源代码或内部结构,从而降低测试的门槛。同时,这也意味着黑盒测试可以用于测试第三方组件或闭源软件,而白盒测试则无法做到这一点。更好的模拟用户行为:黑盒测试的方法是通过输入和输出的模拟来测试程序,这种方式更加接近用户的真实使用场景[1]。黑盒测试可以模拟各种可能的输入组合,从而更好地模拟用户的使用行为。相比之下,白盒测试则更多地关注程序内部的数据流和逻辑,往往无法完全模拟用户行为。更好的保护知识产权:在一些商业软件或产品中,源代码是作为知识产权的核心而保护的。在这种情况下,黑盒测试是一种更好的测试方式,因为它不需要访问源代码,从而不会侵犯知识产权。黑盒测试可以通过测试程序的输入和输出来发现漏洞和缺陷,而不会对源代码造成任何损害。因此,黑盒模糊测试技术的发展也是突飞猛进的。
1990年代:在计算机安全领域出现了许多流行的漏洞检测工具,例如SATAN、Nessus和Nmap。这些工具主要是基于网络协议的分析和漏洞扫描,而不是基于程序的二进制分析。
2000年代:许多商业和开源二进制模糊测试工具开始出现,例如SPIKE、Peach Fuzzer、Sulley和AFL(American Fuzzy Lop)。这些工具通过随机生成或变异输入数据,以期望触发程序中的漏洞和错误。
2010年代:随着二进制程序的复杂性和安全性需求的增加,一些新的黑盒测试技术开始出现。例如基于符号执行的黑盒测试技术,包括Miasm、Triton和Angr。这些工具使用约束求解和符号执行来解决程序路径探索问题,并希望能够生成更有针对性和高覆盖率的测试用例。
2020年代:深度学习和人工智能技术开始应用于黑盒测试领域,例如使用神经网络生成测试用例或预测程序的执行路径。这些技术正在探索新的测试方法和工具,以期望更高效、更准确地发现程序中的漏洞和缺陷。
二进制黑盒测试技术的发展经历了多个阶段,从最初的基于协议的扫描到基于随机或变异的模糊测试,再到基于符号执行和人工智能的新技术。这些技术和工具为程序安全提供了更加全面和有效的保障。
2.1测试工具分类及研究现状
近年来,随着软件规模和复杂度的不断增加,模糊测试已成为发现软件漏洞的重要手段之一。以下是模糊测试研究的一些现状:
模糊测试算法:近年来,研究人员提出了很多新的模糊测试算法,包括基于遗传算法、符号执行、深度学习等技术的算法。例如,使用遗传算法来生成输入,可以在更短的时间内达到更高的代码覆盖率。使用符号执行来指导模糊测试,可以更精确地控制程序状态和输入约束。使用深度学习技术来生成输入,可以在某些情况下提高模糊测试的效率和准确性。
自适应变异策略:自适应变异策略是指根据程序运行时的反馈信息来动态地选择变异策略。近年来,不断发现新的生成策略来有针对性的提高变异的使用价值。这些方法可以在更短的时间内达到更高的代码覆盖率,提高模糊测试的效率[2]。
多目标优化:多目标优化是指在模糊测试中同时考虑多个目标,例如代码覆盖率、漏洞数量和测试时间等。这些方法可以帮助测试人员在更短的时间内达到更好的测试效果,同时考虑多个目标。
跨平台模糊测试:跨平台模糊测试是指在不同的平台上进行模糊测试,例如在移动设备上、嵌入式设备上等等。近年来,研究人员提出了很多跨平台模糊测试的方法,例如基于移动设备的模糊测试方法、基于模拟器的模糊测试方法等等。这些方法可以帮助测试人员在更广泛的场景下进行模糊测试,发现更多的漏洞和缺陷。
不同类型的工具使用的技术和算法有所不同,可以根据其特点进行分类。例如,纯随机模糊测试工具主要是通过随机生成测试用例来模糊测试应用程序,符号执行模糊测试工具则使用符号执行技术来推导程序的路径条件,基于模型的模糊测试工具则通过模型检测和符号执行等技术来生成测试用例以覆盖模型中的所有路径和状态。根据不同类型的特点,可以进行分类以便更好地选择和应用模糊测试工具[3]。
纯随机模糊测试工具:这种工具是最基本的模糊测试工具,它们通过随机生成测试用例来模糊测试应用程序,例如AFL、Radamsa等。
符号执行模糊测试工具:这种工具使用符号执行技术来推导程序的路径条件,并生成测试用例以达到覆盖所有路径的目的,例如KLEE、SAGE等。
基于模型的模糊测试工具:这种工具使用模型检测和符号执行等技术来推导程序的执行模型,并基于模型生成测试用例以覆盖模型中的所有路径和状态,例如TaintScope、Driller以及本文所研究的Digfuzz[4]。
突变模糊测试工具:这种工具通过对已有的测试用例进行变异,生成新的测试用例以发现更多的漏洞,例如Peach Fuzzer、Radamsa等。
智能模糊测试工具:这种工具使用机器学习、人工智能等技术,通过分析程序的执行情况和异常情况,生成更有针对性的测试用例,例如DeepFuzz、Neuzz等。
2.2原理分析
本文核心围绕Digfuzz工具,因此模糊测试工具以AFL(American Fuzzy Lop)举例。它通过对目标程序输入数据进行变异和组合,寻找潜在的漏洞或崩溃点。
AFL-fuzz的原理如下:
插桩(Instrumentation):AFL-fuzz通过静态或动态二进制插桩技术,对目标程序进行修改,以记录程序的运行信息,如分支覆盖、程序计数等。
输入选择(Input Selection):AFL-fuzz会生成一组输入种子,用于作为模糊测试的起点。这些种子可以是任意的、合法的输入文件,也可以是手动构造的,甚至是模糊测试过程中发现的崩溃点生成的输入文件。
变异(Mutation):AFL-fuzz会对每个种子进行变异操作,如位翻转、随机字节替换、删除和插入等操作,以生成新的输入文件。通过不断的变异和组合,AFL-fuzz可以探索大量的输入空间,从而找到潜在的漏洞或崩溃点。
分支导向(Branch Guided):AFL-fuzz使用覆盖率信息来指导模糊测试过程。它会记录程序运行过程中的分支覆盖情况,并尝试优先测试未被覆盖的分支或更深的代码路径。这种方法可以提高模糊测试的效率,使得更多的漏洞或崩溃点被发现[5]。
崩溃或超时检测(Crash or Timeout Detection):当模糊测试发现目标程序崩溃或超时时,AFL-fuzz会将该输入文件保存下来,以供后续的分析和调试。此外,AFL-fuzz还会自动分析崩溃的原因,如内存泄漏、空指针引用、整数溢出等等,并生成报告供分析人员参考。
综上所述,AFL-fuzz通过插桩、输入选择、变异、分支导向和崩溃或超时检测等步骤,实现了高效的模糊测试。它是一款广泛使用的漏洞发现工具,可以在各种环境下使用,包括二进制、网络协议、Web应用程序等。
2.3混合测试的分析
模糊测试聚焦于对不确定状态的随机尝试,在分支选择上更轻松进入到具有广阔的满足值范围的路径,因此在探索过程中会遗漏包含特定分支的路径。遗漏特定分支会影响测试代码覆盖率,对于最终结果的有效性有较大影。模糊测试和符号执行在自动化漏洞挖掘中是可以相辅相成的,模糊测试的劣势恰好是符号执行的功能所在,因此将二者结合起来利用它们的独特优势并减轻弱点[6]。
在混合方案中,模糊测试由于时间成本较低速度较快通常负责大范围的路径探索,如果需要有特异性的针对性路径探索则需要动态执行加以辅助。通过这种方式,可以减少动态符号执行中的路径爆炸问题,因为符号执行主要工作负责探索可能阻止模糊测试的低概率路径。那如何协调好两者的关系就成为混合测试工具效率的决定性因素。
在其他混合测试工具中如(driller),是采用“需求启动”策略来结合两者,主要思路是先由模糊测试工具对初始化种子进行测试,当进行一段时间后,模糊测试成果进入瓶颈即一段时间没有取得新路径的输入,则启动符号执行,寻找之前遗漏的路径,并对当前输入队列进行重排。
Digfuzz理论基础
3.1策略优势
Digfuzz的策略是“最佳切换”,而其他主流混合测试工具的策略是“需求启动”,要了解到“需求启动”策略的弊端,首先,模糊器的卡住状态不是启动符号执行的最优指标。模糊测试正在取得进展并不一定意味着没必要进行符号执行。模糊器仍然可以插入工作来探索新代码,即使它已经被许多特定分支阻碍,而因为模糊器未处于卡住状态,因此符号执行器长期处于空闲。其次,启动符号测试的用例可能数量庞大,导致需要大量的时间耗费在多余路径的探索过程中,并且可能会在很长一段时间后为特定路径生成有效的输入。到那时,模糊器可能已经生成了一个有价值的输入来遍历该特定路径,从而使整个符号执行变得并不必要。
策略的关键挑战是以轻量级方式量化遍历模糊器路径的难度。目前常见的解决方案是使用重量级的程序分析来量化路径的价值,譬如值分析和概率符号执行。然而,这些技术并对于解决我们的问题帮助微小:通过使用重量级分析来量化路径的难度,也可以解决路径约束并生成分支路径的输入。最近的一项研究提出了一种策略模型,用于优化的结构测试。它的创新点在于计算程序路径概率和约束求解成本的最优策略,然后问题可视为为带有成本的马尔可夫决策过程(简称MDPC)。
3.2算法原理
在Digfuzz工具中,作者使用了一种新的基于蒙特卡罗的概率路径优先级模型来应对这些挑战[1]。为了轻量化,模型应用蒙特卡罗方法来计算通过模糊测试探索路径的概率。要使蒙特卡罗方法有效运作,需要基于以下两点:1)以搜索空间为样本,对搜索空间的抽样必须是随机的; 2)抽样的数量要足够量,才能使估计具有统计意义。由于输入来自于模糊器随机为测试程序生成,将这些输入被视为整个程序状态空间的随机样本,以期满足第一个要求[2]。此外,由于模糊测试具有比较高的测试速度,因此第二个要求也可以满足。因此,通过将模糊测试作为抽样过程,我们可以通过覆盖统计以轻量级方式统计估计概率。
源码分析
随着新兴的高效符号执行方法的出现,传统的以Fuzzing为主导的混合测试的方法也许已经不再是最佳方案。新兴的符号执行方法的执行效率已经大幅度提高,也不再成为混合模糊测试的瓶颈。但是fuzz依然是模糊测试中重要组成部分,为项目量身定做高效的fuzz策略就不得不要求研究人员熟悉掌握源码的主要逻辑[7]。
4.1 AFL源码分析
4.1.1工作流程以及模块分析
|
AFL的fuzz工作流程 | |
|---|---|
|
Step1: |
main函数先进行初始化和选项处理; |
|
Step2: |
执行input文件夹下的预先准备的所有testcase(perform_dry_run),生成初始化的queue和bitmap; |
|
Step3: |
通过cull_queue对queue进行精选,减小input的量; |
|
Step4: |
然后进行while(1)循环不断进行fuzz。 |
|
预料精选流程 | |
|---|---|
|
Step1: |
cull_queue()对queue进行精选,选出favored。 |
|
Step2: |
判断queue_cur是否为NULL,如果是,则表示已经完成对队列的遍历,queue_cycle++,初始化相关参数,重新开始遍历队列; |
|
Step3: |
fuzz queue_cur对应的input文件,**fuzz_one()**; |
|
Step4: |
判断是否结束,并更新queue_cur和current_entry |
AFL是一种通过变异、输入收集、变异策略和收缩器等技术生成大量测试用例以提高程序有效性和效率的模糊测试工具。其中插桩模块分为3类,分别是普通插桩、llvm插桩模式、qemu插桩模式,以及核心fuzzer模块与其他辅助模块[8]。
在主循环(while)之中,是对fuzz预料(queue)的精选过程
4.1.2插桩分析
普通插桩使用的是AFL-gcc,作为GCC或clang的一个封装(wrapper),主要实现对关键节点的代码插桩,属于汇编级,从而记录程序执行路径等相关信息,同时对编译参数进行筛选,主要实现封装效果。
AFL插桩是GNU as的封装,主要实现对GCC或clang生成的汇编文件进行预处理,并注入AFL-as.h中的插桩代码,完成后在.s文件中会包含插桩代码。插桩只针对于.text代码段,在代码段中正则匹配 \tj[^m].格式的指令,用来判断条件跳转,并根据产生的随机数来控制插桩密度,使用fprintf将trampoline_fmt_64插桩指令写入输出文件[10]。
插桩代码的功能首先是保存当前上下文,然后识别当前插桩代码块的id并调用AFL_maybe_log函数记录,最后对上下文进行复原。进入到AFL_maybe_log函数操作共享内存,并开始运行AFL_forkserver,完成后通过管道告知fuzzer。AFL_store将传入的代码块id的记数总量加一。
LLVM插桩作为另一个单独的插桩形式,它具备了前后端分离的形式,前端后端都依赖于中间语言IR,同样实现编译器级别的插桩,具备多种优化提高效率,可以实现与CPU无关,可以在非X86架构上进行fuzz,并且可以更好的处理多线程目标[9]。
4.1.3核心源码分析
AFL中的核心部分就是fuzzer的实现,对应源码AFL_fuzz.c部分,即通过不断变异测试用例来调整程序的执行路径。该部分代码根据功能进行分类分为三个部分,分别是初始配置fuzz环境、执行fuzz的主循环过程、测试用例的变异过程和方式。
在初始过程中会大量读取当前环境信息,包括对于启动命令的存储、CPU状态、共享内存的初始化,便于后期直接使用或fuzz结束后的回溯。将初始化的种子从in_dir目录下扫描到queue目录中,并按照是否经过确定性编译进行筛选,通过参数形式对,对文件IO形式进行判断。
在开始第一次fuzz时,测试执行所有用例生成初始化的queue和bitmap,只对输入执行一次测试,判断是否出现新的路径、是否需要增加内存、是否需要设置timeout。其中的关键点是对新测试用例的评估,AFL是通过calibrate_case函数实现的,主要功能是初始化并多次启动fork server并用update_bitmap_score进行输入队列的排序,维护最佳入口。
在单个测试用例的启动过程中调用run_target函数进行控制,具体流程如下表
|
Run_target流程 | |
|---|---|
|
第一步 |
将trace_bits全部置0,共享内存初始化 |
|
第二步 |
进入fork子程序,设置环境变量,用execve启动app |
|
第三步 |
在fuzzer中进行控制管道写和状态管道读 |
在fuzz过程中避免多次迭代fork和execve调用,使用fork server机制,只需要调用一次然后依赖管道传递信息即可。每一次新的测试实际上是从forkserver启动fork一个子进程,整体是一个三段式的结构 fuzzer -> fork server -> target子进程。
保持高回报的测试效率要求维持一个有效的入口,通过对case中top_rated的计算以及对top_rated[]的设计,以favorable标识保证更有价值的路径获得更多的测试机会。
4.1.4覆盖率信息记录
如何在fuzz中实现高覆盖率以及高测试效率是最关键的核心,提高覆盖率的基本方法有两种,一种是通过对项目代码进行分模块,使用源码阅读工具understand的treemap功能可以观察项目内模块划分结构;第二种办法是避免无用变异测试用例浪费测试资源即提高效率,首先AFL-fuzz永远不会停止,所以通过什么指标来验证当前用例的测试价值以及后续变异可能产生的价值,就是有针对性自定义AFL-fuzz的效益门槛[11]。针对一个项目找到优秀的测试用例可以考虑寻找项目自身提供的测试用例、之前历史出发bug的测试用例、对现有格式直接进行转换、或使用其他开源的大型语料库等方法。
如何衡量种子用例的测试价值就是覆盖率的意义,首先覆盖率是通过之前插桩所留下的通信结点收集路径信息来量化覆盖率的大小。在AFL套件中有很多辅助工具来提高fuzzer的效率以及对fuzzer的状态进行观测,观察当前程序代码覆盖率的直观工具是AFL-plot,如图1所示,用来绘制fuzzer各种指标的变化趋势,数据可视化更加直观。

添加图片注释,不超过 140 字(可选)
图表 AFL-plot显示fuzzer指标随时间的变化
对覆盖率的实现细节进行分析,对于程序路径的分片依靠对于基本块、函数、边界的识别。基本块顾名思义就是一组必然会顺序执行的代码块,当基本块内部第一条指令被执行后,基本块的其他指令也必然会被识别且块内每条指令的执行数量都是相同的。在AFL实现中,使用的元组(tuple)的思想,二元组来记录当前基本块+前一基本块的信息,从而记录用例在程序中的流程和代码覆盖率。
4.2 Driller源码分析
4.2.1原理概述
使用情景是当fuzzer到达瓶颈或者当前fuzz被卡住长时间没有进展的情况下,将fuzzer当前使用的种子输入到符号执行中,并作为约束,以期待符号执行能发现当前种子路径上尚未探索的分支并进行求解,将求解结果重新输入到种子目录,作为新的一轮输入重新开始fuzz。
4.2.2调用方法
Driller和fuzzer作为python库可以直接导入,设置driller.Localcallback回调函数,指定符号求解新获得路径数量以及其他driller参数。可以通过自己编写python脚本来实现具体调用细节,也可以直接使用shellphuzz,shellphuzz是对driller和fuzzer的启动器,可以直接通过启动参数指定driller和AFL核心数量以及拓展字节数。
启动过程中先对输入种子进行初始化队列,紧接着初始化一个fuzzer对象,它作为一个AFL的壳,指定相关参数并启动AFL-fuzzer监控运行状态。在执行流程中会定时检查当前状态,检查是否有退出操作以及有效目标的发现。不难发现其实fuzzer就是对AFL的一个python封装,主要工作是启动driller以及配置启动参数。
4.2.3约束求解过程分析
启动driller的情形是在fuzzer一筹莫展无法获得新的路径突破的情况下,利用符号执行(symbolic execution)来发现新的路径以打破fuzzer低效的僵局实现效率的提高。
通过指定增加输入的长度数量来生成新的临时输入,将临时输入推到符号执行的stdin中,进入drill_generator的生成流程。首先创建两个对象,分别是tracer和angr.project,完成对stdin的限制后用该state生成一个simulation_manager,使用了一个名为 simgr 的符号执行状态管理器,它包含了多个状态(state),每个状态代表着一个程序执行的路径。while simgr.active and simgr.one_active.globals['trace_idx'] < len(r.trace) - 1: 表示只要还有可用状态,并且当前状态的指令索引还没有达到程序执行轨迹的最后一个指令,就执行循环体中的内容。循环体中的 simgr.step() 命令表示执行一条指令,并根据指令的影响,更新符号执行状态[11]。
接着,代码使用了条件语句 if 'diverted' not in simgr.stashes: 来判断当前的状态是否需要进行分支(即是否遇到了条件分支或函数调用等情况)。如果需要进行分支,那么就使用 simgr.diverted.pop(0) 命令从 diverted 中取出一个状态,并将其作为当前状态进行符号执行。另外,还使用了 state.options.remove(angr.options.LAZY_SOLVES) 命令将符号执行选项中的 LAZY_SOLVES 选项删除,以便在符号执行时立即解决约束条件。
接下来,代码使用了一个循环 while len(simgr.active) and accumulated < 1024: 来执行符号执行,直到当前状态列表为空或者已经执行了 1024 次。每次循环,使用 simgr.step() 命令执行一条指令,并根据指令的影响,更新符号执行状态。使用 len(simgr.active) + len(simgr.deadended) 计算当前状态列表中的状态数(len(simgr.active))和已经结束的状态数(len(simgr.deadended)),并将其乘以 steps 得到当前已经执行的指令数,最终判断其是否达到了 1024。如果当前状态列表不为空,就将其中的状态移到 active 列表中,以便后续的符号执行。
最后,代码使用 for dumpable in simgr.active: 循环遍历当前状态列表中的每一个状态,并判断程序的操作系统类型是否为 CGC(Cyber Grand Challenge)。如果是 CGC,就使用 state.solver.eval(generated, cast_to=bytes) 命令对符号变量 generated 进行求解,得到它的具体取值。其中,generated 是一个符号变量,其代表的是程序中的某个值,而不是具体的值。通过使用 state.solver.eval() 命令求解这个符号变量,就可以得到该符号变量的具体取值,即该程序中对应的具体值[6]。参数 cast_to=bytes 表示将结果转换成字节串的形式进行输出。
4.2.4流程分析结论
当情景符合driller启动需求,通过fullinit的state作为初始状态,将queue中的种子依次装填到stdin中,建立一个simulation_manager然后执行单步step,直到遇到divert分支,将此刻为最终状态获取满足约束的stdin内容,写入到queue中,启动新的一轮fuzz。
实验分析
5.1实验环境和样本
在对工具进行完源码原理分析后,通过对比实验来比较启动策略对于漏洞发现的效率差异,作为评价效率的重要有效指标分为两类,一种是漏洞发现的数量,一种是新的种子生成的数量。以漏洞数量直接作为漏洞挖掘的最终目的,并在过程中以程序运行出现crash的形式体现。而新生成的种子代表这新路径的发现,表示着符号执行获得的价值。两类指标分别对应着不同的过程,最优解的模糊测试正是将模糊测试和符号执行找到其中的平衡点。
选择的实验样本是DARPA Cyber Grand Challenge在github上开源的程序代码,由Cromulence、Kaprica Security等多家顶尖安全公司开发,程序的代码体量,程序功能都十分健全,作为自动化漏洞挖掘测试具有重要意义。其中共有118个测试样例,涉及到的漏洞类型众多,以CWE(通用漏洞列表)作为分类方式,主要涉及CWE-190整数溢出或回绕、CWE-131缓冲区大小计算不正确、CWE-120缓冲区复制而不检查输入大小、CWE-787越界写入、CWE-122基于堆的缓冲区溢出、CWE-119内存缓冲区范围内的操作限制不当、CWE-476 NULL指针解除引用等二进制经典漏洞,丰富的漏洞类型用来验证自动化漏洞挖掘工具的健壮性。
选择对照进行测试的三个工具分别是 AFL-Fuzz、Driller、Digfuzz。其中Driller是一个用于自动化漏洞发现的工具,在DARPA Cyber Grand Challenge比赛中成功地运行了12个小时,并发现了五个重要的漏洞。尽管这个工具的效率得到了肯定,但是它也存在着一些问题和挑战。
5.2实验过程
在测试过程中监控测试程序的成果产出,并观察不同的程序逻辑结构对于不同切换策略产生了何种影响。同时在测试过程中保持不同的测试方法要均衡的分配测试实例,保证在底层执行效率一致,在本实验中AFL为一个测试实例、Driller和Digfuzz都是一个测试实例一个符号执行实例。
如何正确识别漏洞的真实性,也是实验有效性的重要一环。对于漏洞挖掘的结果处理也是重要的一部分,当大量的异常输出导致测试主体出现了很多crash时,就需要将工作中心放在如何快速处理crash的过程中。在网络上有很多批量处理的工具,要对这些工具大致进行甄别,用来应对使用者个性化的需求,针对所要发现的有针对性漏洞进行批量用例测试,采用插件工具使用gdb插件exploitable来对崩溃现场进行分析以及快照工作,以及crashwalk对崩溃进行识别,确定崩溃的可利用性并对漏洞进行分类。
5.3实验结果
首先,模糊器只在118个二进制程序中的49个中使用了concolic执行。这代表着模糊器只会卡在其中49个二进制文件上。这一事实与Driller在比赛报道的数字相当。这意味着Driller需要更多的优化,以便能够更广泛地使用concolic执行,并更好地适应各种类型的程序。此外,尽管模糊器的执行状态会卡住,但这并不意味着一定需要执行。因此,需要启动策略的效率需要进一步改进。
其次,需求启动策略无法识别特定路径,这导致执行效率收到极大的影响。虽然模糊器通常可以保留更多的输入,但对于阻止模糊测试的特定分支的输入却只有非常小的机会被通过结构执行来拾取和处理。这也解释了为什么在1709次符号执行之后,模糊执行器仅使用了51个来自符号执行的输入,没有被使用的输入实际上就是对时间的无意义消耗。因此,需要优化需求启动策略,以更好地捕捉程序的特定路径和条件,并减少输入的浪费。
另一个问题是,模糊器的卡住状态仅在少数程序中发生,并且大多数卡住状态非常短,85%的卡住状态要少于100秒。这意味着卡住状态并不一定意味着需要执行。因此,需要设计更加智能的策略来判断是否需要执行特定的输入,以避免浪费时间和资源。在实验过程中有一种类型的问题影响也很明显,如图2所示,在混合测试过程中碰到特殊值匹配的情况下一般会唤起符号执行来约束求解求得一个输入内容后,再次调用时会直接步过其他路径,重复一个分支,导致没有结果或者结果单调。
图表 2

添加图片注释,不超过 140 字(可选)
另外,Driller的模糊器可以从之前提到的49个二进制文件中的13个二进制文件中获得有关执行的帮助。这意味着Driller需要更多的针对不同类型程序的优化,以提高模糊器的执行效率和准确性。这也需要更好的机器学习算法和模型来更好地理解程序的特定路径和条件,并更好地适应各种类型的程序。
结束语
本文全面分析了fuzz技术的发展及其在漏洞发现中的应用。DigFuzz工具得到了详细的研究,强调了它在发现先前未知漏洞方面的有效性。
研究还重点分析了两个流行fuzzing工具AFL和Driller的源代码,以了解它们的内部工作原理,并深入了解如何改进它们。这项分析揭示了AFL和Driller各自的优缺点,并指出结合这两种方法可能会导致更强大的fuzzing工具。
总体而言,本文强调了fuzzing作为识别漏洞的技术的重要性,并强调需要持续的研究和开发,以提高fuzzing工具的有效性和效率。随着软件的不断发展,我们识别和解决安全问题的方法也必须不断改进,而fuzzing无疑将在这个持续努力中发挥关键作用。
参考文献
-
张阳,佟思明,程亮,孙晓山.模糊测试改进技术评估[J].计算机系统应用,2022,31(10):1-14.
-
王允超. 覆盖率导向的模糊测试关键技术研究[D].战略支援部队信息工程大学,2020.
-
李垠帅. 定向型模糊测试方法研究及应用[D].广州大学,2022.
-
任泽众,郑晗,张嘉元,王文杰,冯涛,王鹤,张玉清.模糊测试技术综述[J].计算机研究与发展,2021
-
苏文超,费洪晓.覆盖率引导的灰盒模糊测试综述[J].信息安全研究,2022,8(07):643-655.
-
莫兴远. 可疑基本块导向的定向模糊测试技术研究[D].广州大学,2022.
-
朱佳超. 基于模糊测试的软件漏洞检测技术研究[D].杭州电子科技大学,2022.
-
武伟东. 基于AFL的二进制模糊测试技术研究[D].西安电子科技大学,2021.
-
赵栖栖. 模糊测试工具AFL变异策略优化[D].大连理工大学,2021.
-
刘华渊. 二进制软件堆溢出漏洞自动挖掘技术研究[D].国防科技大学,2020.
-
张咏梅,董竹涛,崔夏晖,张英浩.基于AFL的软件漏洞模糊测试[J].科学技术创新,2018(36):78-79.
致 谢
感谢我的导师,在我的整个研究生生涯中给予了我无私的指导和支持。您的专业知识、耐心和鼓励对我非常重要。在我面临困难和挑战时,您总是给我信心和勇气。特别地,我要感谢您对我进行的指导,帮助我更好地理解和掌握了DigFuzz工具的使用。
感谢我的同学和朋友,他们在研究过程中给予了我很多启示和帮助。我们一起探讨问题、交流经验、分享资源,让我深刻认识到合作的重要性。同时,我要感谢他们在论文写作和修改过程中给予的宝贵建议和帮助。
感谢开源社区的贡献者,感谢你们的开源代码和文档,为我的研究提供了非常有用的资源和支持。同时,我也要感谢在研究过程中参与过实验的个人和组织,感你们提供的数据和环境。
感谢我的家人,感谢你们一直以来的支持和关心。你们的鼓励和支持是我前进的动力和信心。
542

被折叠的 条评论
为什么被折叠?



