- 【参考文献】Zhengxiong Luo, Junze Yu, Feilong Zuo, Jianzhong Liu, Yu Jiang, Ting Chen, Abhik Roychoudhury, and Jiaguang Sun. Bleem: Packet sequence oriented fuzzing for protocol implementations. In 32nd USENIX Security Symposium (USENIX Security 23), pages 4481--4498, Anaheim, CA, August 2023. USENIX Association.
- 【所属团队】清华大学软件学院软件系统安全保障小组
- 【团队网站】http://www.wingtecher.com/
- 【注】本文仅为作者个人学习笔记,如有冒犯,请联系作者删除。
目录
[2、Protocol Fuzzing](#2、Protocol Fuzzing)
[2.1、Mutation-based Fuzzers](#2.1、Mutation-based Fuzzers)
[2.2、Generation-based Fuzzers](#2.2、Generation-based Fuzzers)
[3、System Overview](#3、System Overview)
[3.1、System Under Test (被测系统,SUT)](#3.1、System Under Test (被测系统,SUT))
[3.2、Test Initialization](#3.2、Test Initialization)
[4、Feedback Collector](#4、Feedback Collector)
[5、Guided Fuzzing](#5、Guided Fuzzing)
[5.1、Mutation Operators](#5.1、Mutation Operators)
[5.2、System State Tracking Graph](#5.2、System State Tracking Graph)
[5.3、Guided Sequence Generation](#5.3、Guided Sequence Generation)
[5.4、Packet Instantiation](#5.4、Packet Instantiation)
[6.2、Experiment Setup](#6.2、Experiment Setup)
[6.3、Coverage Analysis](#6.3、Coverage Analysis)
[6.4、Bug-Detection Capability](#6.4、Bug-Detection Capability)
[6.5、Effectiveness of Sequence Generation](#6.5、Effectiveness of Sequence Generation)
[7、Related Work](#7、Related Work)
Abstract
- 在对协议进行模糊测试时,存在无效的反馈机制和协议状态空间探索不充分的缺陷。
- 本文提出BLEEM,一个面向数据包序列的墨盒模糊测试工具。BLEEM不针对单个数据包的生成,而是生成序列级的数据包。
- BLEEM通过分析系统输出序列提供有效的反馈机制,通过采用包括所有相关方在内的状态空间跟踪及时引导模糊测试,并利用交互式流量信息生成具有协议逻辑感知的数据包序列。
1、Introduction
- 传统模糊测试工具存在以下问题:
- 生成无效的数据包,浪费资源。传统的fuzzers基于预定义的测试模型生成数据包,没有程序反馈。因此,fuzzers不知道生成的输入是否触发了新的程序状态。
- 现有的解决方法有:
- 识别成功的请求。通过代码覆盖率或从服务器相应提取的状态码。
- 保留请求成功的数据包,对其进行变异操作,再输入到系统中进行测试。
- 这些方法迁移性差,不能跨协议实现。
- 这些反馈机制需要分析源程序或二进制文件,不能实现黑盒测试。并且这些方法的实现与协议格式紧密相关,不适合测试各种协议。
- 一些协议的特定验证规则可能使现有的演化过程失效。例如,一些常见的协议,如TLS、DTLS和SSH,在握手过程中使用随机的nonces来防止"重放攻击",即攻击者通过拦截、记录和再次发送先前的通信数据包来欺骗系统。在这种情况下,对先前请求成功的数据包进行变异操作所获得的新数据包就不可能产生有价值的结果。
- 现有的解决方法有:
- 由于协议是有状态的,被测系统的输入空间受其状态的严格要求。有效地遍历状态空间并覆盖各种状态转换需要精心设计的数据包序列。构造这样的数据包序列涉及到复杂的协议逻辑,例如,按照什么顺序传输数据包以及如何构造它们以保证格式和参数的正确性。
- 现有的解决办法有:
- Peach采用用户定义的协议模型,并严格遵循该模型中描述的操作来生成数据包序列。虽然模型涉及的协议逻辑可以得到有效覆盖,但不能执行超出模型范围的逻辑。
- AFLNet通过对现有数据包进行变异来生成数据包序列。由于不了解协议格式,这些方法在处理高度结构化数据包的协议时难以提供有效的测试用例。
- 现有的解决办法有:
- 生成无效的数据包,浪费资源。传统的fuzzers基于预定义的测试模型生成数据包,没有程序反馈。因此,fuzzers不知道生成的输入是否触发了新的程序状态。
- 本文提出BLEEM,一个面向数据包序列的墨盒fuzzer,以解决上述问题。
- 首先,本文引入了一种通过分析系统输出的动态反馈机制。协议实现的输出可以抽象出内部协议状态。因此,我们在运行时收集目标系统的输出序列,分析输出中蕴含的语义,从而获得系统内部的状态转换。
- 其次,我们利用这些反馈来指导数据包序列的生成。我们设计了系统状态跟踪图SSTG,它在运行时动态构建,并允许BLEEM绘制已经探索的状态空间。SSTG能够为达到所需状态提供指导,并且引入了用于单个或序列级数据包的突变操作符,支持生成不同状态下的数据包。同时,我们利用从协议的交互流量中提取的信息来生成数据包序列。我们观察到由于协议逻辑是在服务器和客户端中实现的,我们可以根据双方交换的流量生成具有协议逻辑意识的数据包序列,从而保留参数依赖性,避免生成无意义的数据包。
- 本文贡献如下:
- 设计了一种通用的方法,通过分析系统输出序列来收集反馈。
- 设计了识别状态空间的SSTG来引导模糊测试,并提出了一种基于先验信息的协议逻辑感知的数据包序列生成办法。
- 实现了BLEEM并在广泛使用的协议实现上进行评估。结果表明,BLEEM优于最先进的技术,并且已经检测到许多安全关键漏洞。
2、Protocol Fuzzing
- 传统的协议模糊测试侧重于测试服务器,即Fuzzer充当客户机,不断生成数据包并将其发送到服务器。根据数据包的产生方式,这些Fuzzers可以大致分为两类:基于突变的和基于生成的。
2.1、Mutation-based Fuzzers
- 基于突变的Fuzzers通过随机改变从种子池中选择的测试用例来生成新的测试用例。它们不需要事先了解协议规范和消息格式,因此易于实施。
- 这些Fuzzers擅长测试无状态程序,如文件处理应用程序。为了实现测试,研究人员通常只针对特定的服务器状态进行测试,或简单地将发送到服务器的输入串联在一起。、
- 为了获得更好的性能,AFLNet在传统的fuzzers中增加了反馈回路,使fuzzers能够跟踪每个输入的执行信息,并保留有趣的输入以供进一步利用。此外,它还通过分析服务器响应中的状态码来感知状态。然而,面对高度结构化数据包的协议时,这种随机变异生成数据包的测试方法就变得不适用了。此外, 这些反馈机制也不适用于黑盒模糊测试。
2.2、Generation-based Fuzzers
- 基于生成的Fuzzers通过人工构建的协议模型来生成数据包,包括数据模型和状态模型。状态模型通常以图的形式给出,并指定了与服务器交互时的有效消息顺序。数据模型描述了相应状态的消息的格式(例如,字段类型、字段大小和有效值范围)。用户需要了解协议交互逻辑,通过分析源代码或阅读协议规范来构建该测试模型。
- 以QUIC为例。
- 图1描述了QUIC的客户机和服务器之间的对话。
- QUIC客户端首先发送一个Initial[CRYPTO]数据包(Initial[CRYPTO]表示该数据包为Initial类型,并且包含一个CRYPTO帧)。
- 服务器响应初始[CRYPTO, ACK]和Handshake[CRYPTO]数据包,其中包含握手建立所需的基本信息。
- 然后客户端发送初始[ACK]和Handshake[CRYPTO, ACK]数据包,表示握手完成。
- 为了简化,我们假设服务器在握手后通过1-RTT[APPLICATION_CLOSE]终止连接。
- 图2以Peach Pit的格式描述了QUIC的握手模型,Peach Pit是Peach的XML配置文件。
- 第16-27行展示了Initial[CRYPTO]数据包的数据模型,它被状态模型(第2-12行)中的第一个动作(第3-5行)发送。
- 具体来说,这个状态指定了Peach如何与QUIC服务器进行握手测试。首先,发送Initial[CRYPTO]数据包(第4行);然后等待服务器的响应,并检查响应是否符合数据模型给出的Initial[CRYPTO, ACK]和Handshake[CRYPTO](第7行)数据包的格式。如果是,就执行第三个动作,发送Initial[ACK]和Handshake[CRYPTO, ACK]数据包。(图2中省略了其他类型数据包的数据模型)
- 图1描述了QUIC的客户机和服务器之间的对话。
- 虽然基于生成的fuzzers可以检测出大量漏洞,但它们的有效性高度依赖于用于提供的协议模型质量,并且这些协议模型的构建通常需要大量的专业知识。
- 首先,这些规范容易被误解,因为它们通常是用自然语言表达的,因此具有固有的歧义性。
- 其次,协议通常具有庞大的状态空间,因此很难构建一个完整描述所有协议行为的模型。
- 例如,图2只描述了QUIC的握手逻辑,而没有涵盖其他逻辑,比如流量控制。不幸的是,这些模糊测试工具通过严格遵循协议模型中描述的操作(图2,第1-14行)来进行测试,并且它们不会在运行时更新状态模型。因此,这些模糊测试工具无法识别已经确定的模型之外的新的有趣程序行为,从而无法进一步探索这些新路径。
- 例如,在图2中,假设Peach的设置中,一些有趣的Initial[CRYPTO]数据包意外触发了一个新的程序行为,但由于服务器响应了另一个没有在数据模型中定义的数据包,因此Peach会将其丢弃,并继续执行确定性的测试行为,也就不会基于此发现探索更有趣的行为。
- 最后,一些协议实现中的非法行为可能会引发执行错误。这些行为违反了标准,并且不包括在协议规范中。因此,这些场景不能被基于协议规范构建的模型所涵盖。
2.3、Challenges
- 为了设计一种高效的模糊测试方法,可以针对不同协议进行测试,我们需要克服两个挑战。
- 缺乏可扩展的反馈机制。
- 尽管反馈驱动的方法已经显示出在优化模糊测试方面的有效性,但协议实现的多样化应用使传统反馈机制的可扩展性受到阻碍。如今,协议实现已广泛应用于工业可编程逻辑控制器(PLC)设备和物联网(IoT)设备中。当实现仅以黑盒方式访问时,检测设备的固件是具有挑战性的,这就需要一种非侵入性的解决方案来获取协议fuzzers的反馈。
- 高度复杂的协议逻辑。
- 协议具有高度复杂的逻辑,旨在保证各种情况下的正确性和可靠性,从而使实现成为高度复杂和有状态的交互目标。因此,要深入实现模糊测试,需要仔细考虑协议逻辑,包括数据包格式、数据包之间的参数依赖关系和数据包交互逻辑,来构造数据包序列。
- 其中,数据包格式表示标准的数据包结构,数据包交互逻辑表示不同类型数据包的传输顺序,参数依赖关系表示数据包之间交换参数的依赖关系。
- 例如,在图1中,第一次握手发送的Initial[CRYPTO]数据包和第二次握手发送的Initial[ACK]数据包携带的参数DCID应该为相同的值,DCID是一个连接标识字段。否则,握手将会失败,使下面的测试操作变得毫无意义。
- 缺乏可扩展的反馈机制。
3、System Overview
- 图3展示了BLEEM的概述:给定目标协议实现的被测系统 (SUT), BLEEM使用初始化的测试进行初始化,并通过数据包序列引导的模糊测试输出Bug报告。
3.1、System Under Test (被测系统,SUT)
- 被测系统是我们要分析的协议实现。这类协议通常遵循客户端-服务器模式。传统模糊测试在测试协议时,主要针对协议的一端。例如,为了测试协议的服务器端,传统fuzzer会充当客户端,不断向服务器端发送数据包进行测试。
- 在这种情况下,有效的模糊测试就需要大量专业知识的参与。即在服务器的不同状态下以什么顺序发送数据包,以及如何构造参数正确的数据包。与从头开始手动实现协议逻辑不同(Peach Pit),我们认为客户端已经集成了所需的协议逻辑,可以为生成数据包提供帮助。
- 具体来说,BLEEM需要一对相互通信的客户端和服务器,并将其视为整个被测系统。BLEEM可以与SUT中的各部分进行交互。
3.2、Test Initialization
- 与现有的黑盒模糊测试需要手动构建协议模型不同,BLEEM通过捕获SUT的对话框开始模糊测试。当然,直接启动SUT测试协议是不可能发现漏洞的,因为这样合规的案例应该已经在预发布测试中覆盖了,我们需要为双方生成不同的变体作为输入。为此,BLEEM会与服务器和客户端直接交互,通过拦截双方通信流量,并引入精心制作的数据包进行测试。
- 因此,在测试初始化阶段,我们需要为BLEEM提供服务器的地址(例如,套接字地址),并为连接客户端配置一个不同的地址。通过这种方式,BLEEM可以收集SUT中双方发送的数据包。然后,这些数据包被用于SUT内部状态分析,并作为数据包生成的基础。BLEEM利用Scapy来分析拦截的数据包。对于不受支持的协议,我们可以通过识别协议格式(即数据模型)来扩展Scapy,这比需要数据模型和状态模型的传统生成式fuzzers更易于实现。
- 【注】Scapy 是一个功能强大的 Python 库,用于交互式数据包操作、网络协议分析和网络嗅探。它允许用户创建、发送、捕获和分析网络数据包,支持构建各种网络协议的数据包,并提供了灵活的接口来实现自定义网络协议的解析和操作。
3.3、Workflow
- 如图3所示,BLEEM由两部分组成:反馈收集器和引导模糊测试模块。
- 在每次迭代时,反馈收集器先捕获SUT执行期间的网络流量。然后,对流量进行分析,提取消息语义,构造抽象数据包序列,并将抽象数据包序列转换为包含客户端和服务器的状态跟踪,作为SUT反馈传递给引导模糊测试模块。
- 然后,引导模糊模块将状态跟踪合并到**系统状态跟踪图 (SSTG)**中,细化SSTG的遍历概率分布,并应用引导模糊策略选择突变算子,生成待实例化的数据包序列。
- BLEEM使用在SUT中的初始会话来初始化SSTG,并使用在反馈中发现的新状态或新转换来动态更新它。然后BLEEM根据SUT双方之间交换的数据包实例化数据包序列。由于这些数据包通常在语法和语义上都是正确的,故基于它们生成的数据包序列有很高的概率去探索协议的深层逻辑。
4、Feedback Collector
- 为了增强BLEEM在模糊测试时的获取反馈能力,我们提出了基于系统输出来获取系统反馈的方法,而不是侵入性地检查系统。协议实现时输出的数据包可以作为协议内部状态的指示器。
- BLEEM的工作方式如图4所示。
- 过滤数据包序列(Filtering the Packet Sequence)
- 表示客户端发送的第一个数据包, 表示服务器接收到的第一个数据包。
- 在模糊测试过程中,我们从捕获的流量中,挑选出与SUT状态相关的数据包以组装成数据包序列S。In detail,我们只考虑SUT中服务器和客户端发送的数据包(如 ),而不考虑它们收到的数据包(如 ),因为这些数据包是BLEEM发送的测试用例。
- 抽象数据包序列和处理(Abstracting the Packet Sequence and Processing)
- 为了表示SUT的状态,直接使用具体的数据包可能会造成混淆,因为有些字段(如data字段)与系统状态的关联很低,而且它们的可能值有时是无限的。例如以图2的Initial[CRYPTO]数据包为例。用于标识连接的DCID字段的值(第23行)是在连接开始时随机生成的。考虑这个地方会使状态空间过大或无限大。因此,我们需要抽象掉一些细节,把重点放在数据包所携带的关键语义信息上。
- 我们将S中的数据包逐个进行抽象,得到基本抽象数据包序列π。对于每个数据包 ,我们使用Scapy对其进行分析并得到解析 。解析结果是一个按层次组织的字段列表,其中每个字段表示为类型-值对。基于解析结果,该模块通过保留具有特定类型(即枚举类型)的字段,来构建相应的数据包抽象 。
- 通过这种方式,BLEEM将QUIC的第一次握手(图1中的1,图2中的第16-27行)抽象为 Initial[CRYPTO],保留了数据包类型Initial和帧类型CRYPTO以及它们之间的层次关系。
- 此外,BLEEM在一个Oracle map 的数据结构中缓存了每个抽象数据包 和它对应的最近的具体数据包P的映射。
- 为了方便构建状态跟踪,需要进一步处理π。我们将来自同一发送端的相邻抽象数据包连在一起,得到ω。例如,图1中第二次握手的两个数据包可以被抽象并连接为Initial[CRYPTO, ACK]+Handshake[CRYPTO]。因此,在最终的抽象数据包序列π中,相邻的两个抽象数据包是由不同的协议方发送的。
- 构造状态跟踪(Constructing the State Trace)
- 给定一个抽象数据包序列 ,每个抽象数据包 只能表示SUT中对应 的时间状态,而不能表示整个SUT。这是因为可能有多条路径可以到达 的这个时间状态。例如,客户端在某一点向服务器发送了数据包a或数据包b(a和b是不同类型的),并且这两个数据包都是服务器不能识别的。在这种情况下,服务器将用相同的错误消息c来响应它们。当服务器发出数据包c时,这两个不同路径下的整个SUT状态是不同的。
- 因此,我们将协议模糊视为一个双向SUT,并通过引入一些先验信息对其状态进行建模。然而,考虑完整的历史记录可能会导致状态空间的爆炸。我们发现,仅考虑双向通信中最重要的内容足以在表示整个SUT的时间状态,这能最小化整体状态跟踪空间,从而实现实际平衡。
- SUT状态
- SUT状态包含了SUT的双方,可以表示为 对象的形式。
- 每个对象都是从 中抽取出来的 ,其中,C代表示客户端,S表示服务器,ω是一个抽象的分组字母表。
- 顺序也很重要, 表示T1发送ω1响应T2发送的ω2请求。注意,ω2只记录T2最近发送的抽象数据包。
- 这样,我们可以通过分析π中的每一对相邻的 和 来推断SUT的状态。然后,将两个相邻SUT状态之间的转换相加,形成状态跟踪。
- 我们在图1中使用带有5个符号的抽象数据包字母表W来表示所涉及的SUT状态:
- SUT的第一个状态可以表示为 ,表明服务器没有发送数据包,而客户端响应了一个(Initial[CRYPTO])。
- 同样,第二个状态可以表示为 。
- 这样,整个对话的状态跟踪就为:
5、Guided Fuzzing
- 借助反馈收集器,BLEEM能够跟踪SUT的状态转换,并确定SUT是否达到了一个新的、以前未遍历过的状态。我们设计了SSTG来表示已探索的状态空间。依据SSTG,我们引入了协议感知的变异操作符,以支持不同状态的多样化数据包生成。我们还扩展了SSTG,使其能提供达到所需状态的引导能力。我们设计了一种引导性的序列生成策略,以有效地探索状态空间,并利用数据包实例化模块来提供高质量的数据包序列。
5.1、Mutation Operators
- Packet-Level Mutation Operator
- 该突变策略对单个数据包中的字段进行操作。对于给定的数据包,它随机选择几个字段,并根据字段类型进行相应的突变操作。
- Scapy已经确定了五种一般的字段类型,包括NumberField、StringField、ListField、EnumerationField和LengthField。我们根据字段的特点设计不同的变异策略。
- 例如,NumberField突变操作符在考虑有效值范围的同时对原始值执行随机的加减操作。
- Sequence-Level Mutation Operator
- 该变异策略是对序列中的数据包进行操作。有两种策略:
- Packet duplication(重复)
- 给定一个数据包序列,从中随机选择一个数据包 ,并将其复制几份。如图5所示,将数据包P2复制了两次。
- 【注】Note that 序列中的后续数据包在可能会发生变化,我们引入符号"F"来表示位于被操纵数据包之后的数据包。在该案例中,引入重复数据包后,实际流量中就可能不存在P3了(可能发送的是错误包)。我们用F5表示突变序列中的第五个包。
- Packet disordering(无序)
- 给定一个数据包序列,从中随机选择一个数据包 ,并修改它的发送顺序。如图5所示,让数据包P4在P1后发送,即删除P2,P3。
- Packet duplication(重复)
- 这两种变异操作可能引入动态数据包延迟或丢失。具体来说,Packet disordering操作可以发现由携带命令的数据包的异常顺序所触发的异常。
- 例如,一组文件传输协议 (FTP)用户的合法命令序列:[USER test, PASS test, STOU test.txt, . . . , DELE test.txt],表示FTP用户先以用户名test和密码test登录到FTP服务器,然后上传一个文件test.txt,执行一些操作,最后删除它。
- 由于可能存在攻击者的恶意行为,FTP服务器应该能够正确处理错误的命令序列,例如删除序列中的"PASS test"尝试绕过验证。
- 该变异策略是对序列中的数据包进行操作。有两种策略:
- Formalization
- 为了方便表示,我们可以将Sequence-Level Mutation Operator 看作是Packet-Level Mutation Operator 。例如,图5a中的Packet duplication 可以看作是对P2的操作;图5b中的Packet disordering可以看作是对P2的操作,即用Oracle Map中缓存的P4替换P2。
- Based on this premise,我们引入一个符号⊕来将数据包P和变异操作σ结合起来。这样,P⊕σ就可以表示为使用σ对P进行变异所产生的包。此外,我们引入 来表示不执行突变操作。因为任何突变算子σ具有随机性,所以生成的数据包通常是无限的。
5.2、System State Tracking Graph
- 我们设计了SSTG来识别SUT的状态空间。
- Labeling the State Transitions
- 为了从中feedback collector获得状态跟踪,BLEEM会标记每个转换的触发条件。BLEEM不像现有方法那样直接保留具体的数据包,而是记录如何使用相应的抽象数据包ω和选择对其进行操作的突变算子σ(即ω⊕σ)来生成这些数据包。当BLEEM根据从SUT双方接收到的数据包生成数据包时,可以从源SUT状态中提取抽象数据包ω。对应的变异算子σ可以从BLEEM的执行记录中得到。
- 例如,我们标记公式2的状态轨迹,不进行变异。如图6所示。SUT从状态q0开始,此时客户端发送数据包a;BLEEM接收到a后,通过 改变a,即 。生成的数据包作为SUT的输入,SUT变为状态q1。
- Merging the State Trace
- 我们使用SSTG通过不断合并状态踪迹来拓展探索的状态空间。
- SSTG的定义
- SSTG可以用5元组 表示。其中是SUT状态的有限集合,是初始状态,是一个抽象数据包的字母表,是一组突变算子,定义了一个转换函数,其中P(Q)是Q的幂。
- BLEEM使用初始对话框中定义的状态跟踪初始化SSTG。例如,如果BLEEM根据图1中的对话进行模糊测试,图6为初始的SSTG,,为公式1。
- LEEM在模糊测试期间与SUT各方进行交互,并为SUT各方生成各种数据包,而不是直接转发接收到的数据包。由于BLEEM基于通信流量进行模糊测试,因此每次迭代的启动都由SUT的第一个动作驱动。例如,基于图1的对话框进行模糊测试,客户端总是在每次迭代开始时向BLEEM发送Initial[CRYPTO]数据包。区别在于BLEEM如何改变接收到的数据包等。因此,为了将状态跟踪合并到已经实现的SSTG中,BLEEM从SSTG的启动状态开始,合并它们的共享节点或转换,并用状态跟踪中发现的新状态或状态转换更新SSTG。
- 此外,BLEEM实现了一个功率调度,该调度动态地细化了合并过程中执行的转换的优先级。对于一个已执行次数为 的转换 ,其优先级由 计算,其中h(x)是一个递减函数。也就是说,在合并期间新添加的转换将被分配最高优先级。
- SSTG捕获了协议实现的已执行状态空间,并为状态空间遍历提供了一种高效的方式:给定一个要达到的目标状态 ,从 到 的任何路径上的转换标签形成一个数据包模型序列,为SUT输入的生成提供指导。
5.3、Guided Sequence Generation
- 在SSTG的引导下,该模块输出数据包模型序列,并将其传递给数据包实例化子模块,作为数据包序列生成的指南。
- 算法1提供了该过程的概述(Algorithm 1 provides an overview of the process)。
- 首先,我们尝试使用推荐的变异操作(行1-4)对每个SSTG的状态进行测试。具体而言,对于一个SUT状态,尽管状态输出 是确定的,但我们可以选择不同的变异操作 来构造不同的数据包模型 作为输入。因此,我们首先需要检查是否有状态q能通过其它未使用的数据包模型转换得到(行1)。如果有,我们构造一个可以到达q的数据包模型序列(行4),然后在 中添加需要的数据包模型。例如,对于图6中的SSTG,其中(变异算子),如果用一个新的数据包模型来测试q1,该数据包模型将q1的输出b与未使用的变异操作 结合起来,即 ,那么我们可以构造这样一个数据包模型序列:。
- 其次,在对所有SUT状态进行测试后,我们对已实现的SSTG进行全面遍历。我们从初始状态开始(第7行),然后循环运行直到结束(第8行):在每一步中,我们从相应状态的可用转换中选择优先级最高的一个(第9行),记录其标记的数据包模型(第10行),然后执行该转换(第11行)。
- 值得注意的是,即使我们曾经对状态S应用过一个数据包模型P进行测试,且没有发现新的行为,但还是会尝试在相同状态上应用P。因为P能够生成的数据包集合通常是无限的。
5.4、Packet Instantiation
- 对于给定的数据包模型序列 ,数据包实例化子模块生成符合的具体数据包序列作为SUT的输入。
- 由于数据包模型序列是抽象的,为了实例化它们,同时要保证它们的语法和语义正确性。因此,该模块利用了编码在SUT各方内的协议逻辑,并且作为代理运行,同时与客户机和服务器交互,从而使用它们交换的数据包并引入精心制作的数据包。
- 在φ中,相邻的数据包模型是为SUT中不同方准备的。数据包实例化子模块会逐个实例化它们。例如,对于要被实例化作为服务器输入的数据包模型 ,数据包实例化子模块会拦截来自客户端的数据包P(如果有多个数据包,则将它们连接成一个),并检查P是否符合 的结构:
- 符合,直接将P作为 的实例化,并对P执行对应的变异操作 以生成测试数据包。
- 不符合,表示SUT的真实内部状态和设计的状态不同,可能存在两种情况:
- 发现了一些新的状态或状态转换。
- 由于SSTG固有的非确定性,某些转换未如预期那样进行,导致了不同的状态跟踪。
- 在这些情况下,为了在对已实现的SSTG进行全面遍历和对未知状态空间进行有效探索之间取得平衡,BLEEM随机选择以下两种策略来生成具体的数据包:
- 忽略,直接在P上执行。
- 借助Oracle Map找到符合的数据包,并在其上执行。
6、Evaluation
- 本节,我们实现和评估了BLEEM,以回答一下三个研究问题:
- BLEEM是否比传统fuzzers更高效?
- BLEEM在寻找真实协议的未知漏洞方面是否有效?
- 数据包序列引导生成策略是否对BLEEM有帮助?
6.1、Implementation
- 我们使用python3实现了BLEEM。
- Feedback Collector
- 我们基于Scapy提供的数据包分析能力来对收集到的数据包进行分析,Scapy是一个支持140多种常用协议的数据包操作库。
- Scapy给定具有详细字段类型、值以及结构信息的解析结果,我们通过在保持数据包结构的同时保留枚举字段值来抽象每个数据包。然后将相邻的具有相同来源的抽象数据包连接起来,构造抽象数据包序列。
- 此外,为了展示BLEEM的可扩展性,我们还选择了Scapy不支持的两种协议,i.e.,QUIC和SSH进行实验。
- Guided Fuzzing Module
- First,我们实现了两个层级的变异算子:
- 为了支持数据包级的突变,我们设计并实现了Scapy中使用的五种一般字段类型的突变策略。
- NumberField:在有效范围内,对原数进行随机加减操作或随机选择一个数字。
- LengthField:描述了引用字段的长度。除了继承了NumberField的操作,还将原数替换为特殊值(如0,负数,最大值和最小值)。
- StringField:在原始字符串上执行字符串拼接、子字符串复制和子字符串删除的有限组合。
- ListField:描述了包含具有相同类型的项的列表的字段。执行元素重复、元素重复、元素删除、添加元素(基于Oracle Map语料库)以及打乱原始列表元素顺序。
- EnumField:表示从给定枚举集中获取可能值的字段。例如,在HTTP中,Method字段可能的值有 [" GET ", " POST ", " HEAD ", ...] ,这些是枚举字段。EnumField突变操作符以高概率从枚举集中选择一个值,也以低概率提供枚举集之外的值。
- 基于Oracle Map执行序列级的突变,Oracle Map在每个抽象数据包和其对应的最近的具体数据包之间缓存了一个中间映射。
- 为了支持数据包级的突变,我们设计并实现了Scapy中使用的五种一般字段类型的突变策略。
- Second,我们通过不断合并抽象数据包序列和细化转换优先级来实现SSTG的运行时构建。在此基础上,我们实现了序列生成策略,以实现高效的状态空间遍历。
- Third,数据包实例化子模块同时与客户端和服务器交互并拦截交换的数据包。
- First,我们实现了两个层级的变异算子:
- Crash Detection
- 除了广泛使用的本地进程监视之外,我们还实现了几个可以远程检测系统崩溃的网络监视器,以支持黑盒模糊测试。
6.2、Experiment Setup
- Subjects
- 我们在两个基准上测试了BLEEM:
- Open-source protocol implementations(开源协议)
- Closed-source protocol implementations(闭源协议)
- 我们还收集了主流物联网制造商的固件,并使用其中包含的协议二进制文件作为闭源目标。
- Open-source protocol implementations(开源协议)
- 我们在两个基准上测试了BLEEM:
- Compared Fuzzers and Manual Configurations
- 由于BLEEM是一个黑盒模糊器,我们选择了学术界和工业界广泛使用的三个著名的黑盒协议模糊器作为黑盒方案的基准,包括Peach、BooFuzz和Snipuzz。此外,为了证明BLEEM的有效性,我们选择了AFLNet和SGFuzz进行比较,这两种最先进的灰盒协议模糊器集成了覆盖和状态反馈。
- 为了使这些fuzzers能进行良好的模糊测试,我们遵循它们的教程来进行配置:
- 对于基于生成的模糊器BooFuzz和Peach,我们通过对SUT中的对话进行建模,为每个实现提供所需的测试模型。具体地说,在测试模型中,我们描述了所涉及的协议消息的格式(数据模型)和它们的顺序(状态模型)。
- 对于基于突变的模糊器Snipuzz、SGFuzz和AFLNet,我们根据执行SUT捕获的消息准备了初始种子池。
- Experiment Settings
- 由于固有的随机性,模糊测试性能会有一定程度的波动,因此我们在每个选定的项目上使用了24小时的时间预算来运行每个模糊测试工具,并重复了每个24小时的实验10次。为了公平起见,每个模糊测试活动都在一个配置有1个CPU核心和1G RAM的Docker容器上运行。
6.3、Coverage Analysis
- 我们使用分支覆盖率作为比较的度量,并利用LLVM的SanitizerCoverage来计算目标程序上每个fuzzer所覆盖的唯一分支的数量。
- 图7显示了不同的fuzzers在服务器端覆盖的分支。结果回答了第一个问题,BLEEM可以实现比现有协议模糊器更高的覆盖范围。
6.4、Bug-Detection Capability
- 为了衡量bug检测能力,我们使用BLEEM对真实协议进行测试,包括开源和闭源。
- Open-Source Targets
- 我们使用AddressSanitizer和UndefinedBehaviorSanitizer(也称为ASan和UBSan)报告的唯一漏洞数量作为统一指标。原因在于,BLEEM和其他模糊测试工具的漏洞检测方法不同。例如,经典的黑盒模糊测试工具Peach通常通过端口探测,检测被测服务的活跃性来发现漏洞。然而,并非所有的漏洞(例如,某些缓冲区溢出漏洞)都会导致程序崩溃。因此,我们利用ASan和UBSan来增强目标程序,并将不同模糊测试工具识别的崩溃作为度量标准,以表示它们的漏洞检测能力。此外,一些由Sanitizer报告的崩溃可能源自相同的原因。为了消除重复项,我们利用Sanitizer报告中的堆栈跟踪进行漏洞去重,仅考虑唯一的漏洞。
- BLEEM已经在几个广泛使用的知名协议的实现中检测到了15个新的漏洞,并在曝光后分配了10个CVE标识符。我们还尝试使用其他模糊测试工具来复现这些漏洞。表1总结了BLEEM暴露的漏洞以及其他模糊测试工具是否能够发现它们。具体来说,Peach、BooFuzz、AFLNet、SGFuzz和Snipuzz分别发现了8个、5个、6个、7个和5个漏洞,而且都是BLEEM发现漏洞的子集。
- Closed-Source Targets
- 我们收集了四个含有易受攻击的协议实现的固件,这些漏洞由CVE数据集披露,来评估所选的黑盒模糊测试工具在发现严重漏洞方面的性能。
- 我们将BLEEM与选定的黑盒模糊测试工具进行了比较,并使用网络相关的监视器通过端口探测检测服务的活跃性来发现崩溃。我们使用首次崩溃时间作为评估这些模糊测试工具漏洞检测能力的度量标准。如表2所示,与其他模糊测试工具相比,BLEEM实现了最佳的CVE发现性能。BLEEM和Peach都能找到所有这些CVE漏洞,而BooFuzz和Snipuzz分别只能找到3个和1个。平均而言,BLEEM至少比Peach、BooFuzz和Snipuzz分别快7.5倍、13.3倍和87.1倍发现崩溃,这证明了BLEEM相对于最新技术的效率提升。
- 结果回答了第二个问题,BLEEM能够在真实协议实现中有效地发现未知的错误。
- Open-Source Targets
6.5、Effectiveness of Sequence Generation
- 为了评估引导序列生成的有效性,我们实现了BLEEMRand,即BLEEM的一个变体,我们用随机序列选择取代它,并保持SSTG结构不变进行比较。表3显示了每个指标的平均值。
- "Paths"列显示在SSTG构建过程中发现的唯一状态跟踪的数量;"Len"列显示这些路径的平均长度;"Types"列显示抽象数据包(连接后)的不同类型数量,它们是SUT状态的元素;"Nodes"和"Trans"列分别表示SSTG的状态和状态转换数量;"Branch Coverage"显示了整个SUT的已达到的唯一分支覆盖率,包括两侧的覆盖率。
- 该表显示,总体唯一路径是有限的,表明我们对SSTG构建的处理有效地避免了状态空间爆炸。
- 从表3的每一行可以看出,我们提出的SSTG的复杂度与数据包类型和覆盖的唯一分支大致成正比,说明我们提出的SSTG可以在一定程度上反映SUT的内部系统执行状态。在引导序列生成策略的帮助下,BLEEM比BLEEMRand平均多实现了5.7%的唯一分支。
- 我们还注意到BLEEMRand在实现的SSTG的复杂性方面在Dnsmasq和accelerate -ppp上表现更好。通过调查,我们发现与其他协议相比,这两个协议对应的SUT的逻辑相对简单。BLEEMRand随机生成的长分组序列可以很容易地触发SUT各方的更多输出,从而导致更复杂的SSTG。但是,由于BLEEMRand在协议实现中没有引导数据包序列生成,很难达到深度状态,因此覆盖的分支比BLEEM少。
- Case Study
- 为了直观地说明BLEEM如何实现指导性模糊测试及其有效性,我们使用在测试mvfst发现的会话作为研究案例。
- 通过执行选定的mvfst(包含QUIC的协议栈)的SUT,BLEEM构建图6的一个初始SSTG。
- 在算法1的指导下,BLEEM尝试使用来测试q1,得到数据包模型序列。然后BLEEM实例化它并触发图8中的会话。
- 突变的数据包触发了客户端重传Initial[CRYPTO]。通过我们的研究,服务器在读取第二个Initial[CRYPTO]后立刻响应了Initial[ACK],而不是将ACK和CRYPTO帧打包到一个数据包里,即Initial[CRYPTO, ACK]。然后,服务器调用CloningScheduler(一个数据包调度器,用于克隆仍然未完成的现有数据包)来派生下Initial[CRYPTO]和Handshake[CRYPTO]。这样,测试过程就覆盖了CloningScheduler的逻辑,其中暴露了一个已知问题。
- 因此,使用新的数据包模型执行q1获得了新的状态跟踪。在反馈收集器的作用下,BLEEM可以自动监控这一点,并在初始SSTG上使用扩展的字母表更新它:
- 产生如图9所示的总体SSTG。
- 不幸的是,触发这样的路径对其他模糊测试工具来说是困难的。
- 实验结果回答了第三个问题,BLEEM的引导序列生成策略增加了不同协议行为的暴露,从而提高了模糊测试的有效性。
7、Related Work
- Protocol Fuzzing
- 模糊测试被常用于测试协议实现。现有方法侧重于单个数据包的生成,而忽视了数据包序列之间的上下文正确性。Peach和BooFuzz每次选择一个状态跟踪并生成数据包。Scapy模糊测试API生成单个数据包,但无法处理参数依赖性或上下文敏感字段。与此不同,BLEEM在序列级别生成数据包,并利用反馈导航协议逻辑探索,同时处理上下文信息。
- 一些最新方法引入协议模糊测试的状态感知。SGFuzz通过识别状态变量并注入仪器来识别服务器程序状态。相比之下,BLEEM利用数据包字段中的枚举来识别数据包类型,并通过双向通信信息构建状态。StateAFL采用编译时仪器获取运行时信息。Nyx-Net通过快照确保无噪音模糊测试。一些方法利用服务器响应优化模糊测试。AFLNet利用状态代码作为状态反馈,而Snipuzz通过分析服务器响应推断语法角色。与特定协议格式紧密耦合不同,BLEEM分析输出语义以获得系统反馈,适用于文本和二进制协议,并分析客户端和服务器输出以获取更多信息。
- State Machine Inference
- 相关工作采用学习算法推断协议状态机,有两种不同的技术。
- 基于主动推理的方法主动生成数据包序列并使用模型学习算法推断状态机。相比之下,BLEEM通过自动语义提取抽象数据包并使用交互式流量实例化数据包。
- 基于被动推理的方法通过分析采样的网络数据包序列来推断状态机。相反,BLEEM根据SUT对话构建初始SSTG,并在引导模糊测试期间逐渐丰富它,构建了漏洞检测的闭环。
- 最重要的是,与传统状态机只描述一个协议方不同,SSTG包含所有协议方。
8、Discussion
- 尽管BLEEM取得了积极的成果,但也存在一些局限性。
- 首先,反馈收集器根据Scapy的解析能力分析输出数据包。但是,Scapy不支持某些协议,特别是那些专有协议。由于BLEEM可以捕获网络流量,并通过模糊测试获得初始会话之外的额外流量,因此我们可以通过基于流量的协议逆向工程来识别不支持的协议。
- 其次,由于SSTG固有的不确定性和粗粒度转换标记,目前的SSTG表示不能总是保证再现性。我们可以通过使用典型算法将SSTG转换为确定性有限自动机,并记录细粒度的突变信息,如突变算子的详细子类和参数,来解决这个问题。
- 第三,BLEEM现在支持ASan/UBSan的崩溃检测和内存相关bug。如果提供了相应的oracle, BLEEM也可以检测语义错误。例如,如果提供了数据包交换约束,BLEEM可以通过分析SUT的状态跟踪来检测不符合协议规范。
9、Conclusion
- 在本文中,我们提出了BLEEM,一个面向数据包序列的协议模糊器,它采用一种进化的方法来探索大量的协议状态空间:它通过分析输出序列来访问系统反馈,并通过应用所提出的引导模糊策略来动态调整探索方向。同时,BLEEM通过利用观察到的交互流量生成高度协议逻辑感知的数据包序列。与最先进的fuzzers相比,BLEEM可以在真实协议实现中实现更高的覆盖范围并检测更多的错误。BLEEM是全自动的,可以黑盒测试大多数通用的协议。