AFLNET:一种针对网络协议的灰盒模糊测试器

robots

 

0x0 摘要

​ 对于网络服务程序的模糊测试是非常困难的。因为与简单的命令行程序相比,网络服务程序具有巨大的状态空间,只有通过精心构造的消息序列才能有效的遍历所有状态。有效的序列是由协议来指定的。在本文种,我们提出了一个用于协议测试的灰盒模糊测试其AFLnet。与现有的针对协议的模糊测试器不同,AFLnet采用了一种新的变异方法,我们使用状态反馈来指导模糊测试。AFLnet语料库种记录的种子是服务器和客户端实际消息交换种产生的数据包。并不需要任务协议规范或消息语法。AFLnet充当客户端,重播发送到服务的原始消息序列的变体,并保留那些能有有效增加代码或这状态空间覆盖率的变体。AFLnet将使用服务器的响应代码识别消息序列执行的后的服务器状态。从这个反馈中,AFLnet能识别状态空间中的渐进区域,并系统地引导覆盖这些区域。AFLnet对两种流行协议实现的案例研究表明,它相比之前最好的模糊测试其也大大提高了性能。AFLnet发现了两个新的CVE,它们都被标记为高危漏洞。(CVSS的评分为9.8高危)

 

0x1 简介

​ 发现网络协议的安全缺陷是至关重要的。在互联网的服务中使用协议,使得用户可以有效和可靠的方式互相交流。协议规定了两个或者多个网络参与者之间可以交换的消息的正确顺序和结构。但是,这种可以从世界任何地方访问服务器的能力也给了远程代码执行攻击的可乘之机。攻击者甚至不需要物理访问该机器。例如著名的“心血漏洞”就是OpenSSL中的一个安全漏洞,用于在SSL/TTL协议中保证通信安全。

​ 然而,发现协议实现中的漏洞是一件困难的事情。对于最先进的模糊测试方法来说也是一种挑战,比如基于覆盖指引的灰盒测试(CGF)和有状态的黑盒模糊测试(SBF)。首先,服务器是有状态的和消息驱动的。它需要一个来自客户端的消息序列(也称为请求),处理消息并发送适当的响应。然而,现实使用的协议可能不完全对应指定的协议。其次,服务器的响应同时取决于当前消息和由早期消息控制的当前服务器状态。与此同时,像AFL这样基于CGF的模糊测试器既无法知道服务器状态信息,也不知道要发送的消息的所需结构或顺序。这些 CGF 模糊器主要设计用于测试无状态程序(例如,文件处理
程序)也就是由当前输入产生一个输出,其中没有维护或考虑程序内部的状态。

​ 开发人员在使用CGF方案模糊测试协议的时候只能使用一些变通的解决方案。在服务器测试中,他们需要为程序特定状态的单元测试编写单独的测试工具,或者将连接消息序列转换为文件并将他们作为种子来进行通常的文件变异模糊测试。这两种方法都有一些缺点。虽然单元测试在某些特定的程序状态下是有效的,但它可能无法彻底测试几个程序状态之间的交互/转换。此外,通常需要大量努力来编写新的测试工具,以保持正确的程序状态和避免误报。最关键的是,在服务器模糊测试中不是总能获取到其源代码,也就是不适用于端到端的测试。

​ 使用将连接消息作为文件变异的方法会导致查找错误的低效率和无效。首先,对于每次模糊测试的迭代,都需要更改整个选定的种子文件。给定一个从$m1$到$m_2$一系列消息连接而成的文件$f$,CGF变异整个文件$f$并平等对待所有消息。假设存在一条消息$m_i$是最有趣的一个(例如探索它会获得更高的程序覆盖率和潜在的程序错误状态),CGF在处理到$m_i$之前一直在重复变异从$m_1$到$m{i-1}$的无趣的消息,它没有意识到应该专注于$m_i$。其次,由于缺乏状态转换信息,CGF 可能会产生许多可能被 SUT 拒绝的无效消息序列。事实上,AFL 的用户很清楚这些限制,所以他们向其开发者的开发者组发送了几个请求和问题。图 1 显 示了来自 AFL 用户的两个请求有状态模糊支持的请求。

​ 由于上述 CGF 对有状态服务器模糊测试的限制,最流行的技术仍然是有状态的黑盒模糊(SBF)。学术界(如SUlley、BuoFuzz)和工业界的(Peach、beSTORM)都开发了一些SBF工具。这些工具以有限状态机或图遍历协议模型,并利用该状态下接受的消息的数据模型/语法来生成(语法上有效的)消息序列来对 SUT 进行压力测试。然而,它们的有效性在很大程度上取决于给定的状态模型和数据模型的完整性,这些状态模型通常是基于开发人员对协议规范的理解和客户端和服务器之间捕获的网络流量样本而手动编写的。这些手动提供的模型可能无法正确捕获在 SUT 内实现的协议。协议规范包含数百页的形式文本。实现的开发人员可能存在对协议的误解,或是实现时增加新的特性。此外,像其他黑盒方法一样, SBF 没有保留有趣的测试用例来进一步模糊。更具体地说,即使 SBF 可以产生测试用例,导致新的有趣状态,在其状态模型中没有定义,SBF 没有保留这些有价值的测试用例进行进一步 探索。它也不在运行时更新状态模型。

​ 在这项工作中,我们引入了 AFLnet——第一个有状态的CGF(SCGF)工具,以解决当前 CGF 和 SBF 方法的上述局限性。AFLnet 使自动状态模型推断和覆盖引导模糊工作携手进行;模糊处理有助于生成新的消息序列来覆盖新的状态,并使状态模型逐渐更加完整。同时,动态构建的状态模型通过利用保留消息序列的状态覆盖和代码覆盖信息,有助于推动模糊测试向更重要的代码部分发展。我们评估了 AFLnet 关于两个著名协议的 实现:文件传输协议(FTP)和实时流媒体协议(RTSP)。我们的初步结果表明,AFLnet 在代码覆盖范围、状态空间的覆盖范围和 bug 查找能力方面大大优于最先进的技术。AFLnet 暴露了RTSP 实现中两个以前未知的安全缺陷。(已分配CVE)

​ 我们计划在 https://github.com/aflnet/aflnet上发布 AFLnet 的源代码。

总结:有状态的黑盒模糊检测(SBF)是目前比较受欢迎的fuzz方法,学术界Sulley或BooFuzz,工业界的Peach,特点是利用FSM或者图遍历协议模型,并生成符合语法的消息序列。这种方法虽部分解决了第一个挑战,但是其的效果通常取决与设计的状态模型,这种模型通常需要由有经验的分析者/开发者基于对该协议的知识,以及结合端点之间的网络流量具体设计。设计的模型可能不会百分百符合黑盒内的协议,毕竟网络协议的实现取决于各个开发者,可能存在对协议的误解,或是实现时增加新的特性。此外现有的SBF方法尽管可以生成一些数据去触发一些特殊状态,然而并未将这类数据进行保留以指导未来的fuzz行为,例如,并不会更新现有的状态模型。针对这个问题,AFLNET的解决方案是比较合理的

 

0x2 示例:文件传输协议

​ 我们首先非正式地介绍服务器通信背后的主要概念以及我们在本文中使用的术语。服务器是一种可以远程访问的软件系统,例如,通过互联网。客户端是一种使用服务器提供的服务的软件系统。在我们的设置中,模糊器充当客户机,而服务器充当模糊测试的目标。

​ 为了交换信息,两个网络参与者都会发送消息。消息是一个不同的数据包。消息序列是消息的向量。有效的消息顺序由协议控制。来自客户端的消息也被称为请求,而来自服务器的消息则被称为响应。每个请求可以将服务器状态,例如,从初始状态推进到身份验证。服务器状态是服务器在与客户端通信时的特定状态。

​ 清单1显示了根据文件传输协议(FTP)在客户端和LightFTP之间的消息交换。LightFTP实现了一个FTP的服务器,是我们评估的主题之一。从客户端发送的消息序列以红色突出显示。FTP 指定客户端必须首先在服务器上进行身份验证。只有在身份验证成功后,客户端才能发出其他命令(即传输参数命令和服务命令)。对于来自客户端的每个请求消息,FTP 服务器以包含状态代码的响应消息进行响应(例如,230[登录成功]或430[无效用户/通过])。响应中的状态代码确保确认客户端请求,并通知客户端当前服务器状态。

 

0x3 工具的设计与实施

​ 我们在流行且成功的灰盒模糊器AFL上实现了我们的工具AFLnet。AFLnet 的架构如图 2 所示。为了方便与服务器的通信,我们首先启用套接字网络通信,这是普通AFL 不支持的。AFLnet 支持两个通道,一个是发送,另一个是接收来自被测服务器的消息/响应。除了在所有 CGF 方法中实现的代码覆盖反馈信道之外,响应接收信道还形成了状态反馈信道。AFLnet 使用标准的 C 套接字 API(即,连接、轮询、发送和恢复)以实现此功能。以确保两者之间的适当同步。

​ 对于 AFLnet 和被测试的服务器,我们添加了请求之间的延迟。否则,如果在发送并确认响应之前收到新消息,多个服务器实现会放弃连接。

​ AFLNET 的输入是包含捕获的网络流量的 pcap 文件(如 FTP客户端和 FTP 服务器之间的请求和响应,如清单 1 所示)。要在 pcap 文件中记录客户端和服务器之间的实际消息交换,可以使用网络嗅探器(例如,tcpdump)。可以使用数据包分析器提取相关的消息交换。例如,我们使用数据包分析器WireShark来自动提取 FTP 请求的序列。

​ AFLnet使用其请求序列解析器组件来生成消息序列的初始语料库。AFLnet 使用消息结构的协议特定信息,以正确的顺序从捕获的网络流量中提取单个请求。它首先过滤掉 pcap 文件中的响应报文,以获取并跟踪客户端的请求报文。然后,它解析并过滤报文以识别每条消息的开始和结束。我们实现了一个 轻量级的方法,它可以找到在给定协议中指定的消息的头和终结符。例如,每条 FTP 消息都以有效的FTP 命令(例如,USER、PASS)并以回车符后跟换行符(即0x0D0A)。此外,SCGF 与相应的服务器状态转换序列中的每个消息关联(参见图3)。这是通过发送消息并逐个解析响应来完成的。

​ 状态机器学习器获取服务器响应,并使用新观察到的状态和转换来增强已实现的协议状态机(IPSM)。AFLnet 将服务器响应读取到字节缓冲区,提取协议中指定的状态代码,并确定已执行的状态(转换)。如果服务器响应中有新的状态代码,则会添加一个表示新状态的新图形节点。

​ 目标状态选择器从 IPSM 中获取信息,以选择 AFLNET 下一步应关注的状态。AFLNET 使用了几种启发式方法,可以从IPSM中可用的统计数据中学习,以帮助目标状态选择器选择下一个状态。例如,为了识别fuzzer的盲点(即很少使用的状态),它选择一个状态$s$的概率与已执行突变消息序列的比例成反比(#fuzz) 。为了最大限度地提高发现新状态转换的概率, AFLnet 选择一个具有更高优先级的状态,该状态在之前选择的过程时显著增加代码或状态覆盖率 (#paths)。值得注意的是,AFLNET 只有在模糊过程工作了足够长的时间来积累统计数据后,才会开始应用这些启发式方法。在开始时,目标状态选择器会随机选择目标状态。

​ 一旦选择了目标状态$s$,序列选择器就会从序列语料库中选择可以到达状态$s$的消息序列(即种子输入)。AFL/AFLNET 将种子语料库(这里包含消息序列)作为队列条目的链接列表实现。队列条目是包含有关种子输入的相关信息的数据结构。此外,AFLNET 维护一个状态语料库,该状态语料库包括(i)状态条目列表,即包含相关状态信息的数据结构,以及(ii)将状态标识符映射到行使与状态标识符对应的状态的队列条目列表。序列选择器利用散列图随机选择一个如队列项中所示的序列来行使状态。

​ 序列变异器用协议感知的突变算子增强了AFL中的fuzz_one方法。AFLnet 是一种基于突变的模糊方法,即从语料库中选择种子消息序列并进行变异以生成新序列。与现有的基于生成的从零开始生成新消息序列的方法相比,有几个优势。首先,基于变异的方法可以利用真实网络流量的有效跟踪数据包来生成可能有效的新序列——尽管我们事先完全没有协议规范。相比之下,基于生成的方法需要一个详细的协议规范,包括具体的消息模板和协议状态机。其次,基于变异的方法允许不断进化一个特别有趣的消息序列进入语料库。导致发现新状态、状态转换或程序分支的生成序列被添加到语料库中以进一步模糊测试。这种进化的方法是基于覆盖的灰盒测试获得巨大成功的秘诀。

​ 给定一个状态$s$和一个消息序列$M$,AFLnet通过突变生成一个新的序列$M’$。以确保突变的序列$M’$依然是在状态$s$上进行进化,AFLNET将原始序列$M$分成三个部分:

  • 需要前缀$M_1$能到达选定状态$s$
  • 候选子序列$M_2$可以在前缀$M_1$执行后包含所有的消息同时依然保留在状态$s$
  • 后缀$M_3$就是原始序列$M$剩下的子序列,例如$<M_1,M_2,M_3>=M$

​ 发生突变的消息序列$M’=<M_1,mutate(M_2),M_3>$。通过维护原始的子序列$M_1$,$M’$仍然会达到状态$s$,这是模糊器目前正在关注的状态。突变的候选子序列$mutate(M_2)$在目标状态选择器上产生一个替代的消息序列。在我们最初的初始实验中,我们观察到替代请求可能“现在”无法观察到,而是传播到后来的响应。因此,AFLNET继续执行后缀$M_3$。

​ AFLNET 提供了几个协议感知的突变算子来修改候选子序列。AFLNET从消息序列$C$的语料库中生成一个消息池。消息池是来自网络嗅探器跟踪(加上生成的消息)的实际消息的集合,可以添加或替换到现有消息序列$M\epsilon C$中。为了改变候选序列$M_2$,AFLNET支持消息的替换、插入、复制和删除。除了这些具有协议感知的突变算子外,AFLNET还使用从灰盒测试中常见的字节级算子,如位翻转和字节的替换、插入或删除字节块。这些变异的操作都是堆叠起来的,即同时应用协议感知的和字节级的突变算子来生成突变的候选序列。

​ 已生成的消息序列$M’$如果被认为是是“有趣”的则,则将被添加到语料库中。如果服务器响应包含以前未观察到的新状态或状态转换 (即它们没有记录在 IPSM 中),那么该序列将被认为很有趣。如果一个序列覆盖了服务器源代码中的新分支,那么该序列也很有趣。

​ 现在我们将说明 AFLNET 的所有这些组件如何一起工作以模糊测试LightFTP服务器。假设 AFLNET 仅从一个包含网络流量的pcap 文件开始,如清单 1 所示。首先,请求序列分析器解析pcap 文件以生成单个序列(如图 3 所示),并将其保存到语料库$C$中。同时,状态机基于响应代码学习构造初始IPSM;这个初始的 IPSM 包含黑色的节点和转换(图4)。假设目标状态选择器选择状态331 (USER foo OK)作为目标状态,序列选择器将从序列语料库$C$中随机选择一个序列,该语料库$C$此时只包含一个序列。然后,序列突变器标识序列前缀(“USER foo” request),候选子序列(“PASS foo” request),和其余子序列为后缀。通过使用复合变异器突变候选子序列,序列突变器可能生成错误的密码请求(“PASS bar”)导致错误状态(530 Not logged in)。在这个错误的密码之后,它重播后缀(例如“MKD demo”, “CWD demo”),导致状态 530 的循环,因为在成功验证之前不允许所有这些命令。最后,发送“QUIT”请求,服务器退出。由于生成的测试序列(如图 5 所示)涵盖了新的状态和状态转换(在图 4 中以红色突出显示),因此会将其添加到语料库$C$和IPSM中。

其流程可以描述如下:程序的输入是数据包文件,程序内部的parser会对数据包进行分析,Parser随协议不同而采用不同设计,目的在于解析出Request请求,这些请求将组合为一个个序列,作为序列数据集。数据集中的序列会通过Mutator变异后发往Server,Server对request返回response,response数据包将被程序捕获,提取其中的状态码,判断该状态码位于FSM的何处,若原本的模型中不存在该状态码,则FSM中会增加一个结点代表该状态。程序中的State Selector将会分析程序当前的FSM模型,从中选出较少遍历/未曾遍历的状态(这是通过一个优先级机制实现的),传递给sequence selector,使之选择出可以触发该状态的request序列。当然,一开始由于缺乏统计信息,State的选取是随机化的

 

0x4 案例研究

​ 我们评估了 AFLNET与两种基线方法比较其有效性,一种是有状态的黑盒模糊器(BooFuzz)和一种是无状态覆盖引导的模糊器(AFLnwe),这是我们对AFL的修改以支持网络Fuzz。具体地说, 我们比较了在两个协议实现的 24 小时模糊活动中的平均分支覆盖、状态覆盖和暴露的错误数量,如图 6 所示。这两种协议很流行。虽然 FTP 已经被广泛用于文件传输,但 RTSP 是实时视频流的最常见的协议,它已经在像 YouTube 这样的大型现实框架中实现。Live555是我们实验中选定的 RTSP服务器,已被安装在隐私和安全保护设备上例如IP 摄像头。

​ AFLNET 和 AFLnwe 使用最常见使用场景的初始记录消息序列作为语料库(例如,上传文件,开始流媒体源),BooFuzz 是 用协议的详细模型启动的,包括消息模板和状态机。

重复实验:为了减轻随机性的影响,我们对每个测试项目都运行了BuoFuzz、AFLnet 和 AFLnwe各20次

A. 代码覆盖率和状态覆盖率

​ 图 7 显示 AFLnet以较大的领先优于最先进的状态灰盒模糊测试器BooFuzz($Vargha-Delaney A_12 > 0.71$)在所有有效性测试项目上。分支覆盖率、语句覆盖率和状态覆盖率上平均增长分别为60%、56%和 67%,我们证明了 AFLnet 变异真实消息序列和进化消息序列语料库的能力,这些语料库已经被观察到,从而增加了服务器代码的覆盖率。

​ AFLnet 的性能也明显优于 AFLnwe,特别是在 LightFTP 中。分支覆盖率、语句覆盖率和状态覆盖率上平均增长分别为121%、79%和 85%。为了了解为什么 AFLnwe 和 AFLnet在Live555中表现相当,我们必须查看已实现的协议状态机(IPSM)。首先, Live555的IPSM 的深度比 LightFTP的IPSM 更小。有效序列中的消息数目较小。其次,函数状态的数量较小,即大多数状态(它们还没有被初始序列发现)都是错误状态。

B. 漏洞发现

​ 图 8 显示了AFLnet、BooFuzz 和 AFLnwe 的漏洞查找功能的结果。对于所有的模糊器,我们统计发现的漏洞的数量,并测量它们暴露这些漏洞所花费的时间。AFLnet 在所有错误上的性能都优于另外两个模糊器。AFLnet 总共发现了 4 个漏洞,其中2个 ( CVE2018-4013 和 CVE-2019-7733 ),其余两 个 漏 洞 (CVE-2019-7314 和 CVE-2019-15232)为0 Day漏洞。CVE-2019-7314 和 CVE-2019-15232 都获得了 CVSS 的评级分数,评分为 9.8。鉴于这些漏洞的严重程度,Live555 的维护者迅速应用了补丁, 并在发送错误报告仅两天后就确认了我们的发现。我们无法使用BooFuzz和AFLnwe发现 CVE-2019-7314。

​ 我们进一步分析了 CVE-2019-7314 的根本原因发现是两者之间存在未指定的快捷方式当处于INIT 和 PLAY 状态(在图9中标记为红色)时设置消息包含一个 RANGE 值。虽然快捷方式本身是无害的,但它启用了只有我们的技术才能发现的漏洞。AFLnet 生成了一个随机消息序列,发现了这个转换, 保留了这个序列,并系统地将其进化,以发现一个0 Day漏洞。要利用该漏洞,攻击者需要发送两条消息序列。第一个消息是具有范围值的 SETuP 消息。第二个消息是长度大于 20,000 字节的任意消息。攻击者最多可以读取 8 个字节的空闲内存。因为标准 RTSP 规范中未记录该转换,所以BooFuzz无法在 Live555 中执行未指定的快捷方式。

 

0x5 相关工作

A. 基于覆盖率引导的灰盒模糊测试器

​ 灰盒模糊测试有几种引导策略。通过从某些“有趣的”种子输入中生成更多的输入,可以引导灰盒模糊测试器,例如,转向危险的或未发现的程序语句。最近,社区探索了让灰盒模糊测试器意识到输入结构的机会。相比之下,我们建议让灰盒模糊测试器了解一个有状态程序的状态空间,比如一个协议实现。

B. 实现网络功能的融合

​ 在学术界和工业界都开发了许多支持网络的模糊器。大多数启用网络的模糊器都采用了黑盒模糊的方法,即基于手动构建的协议规范从零开始生成新的消息序列。大多数启用网络的模糊器也采用了基于生成的方法,即使用预先指定的消息模板从头开始生成新的消息序列。相比之下,SCGF 采用了一种基于突变的方法,即通过突变现有的(记录的)消息序列来生成新的消息序列。

C. 增强在没有协议规范下构建协议模型的能力

​ 手动构建一个协议模型是乏味的和容易出错的。一种更好的方法是在模糊过程中自动逆向协议从而学习构建协议模型。我们可以区分黑盒方法,学习来自给定的消息语料库和白盒方法的消息结构,积极探索协议的实现以获得消息结构。例如,Polyglot使用动态分析技术,如污点分析和符号执行来从协议实现中提取消息格式。在状态机推断方面,我们可以区分被动学习方法,从消息序列语料库中学习协议状态机,而主动学习方法利用Angluin’s L*算法生成的消息序列主动查询协议实现。

​ 与这些现有的方法相比,我们采用了一种轻量级的突变方法。SCGF 既不需要手动构建的消息模板,也不需要推断出的任何消息模板。我们没有使用消息模板来生成新的信息,而是模糊实际的、真实的信息。同样地,与现有的技术相比,SCGF 不使用推断出的协议状态机来生成新的消息序列。相反,以种子语料库中现有的消息序列以以状态为中心的方式进行突变进行系统性的进化,以生成新的消息序列。

 

0x6 未来的工作

​ 在未来的工作中,我们计划在其他流行的关键协议(如安全外壳(SSH)和简单邮件传输协议(SMTP))上进行更多的实验,以评估 AFLnet 的有效性和效率。此外,我们还计划通过增强AFLNET 的状态机学习算法来支持不产生响应代码的协议实现来扩展 AFLNET 的适用性。

 

0x7 确认情况

这项研究部分由澳大利亚政府通过澳大利亚研究委员会(ARC)发现早期职业研究员奖(DE190100046)资助

(完)