覆盖率引导的灰盒模糊测试综述

2022-07-06 01:41苏文超费洪晓
信息安全研究 2022年7期
关键词:测试用例覆盖率漏洞

苏文超 费洪晓

(中南大学计算机学院 长沙 410083)

模糊测试是现在流行的漏洞挖掘技术之一.由于其易部署性和在漏洞挖掘方面的高效性,许多软件公司,如Adobe[1],Cisco[2],Google[3-5],Microsoft[6-7]等,都会在软件开发生命周期中使用模糊测试来保证产品的安全性.

模糊测试的本质是自动化生成随机输入,利用输入快速执行测试程序,并监控异常执行结果来发现软件漏洞.这种随机性可以生成触发程序异常的非预期输入,但同时,随机性也意味着输入是从输入空间无方向、无目标地选定.这种盲目性会导致大量冗余、无效的输入,浪费程序执行时间,而只有少部分输入能真正完成触发程序异常的功能.

为了应对随机性不可避免的性能瓶颈,研究者试图通过程序的执行反馈信息指导模糊测试,从而帮助提升随机性种子生成的方向感,但这种智能化的提升也给模糊器带来一定的性能开销.

以AFL(American fuzzy lop)[8]为首,覆盖率引导的灰盒模糊测试(coverage-guided grey-box fuzzing,CGF)较好地平衡了实际应用中反馈指导的性能开销和漏洞挖掘的效果,成为工业应用最广泛的模糊测试技术之一,也是目前学术界模糊测试研究的主流内容.国际安全会议上关于CGF的研究内容越来越多,如AFLFast[9],Steelix[10],Vuzzer[11],Angora[12],CollAFL[13],T-Fuzz[14].这些工作对CGF的各运行阶段、模糊策略进行了不同程度的改进优化.为了保持CGF的快速发展步伐,有必要对这些改进工作做一个阶段性的整合,帮助梳理其碎片化知识,总结其优化方向,从而全面、连贯地认识CGF的最新发展现状,更明确清晰地进行深入研究.

1 覆盖率引导的灰盒模糊测试简介

1.1 概 述

1.1.1 灰盒模糊测试

模糊测试[15]通过随机生成输入来重复运行测试程序,监控程序异常,通过记录导致异常的输入定位程序中漏洞的位置.所以模糊测试的核心在于能触发程序异常的输入的生成.根据输入的生成方式分类[16],目前主要分为基于生成的模糊测试(generation-based fuzzing)和基于突变的模糊测试(mutation-based fuzzing).基于生成的模糊测试根据已知的输入规范建模,然后根据模型生成新的输入,如Ifuzzer[17],radamsa[18];基于突变的模糊测试先得到应用程序的1组输入样本,根据这些输入样本通过变异的方法生成新的输入,如AFL,hongfuzz[19],zzuf[20].

基于突变的模糊测试随机生成无规则的样本作为测试程序的输入,以测试程序的崩溃或停止行为作为输入是否有效的反馈信息.这种完全脱离程序执行过程的反馈信息指导的模糊测试被定义为黑盒模糊测试.早期的黑盒模糊测试效率低下,大部分输入无法到达程序的深层分支,只能在逻辑较为简单的浅层分支发挥有效作用,在代码复杂度飞跃的现在已经难以发现程序的安全问题[21].

因此,通过程序执行过程的反馈信息指导输入变异方向的白盒模糊测试开始得到应用.白盒模糊测试可合法访问测试程序的源代码,通过重量级的程序分析工具获取程序执行时的反馈信息.但重量级的程序分析工具往往会大幅增加性能开销.为了能在可控的性能开销上提高变异输入的有效性,人们开始提倡使用灰盒模糊测试.灰盒模糊测试处于白盒和黑盒模糊测试之间,它可以采用轻量级的程序分析工具来获取粗粒度的程序执行信息.轻量级的程序分析工具使得灰盒模糊测试在同样时间内较白盒执行更多(百倍甚至千倍)的输入,因此,灰盒模糊测试的效果甚至能超过白盒[22-23].

1.1.2 覆盖率引导的模糊测试

模糊测试的目标是覆盖程序尽可能多的运行状态,挖掘程序中潜在的漏洞.然而,由于程序行为的不确定性,程序状态并不能被直观地测量.研究者因此选择将代码覆盖率作为程序状态的替代测量指标,即新的代码覆盖率的增加近似于新的程序状态的增加.

覆盖率引导的模糊测试(coverage-guided fuzzing)率先使用代码覆盖率作为程序执行反馈信息指导生成有效的输入.有效的输入即能执行程序还未执行过的代码.基于有效的输入进行变异,生成的新的输入更容易遍历到程序尽可能多的代码,也更有可能挖掘到这些代码中的潜在漏洞.AFL在工业上持续的成功经验证实了这种理念的正确性.利用代码覆盖率指导生成覆盖目标代码和程序目标路径的输入的模糊测试叫作定向模糊测试(directed fuzzing),如AFLGO[24].

1.1.3 覆盖率引导的灰盒模糊测试

CGF是灰盒模糊测试和覆盖率引导的模糊测试的组合概念.它通过轻量级的程序分析工具获取程序执行时的覆盖信息,并根据覆盖信息指导输入生成.

跟踪输入在测试程序中的执行路径可得到准确全面的程序反馈信息,但由于路径覆盖会带来高昂的性能消耗,实际应用中的CGF一般选择将更粗糙的基本块作为代码覆盖的粒度.自AFL将边覆盖引入模糊测试后,研究者发现边覆盖可获得比块覆盖更准确的执行信息,边覆盖开始成为覆盖率引导的模糊测试的主流研究方向[13].然而,不管是边覆盖还是块覆盖,CGF都是通过其覆盖信息的反馈调整输入,以期获得在应用程序上更多的代码执行,挖掘到潜在的漏洞.

1.2 覆盖率引导的灰盒模糊测试通用框架

CGF的主流程为模糊迭代过程,主要分为输入生成和程序测试2个阶段.在用户设置的时间超时之前,CGF不断重复生成输入,用输入执行测试程序,再经由执行的反馈信息指导下一轮输入生成.图1展示了典型的CGF流程图.其中:输入生成阶段包括种子调度和变异操作;程序测试阶段包括程序执行和反馈信息收集操作.

图1 CGF流程图

模糊迭代以种子s和插桩程序作为输入.种子s即CGF的初始化种子,其可以由用户提供也可以由模糊器生成.用户提供的种子往往比模糊器随机生成的种子具有更好的输入格式,更可能通过程序的语法语义检查而执行到深层代码.此外,基于高质量的种子进行变异也更容易获得新的高质量的输入[25].所以,在实践中,用户通常会从网络或应用程序的测试文档里提取合法的输入作为模糊迭代的种子,以提高模糊测试的效果.注意,本文中种子和测试用例皆为插桩程序的输入.为了区分其在模糊迭代过程中的作用,将保存在全局队列S中,用于变异的高质量的输入叫作种子;将种子经过变异操作后,随机生成的直接用于执行程序的输入叫作测试用例.

初始化种子作为变异的候选样本,保存到全局队列S中(步骤①).然后,种子调度器根据一定的评价标准把种子排好优先级,从队列S中选择一个优先级最高,也就是质量最好的种子(步骤②),再根据其质量权重分配该种子相应的能量值(步骤③),此时,下一轮待变异的种子s′已确定.能量是指导变异器生成测试用例数量的指标.能量越高,生成的测试用例数量越多.因此,基于能量值的设置,可以将程序测试的时间资源向高质量的输入倾斜,从而在有限的时间内找到尽可能多的程序漏洞.此外,各模糊器的种子选择标准不尽相同,例如,AFL倾向于选择最小测试用例数量或执行速度最快的种子,AFLFAST倾向于选择能够执行到低执行频率路径的种子.Shudrak等人[26]基于复杂代码更容易出错的猜想,利用软件复杂度对种子排序.

待变异种子s′传递到变异器中(步骤④),同样地,不同的模糊器可以执行不同的突变策略.AFL采用随机修改和拼接的突变策略,包括顺序翻转不同长度和步长的位、小整数的顺序加减、已知整数(0,1,INT_MAX)的插入等方法[27].变异器根据预设的突变策略,对种子s′进行突变,生成新的1组输入,即测试用例(步骤⑤).

用生成的测试用例执行插桩程序(步骤⑥和步骤⑦).插桩即在保证程序原有逻辑完整性的基础上在程序中插入一些探针,从而进行信息采集[28].在CGF中,插桩用于获取程序执行时的覆盖信息.AFL根据是否提供源代码,提供编译器内插桩和外部插桩2种方式.编译器内插桩包括gcc模式和llvm模式,在生成二进制代码时完成代码段插桩工作;外部插桩即利用qemu[29]在基本块转换为TCG块时完成代码段插桩工作[30].

程序执行过程中,模糊器持续跟踪程序状态,收集程序异常和崩溃信息以及覆盖率的反馈信息(步骤⑧).如果1个测试用例导致了程序崩溃,或者增加了新的代码覆盖,那么该测试用例将保存到队列S中,等待下一轮的种子调度(步骤⑨).

模糊迭代停止后,将所有测试用例执行到的漏洞信息输出(步骤⑩).

CGF的伪代码如算法1所示,遵循与图1一致的执行逻辑.其中:bitmap为程序的全局覆盖率位图,保存整个模糊迭代过程中的代码覆盖信息;bitmap′为每轮测试用例t所执行到的代码覆盖率位图.当测试用例t探测到新的覆盖信息时,该覆盖信息将被加入bitmap,同时将t作为高质量种子保存到全局队列S.

算法1.覆盖率引导的灰盒模糊测试流程.

① 输入:检测后的测试程序p、种子s;

② 输出:缺陷B.

③B=∅;

④bitmap=∅;

⑤S=s;

⑥ repeat:

⑦s′=Schedule(S);

⑧t=Mutate(s′);

⑨B′,bitmap′=Execute(t);

⑩ ifbitmap′∩bitmap≠∅ then

S=S∪t;

2 覆盖率引导的灰盒模糊测试面临的挑战

2.1 面向模糊阶段的挑战

2.1.1 输入生成阶段的挑战

1) 种子调度问题.

种子调度策略包括种子选择策略和能量分配策略2个子部分.目前,在选择种子时,CGF通常使用单维度评价标准对种子质量进行优先级排序,如文献[26].这种单一的评价体系简单、易实现,但容易导致种子偏见和饥饿现象,使得部分高质量的种子无法得到突变的机会去发挥应有的价值.AFL,AFLFAST试图使用线性权重法[31]计算多维度目标的加权和,并以此作为评价标准.但由于线性权重算法中使用的权重值很大程度依赖于个人经验,而没有统计数据支撑,也带来了经验主义的问题.

能量分配策略决定了程序测试时对种子的时间资源分配.AFL最初将更多的能量分配给规模较小的执行时间较短的种子.AFLFAST基于梯度下降算法将能量更多地分配给执行低频路径的种子.如何合理地评估并量化种子的质量,分配相应的能量,是研究者持续探索的问题.

2) 种子变异问题.

测试用例的质量会直接影响程序测试的效果.因而各模糊器都致力于提高有效的测试用例的生成比例,即尽可能生成既符合程序语法和语义要求,同时能探查到程序漏洞的测试用例.然而,由于突变空间过大,且缺乏有效的突变方向指导,如何对种子进行变异一直都是CGF的难点,也进一步导致当前的CGF方法具有“难以发现特定输入才能触发的漏洞”“难以发现多个条件同时满足才能触发的漏洞”等问题.

具体来说,突变只有发生在种子的关键位置才会影响程序执行的控制流.同时,突变生成的测试用例的选择也很重要,首选可以增加新的路径执行测试用例.为了生成更合法有效的测试用例,需要使用程序反馈信息确定种子突变的位置、突变的值,从而确定测试用例突变的方向,缩小突变空间的范围.但由于CGF只使用轻量级的程序分析工具,因此获取的覆盖信息不足以准确定位突变位置和突变值.如何获取更多的覆盖反馈信息去指导变异和如何提高现有反馈信息的利用率去缩小突变空间都有待更有效的办法去解决.

随着移动互联网的高速发展,越来越多的应用程序和库需要处理高度结构化的输入,如图像、音频、视频、数据库、文档或电子表格文件.应用程序在执行之前通常会对输入进行语法和语义分析.其中:语法分析将原始输入解析为程序内部数据结构;语义分析对该数据结构进行检查并执行程序的核心逻辑[32].由于CGF缺乏输入结构感知,变异操作都是在种子文件的位级上进行的.而位级变异不太可能改变文件结构,使得大多数生成的测试用例在程序执行的早期阶段就被语法与语义检查拦截,无法有效执行到程序的深层次代码.如何在变异阶段对文件结构进行突变,探索到程序的合法输入格式,从而生成有效的高度结构化输入,同样值得思考.

远程监控系统具体指的所有地方的设备通过一个电脑终端即可进行控制的技术。在电气工程当中通过此种设计方案不但可以使安装及材料等成本得到节省,也能够大大缩减电缆的数量,形成很高的稳定性和组态灵活性,同时达到了生产规模的高效益。可是因为电气工程当中通讯量很大,同时现场总线在通讯速度上还达不到要求,在一些信号较差的区域会造成这种远程监控方式起不到实际作用,无法开展,因此远程监控方式的理念只可用到比较小的电气工程中,不适合整个电气自动化控制系统。

2.1.2 程序测试阶段的挑战

1) 代码覆盖率增长瓶颈问题.

CGF将程序的漏洞挖掘问题转换为一个优化问题,即如何在有限的时间内,覆盖尽可能多的程序代码.利用代码覆盖率作为漏洞数量的替代指标[33],程序覆盖率越高也就意味着发现漏洞的可能性越大.然而,众多实践表明大多数测试用例只重复覆盖相同的部分路径,而不会发散性地覆盖更多的路径[9].如何改变这种情况,提高程序的总体覆盖率是CGF发展的挑战与动力[34].

同时,程序校验也是代码覆盖率增长瓶颈的一个原因.魔术数字、魔术字符串、版本号检查和校验是程序中常用的校验方式,而大量的测试用例被这些程序校验拦截,难以执行到深层代码.

2) 覆盖率信息获取受限问题.

程序分析工具跟踪测试用例在插桩程序中的执行状态,基于收集到的程序反馈信息判断测试用例的质量,决定是否将其加入种子队列.因此,反馈信息的全面性和准确性对于高质量种子的选择非常重要.然而,由于只使用轻量级程序分析工具,CGF无法获取测试用例执行路径的上下文信息[35].上下文信息对于种子质量判断非常有效.例如,如果测试用例执行路径的邻近代码都被其他的输入覆盖过,那么由该测试用例变异生成的输入执行到新的路径的可能性就会很低.那么,该测试用例就不适合作为种子保存.由于种子质量直接关系着程序测试的效果,所以如何获取更详细的覆盖信息,如上下文信息,来正确地选择高质量的种子成为一个急需解决的问题.

3) 程序分析技术的性能问题.

目前,CGF通常通过高成本的白盒分析技术收集反馈信息,如符号执行技术.由于每个测试用例都能在程序中执行不同的路径,所以符号执行非常有效.然而,这种有效性是以大量时间开销和内存开销为代价的,高开销使得模糊测试在可扩展性方面受到严重限制.

保证CGF的简单性、高速性、可靠性、可扩展性是程序分析技术集成的前提.重量级分析技术的集成带来的效果提升不足以抵消性能急剧下降带来的弊端.例如,检测工具发现漏洞的可能性提高了10倍,但运行速度却降低了99%,集成了程序分析技术的智能模式还是不如盲目的随机模式有效.

2.2 面向应用场景的挑战

随着应用程序在人们生活中的使用范围越来越广泛,其所承受的安全威胁也日趋多元化、复杂化,并因不同的使用场景而呈现不同的威胁特点.例如,高速发展的物联网设备开始在人们生活中充当重要的角色,按照如今的增长幅度,到2030年,物联网设备数量将比人类数量还多[36],如此庞大的攻击范围使得物联网对安全漏洞检测工具有了前所未有的需求.然而,尽管CGF对于通用平台上的程序漏洞检测很有效,但由于物联网设备对实际硬件配置的强依赖性,所以通常不可能直接在物联网设备上应用模糊测试.为了应对平台兼容性问题,Zaddach等人[37]试图使用硬件和软件仿真相结合的解决方案,Chen等人[38]试图利用完整的系统仿真技术,但其性能也远远不够理想[23].

将CGF应用到通信协议漏洞检测上时,也面临着覆盖率不高、漏洞挖掘效果差的问题.通信协议通常由服务器/客户端上的状态机来实现,状态转换由包/消息交换等关键协议事件驱动.目前主要使用代理对数据包进行突变和测试[39],但由于缺乏状态感知,CGF无法引导协议进入感兴趣的状态,并重复使用数据包输入对其进行测试.此外,在协议中,多端之间的多包通信,既包括非依赖包/字段的消息传递,也包括依赖包/字段的消息传递.目前CGF使用的单包测试无法有效实现非依赖包之间的代码覆盖,而使用多包测试又可能导致不一致的消息传递.应用于通信协议的CGF必须面对这些挑战并提出解决办法[40].

3 覆盖率引导的灰盒模糊测试的发展

3.1 集成的关键技术

为了应对CGF面临的挑战,研究者试图通过集成其他技术的长处来弥补CGF的缺陷,从而更精确地收集程序控制流和数据流信息,利用收集到的信息生成更具针对性的输入,以更好地指导程序执行阶段的路径探索.目前,符号执行和污点分析是使用较广泛的技术,随着机器学习技术的成熟,机器学习在CGF中的应用也越来越多.

3.1.1 符号执行

静态分析是指不执行程序的状态下,利用词法分析、语法分析、控制流等技术对代码进行扫描,并对其程序行为进行分析的技术.根据分析目标可分为源代码分析和二进制分析.近年来静态分析向模拟执行技术发展,通过符号执行、抽象解释等技术可以发现很多传统意义上动态测试才能发现的缺陷,如未对参数进行边界检查而造成的缓冲区溢出、堆溢出等漏洞[41].

静态分析容易集成到开发环境中,自动化程度较高.早期静态分析中的符号执行技术被广泛应用于模糊测试中,一些灰盒或白盒模糊器利用符号执行提高测试覆盖率,如EFFIGY[42],Prefix[43],Prefast,ARCHER[44]等.

符号执行的概念由Boyer等人[45]提出,其主要思想是使用符号变量代替具体数值作为程序输入,在模拟程序执行过程中收集与输入符号变量相关的路径约束条件,计算符号变量的具体值,生成能够引导程序执行该路径的测试数据.

但由于缺乏程序运行时信息,符号执行难以完全模拟程序执行,使得分析不够精确.为解决该问题,研究人员提出了混合符号执行的概念,将符号执行从静态分析技术转变为动态分析技术.其核心思想是在程序真实运行过程中,判断哪些代码需要符号化执行,哪些代码可直接执行,代表性的工具有EXE[46],CUTE[47],DART[48],SymFuzz[49]等.符号执行可能带来状态爆炸的问题,由此也发展出了动态符号执行的概念.动态符号执行是将路径限制在一个具体的路径上,程序只能触发该路径下的bug或者发现一个新的路径,以减少正在研究的状态数量,并用具体数值替换复杂表达式,降低程序复杂度[50].

3.1.2 污点分析

污点分析技术是信息流分析技术的一种,它将系统或应用程序中的数据标记为污点或非污点,如果污点数据在传播时影响到非污点数据,则该非污点数据被标记修改为污点.若污点标签随数据传播到指定存储区域或信息泄露点,则认为该系统违反了信息流策略.污点分析也可以分为静态污点分析和动态污点分析[51].

静态污点分析是指在不运行程序的情况下,通过词法、语法分析等方法分析变量间数据和控制依赖关系,如赋值、函数调用、别名等;动态污点分析旨在程序运行过程中,跟踪并记录变量、寄存器等值,通常采用插桩的方法,在不破坏原有程序逻辑的基础上插入采集信息的代码,从而获得程序运行的相关信息.污点分析技术首先对污染源进行定位,然后监测污染数据在软件中的传播,最后根据污点汇聚点获取关键信息[52].静态分析考虑了程序所有可能的执行路径,但不运行无法分析准确的漏洞点;动态分析则可以获得程序运行中的具体信息,分析精度较高,但频繁的插桩和影子内存会占用大量系统资源[53],并且只能分析执行到的路径,存在一定的误报.所以有研究者将二者进行了结合,先执行静态污点分析获得初步信息,提高路径覆盖率,然后执行动态污点分析获得漏洞的具体信息,提高精度[54].

由于很多软件的源代码无法获取,所以动态污点分析经常被运用.模糊测试的核心思想是生成大量测试用例保证较高的代码覆盖率从而对程序进行检测,污点分析的引入使模糊测试可以更好地理解漏洞点.此外,动态污点分析与符号执行相结合,更好地提升了具有校验机制的程序进行模糊测试的效率.

3.1.3 机器学习

机器学习旨在模拟人类的学习活动,从对数据的自动分析中获得规律,并利用规律对未知数据进行预测.因为机器学习算法中涉及了大量的统计学理论,机器学习与推断统计学联系尤为密切,也被称为统计学习理论.机器学习算法包括分类算法、聚类算法、回归算法等.

模糊测试随机初始种子集通常很难达到很好的效果,这是由于:种子生成是随机的,没有较好的指向性;测试失败时不能总结规律,从而导致大量重复;种子文件较高的代码重复率使得无法测试深度漏洞.引进机器学习可以很好地解决这类问题.例如,Wang等人[55]利用深度学习网络,将恶意html样本的文件结构作为训练集,训练深度学习模型,生成带有部分针对浏览器漏洞的恶意代码,产生更有针对性的种子文件.

3.2 面向模糊阶段的改进

3.2.1 输入生成阶段改进

输入生成阶段又可分为种子调度阶段和突变阶段.

在种子调度阶段:Vuzzer[11]利用程序感知的模糊策略,通过在程序测试期间对程序进行轻量级静态和动态分析推断控制流特性,使得Vuzzer[11]可优先选择执行深度路径的种子进行突变.CollAFL应用3种不同的策略优先选择具有更多未覆盖的相邻分支或后代的种子.Cerebro[35]使用了一个在线多目标算法,通过平衡代码复杂度、代码覆盖率、执行时间等指标选择种子.同时引入输入潜力概念分配种子能量,即通过预测种子对未覆盖代码的覆盖潜力而不是对已覆盖的执行跟踪来评估输入.FuzzFactory[56]将提高代码覆盖率的输入放入种子队列,同时将特定域相关的中间输入放入种子队列,从而提高特定域的模糊测试覆盖率.

在突变阶段:Hybrid-Fuzz[56]利用符号执行收集关注的程序路径上的约束条件,使用约束求解生成能实际覆盖该路径的测试输入,以达到检测测试程序特定代码点的功能;Skyfire[55]应用数据驱动种子生成策略,以语料库和语法作为输入,学习概率上下文敏感语法(PCSG),以指定语法特征和语义规则,然后利用学习过的PCSG生成种子输入;AFLFast将CGF建模为马尔可夫链,指定从执行路径i的种子突变生成能探索到路径j的测试用例的概率pij,并根据概率指定分配相应的程序执行时间;AFLsmart[32]针对需要处理复杂文件格式的应用程序,利用种子文件的高级结构来生成新文件.将变异的粒度由位级变更到文件级,从而在保持文件有效性的同时探索全新的输入域;Neuzz[33]应用了一种新的程序平滑技术,该技术使用代理神经网络模型,可以增量地学习一个复杂的、真实世界的程序的分支行为的平滑近似,这种神经网络模型可以与梯度导向的输入生成方案一起使用,生成能探索到新路径的输入;Zest[57]通过参数生成器将输入从简单的参数域转换为更结构化的域,如语法有效的XML,从而使参数级突变能够映射到测试输入中的结构突变,生成语义有效的输入;Superion[58]基于语法感知覆盖的灰盒模糊方法处理结构化输入.将每个测试输入解析为一个抽象的语法树,进而引入一种语法感知的调整策略,在保持输入结构有效性的同时调整测试输入;Choi等人[59]利用动态符号执行最大限制地将灰盒变白,生成能满足分支条件的测试用例;SLF[60]利用AFL来识别输入有效性校验以及对此类检查有影响的输入字段,然后根据这些校验与输入的关系对输入进行分类,包括算术关系、对象偏移量、数据结构长度等,提出多目标搜索算法以应用特定类别的变异.

3.2.2 程序测试阶段改进

在程序测试阶段,CGF会持续跟踪输入在测试程序中的执行状态,提取相关的反馈信息,指导如何探索新的路径,并挑选出有价值的高质量种子放入种子队列.研究者们针对该阶段覆盖率信息获取的准确性、全面性挑战和覆盖率增加的挑战做了大量工作,其中,性能开销的降低是伴随着覆盖率信息获取或覆盖率增加的过程而实现的.

在覆盖率信息获取问题上,CollAFL[13]分析了AFL代码覆盖率计算存在的哈希碰撞问题,并提出了新的哈希算法以提高覆盖率信息的准确度.Intel处理器提供Intel PT(Intel processor trace)完成触发和过滤功能,实时地跟踪执行过程,获取更准确的覆盖信息[61].Intel PT具有执行速度快、无源依赖等优点[30].Nagy等人[62]基于大多数测试用例都是无效输入的观点,提出将覆盖率信息用于指导跟踪的方法,只跟踪增加代码覆盖率的测试用例,从而获得更快的速度.

为了突破覆盖率增长瓶颈,很多研究者都试图在突破程序约束及路径检查工作上作改进.符号执行具有天然的优势,可识别执行过程中的阻塞点,帮助模糊器通过程序控制流程中复杂的条件判断.例如:Driller[63]利用动态符号执行生成满足特定条件要求的值,绕过条件判断,从而找到更深层次的漏洞,且为了降低性能开销,只有CGF在一定时间内无法取得任何进展时,才会启动动态符号执行;Wang等人[64]提出了一种“最佳转换”策略,分别通过模糊测试和约束执行来量化探索每条路径的成本,并选择更经济的方法探索该路径;Zhao等人[65]提出“区分派遣”策略,基于蒙特卡洛的概率路径优先级模型量化每条路径的难度并排列优先级,将动态符号执行倾斜于最困难的路径计算,从而缓解路径爆炸问题,增加模糊测试效果.

T-Fuzz[14]删除了代码探测路径上的完整性检查,使用符号执行检测删除操作所导致的误报问题;Pak[66]提出“区别调度”策略,通过量化探索每条路径的成本,对路径进行优先级排序,使用协同执行计算优先级最高的路径,从而避免了路径爆炸问题,更经济有效地完成对路径的探索.REDQUEEN[50]基于部分输入直接对应于运行时的内存和寄存器这一观察,通过跟踪程序比较指令中的使用值,创建轻量级的近似污染跟踪,使用快速模糊过程验证是否触发了新的和潜在的行为.

3.3 面向应用场景的改进

为了应对物联网设备的兼容性及性能问题,FIRM-AFL[67]在系统模拟器中模拟的posix兼容固件上应用CGF来解决兼容性问题,并使用一种称为增强流程仿真(augmented process emulation)的新技术将系统模式仿真和用户模式仿真结合起来,兼具了系统模式仿真的高兼容性和用户模式仿真的高吞吐量,从而突破系统模式仿真所导致的性能瓶颈.

Chen等人[68]提出了针对通信协议的有状态协议模糊策略,探索与不同协议状态相关的代码,从而实现更高的代码覆盖率.具体来说,该状态模糊器在测试程序执行时创建多个状态,并识别用于不同状态的包和字段,根据动态执行的模糊效果,选择合适的时间复制协议状态、前进到下一个协议状态或回滚到前一个协议状态,多次重复从而确定最高代码覆盖率的最佳点,实现最大的代码覆盖率和代码执行深度.

4 覆盖率引导的灰盒模糊器评估

模糊测试在挖掘真实软件中的安全漏洞方面取得的巨大成功极大促进了研究者的研究热情.近年来,越来越多的模糊测试策略和算法相继被提出.每当一种新的模糊算法被提出时,必须在实验上证明它优于现阶段的模糊算法.

4.1 主流评估方法

目前,对于模糊器的实验评估,主流方法是:首先,选择一个现阶段先进且性能令人信服的模糊器作为比较对象,近年来,将AFL作为比较对象是众多研究的选择.其次,选择1组用于测试模糊器效果的测试程序.测试程序主要分为实际可用的程序,如:谷歌模糊测试组件、readelf、nm、objdump等开源程序;人工构造的程序,如CGC,LAVA-M数据集,这些数据集都被人工注入了漏洞,以测试模糊器挖掘漏洞的能力.在相同的运行环境下比较需要评估的模糊器和作为比较对象的模糊器在寻找漏洞方面的效率和效果.通常情况下,将找到漏洞的速度、找到新的CVE漏洞数量、程序代码的覆盖率作为评估标准,以证明模糊器的真实有效性.

4.2 重新评估工作

Klees等人[69]对2012—2018年发表的32篇模糊测试论文的实验评估部分进行了重新评估,其结果表明,没有一个模糊测试的评估实验能正确执行所有符合规范的评估流程和评估标准.而评估流程和评估标准的差异性确实可以转化为有误导性或不正确的评估结果.根据Klees等人[69]的工作结果,继续对2018—2019年所发表的模糊器论文的实验评估作统计调查,结果如表1所示:

表1 模糊器实验评估数据总结

测试对象:R表示实际程序,C表示CGC数据集,L表示LAVA-M基准,G表示谷歌fuzzer测试套件.

性能方差:不同轮数的模糊测试的性能差异方差.A表示置信区间.

崩溃分类:S表示用堆栈哈希对相关崩溃进行分组,O表示用其他工具/方法分类,B表示用覆盖率信息对崩溃分类,F表示用现实依据进行崩溃分类.

覆盖率指标:L表示行/指令/基本块覆盖,M表示方法覆盖,E表示控制流边或分支覆盖,Z表示其他覆盖信息.

种子:H表示随机取样种子,M表示手工构建种子,T表示自动生成种子,N表示未预设有效性的非空种子,V表示预设有效的种子,但不清楚该种子的获取途径,Y表示空种子,/表示在不同的程序用了不同的种子.

4.3 存在问题

尽管研究者都尽量设置了客观有效的实验以增加结果的可信度,但由于缺乏统一的评估流程、评估指标,还是难以在现实中用统一维度证明这些模糊器的进步性.主要问题分析如下:

1) 参考对象选择不统一.尽管大部分研究都选择将AFL作为比较对象,但仍有一些选择了其他参考对象,如REDQUEEN[50].且近年来众多模糊器都是在AFL的基础上进行改进,一些研究会倾向于选择与更先进的模糊器进行比较,以证明取得的进步.由于参考对象选择范围的广阔性,充当参考对象的模糊器在各评估指标上表现的差异性,客观的参考标准难以确定.

2) 测试程序选择不统一.通常情况下,模糊算法的进步性在测试程序上得到验证后,希望其性能优越性能在现实生活中的大部分程序中体现出来.即模糊器在测试程序上的良好性能能转化为总体上的良好性能,这就要求测试程序具有一定的代表性.目前没有代表性的测试基准能满足这些要求,因此不同的模糊器往往会自主选择测试程序.部分研究会倾向于选择模糊器表现优异的程序进行测试,这使得客观评判模糊器在现实运用中的真实性能更加困难.

3) 测试时间设置不统一.目前模糊器的超时时间设置通常从1 h到几天或几周不等,常见的选择是24 h,5 h,6 h.大多数研究都没有给出合理的超时报告.然而,算法之间的相对性能会随着时间的变化而变化,较短的运行时间可能会造成误导,产生不完整的结果.

4) 测试轮数选择不统一.由于模糊测试本身的随机性,对测试程序的每次模糊测试都可能产生不同的结果.所以,仅仅进行1轮测试就比较它们的性能是不够的.

5) 初始种子的选择不统一.不同的初始种子会造成差异化的测试结果.然而,大多数研究对种子的选择很随意,显然认为任何种子都同样有效,且没有提供细节.

6) 评估标准不统一.目前,直接评估模糊器所挖掘的漏洞数量是主流方法.然而,也有部分研究使用代码覆盖率或程序崩溃的去重输入数量评估模糊器的性能改进.

4.4 建议方法

如表1所示,没有研究完全遵循评估准则,因此对CGF的评估流程提出了如下建议:

1) 所有测试应该持续至少10轮,且统计其总体方差,用统计学相关知识正确评估模糊测试算法性能,防止随机性.

2) 每轮测试应该维持足够长的时间,并绘制发现bug数量随时间变化曲线,对比不同模糊测试算法性能.

5 总结与展望

CGF作为目前流行且发展潜力巨大的漏洞挖掘工具之一,其未来的发展前景是值得期待的.本节大胆猜想了未来可能的发展方向,以供参考.

5.1 智能化的进一步发展

通用的CGF技术已经成熟,并且在程序的初期漏洞检测中取得了较大的成功.然而,随着CGF所应用的领域越来越广,测试程序的输入格式越来越复杂和多样化,针对具体应用场景实行专业化的智能指导型CGF有待进一步发展.结合相关应用场景的理解和特征分析,CGF能对模糊过程实现更精确的控制,更快更准确地挖掘程序中的深度漏洞.目前,传统的静态和动态分析对CGF的性能提升已经发展到了瓶颈期,更轻量级的替代技术或模糊策略还在持续提出.同时,随着机器学习的发展,机器学习技术也将在与CGF的结合中发挥更重要的作用.

5.2 统一评估系统的建立

如第4节所述,不统一的评估流程、不同的测试程序和评估指标都会给评估结果带来误差或错误.这些误差或错误不仅给模糊器间的性能比较带来困难,同时也可能误导相关研究者相信误差后的性能效果,从而投入更深的研究中,造成研究人力的浪费.因此,建立一套统一的、可靠的模糊测试评估系统是有强烈现实需求的.评估系统可通过包含足够数量、各种大小的程序来体现现实程序的多样性,同时设置统一的评估指标对不同的模糊器进行同维度的比较.

5.3 模糊测试的可解释性和量化性

目前,模糊测试技术的主要创新研究集中在如何更有效地挖掘漏洞上,对于模糊测试实际应用中的可解释性和量化性的研究还远远不够.例如:经过模糊测试后,程序的安全性究竟能得到多大程度的保证;如何有效地评估停止模糊测试后的剩余风险,即攻击者找到模糊测试没有找到的漏洞的风险;如何判断停止模糊测试的合理时间.目前,上述判断都依赖于研究者的个人经验,而不能根据测试过程中观察到的程序行为进行统计上有根据的推断.因此为未发现的漏洞设置合理的剩余风险阈值,并用可行的统计框架对剩余风险进行量化是解释和量化模糊测试的必要工作.Böhme[78]提出了一个总体框架,可作为解决这一挑战的一个起点,并讨论了未来研究的具体机遇.

5.4 分布式技术与CGF的结合

随着分布式技术的不断发展,当前分布式技术已经能够对大量计算单元进行整合,完成大规模的问题求解任务.如何变异出更多有效的测试用例,以及CGF在种子变异过程中产生的海量测试用例,一直都是制约CGF性能的关键因素.将分布式技术与CGF相结合,通过大量分布式计算机并行执行模糊测试任务,可以有效提高CGF在输入生成阶段和程序执行阶段的性能.

猜你喜欢
测试用例覆盖率漏洞
民政部等16部门:到2025年村级综合服务设施覆盖率超80%
漏洞
我国全面实施种业振兴行动 农作物良种覆盖率超过96%
基于LDA模型的测试用例复用方法*
基于selenium的SQL注入漏洞检测方法
侦探推理游戏(二)
电信800M与移动联通4G网络测试对比分析
软件测试中的测试用例及复用研究
漏洞在哪儿
测试工时受限的测试策略研究