模糊测试(Fuzzing)教程

⚡ 智能摘要

模糊测试将无效、意外或随机数据输入程序,并监视崩溃、挂起和内存错误,从而发现脚本化功能测试几乎永远无法自行发现的安全缺陷。

  • 🔘 产地: 巴顿·米勒在威斯康星大学麦迪逊分校创造了这个术语,1989 年的第一次模糊测试导致大约三分之一的 UNIX 实用程序崩溃。
  • ☑️ 六步循环: 确定目标,确定输入,生成模糊数据,执行,监控行为,然后记录出现的每一个缺陷。
  • 三种世代策略: 变异模糊测试器会篡改有效样本,生成模糊测试器会根据模型构建输入,而协议模糊测试器则会根据规范进行工作。
  • 🧪 报道反馈改变了这一领域: 现代引擎会保留所有进入新代码的输入,这比纯粹的随机数据能发现更深层次的错误。
  • 🛠️ 工具已经发展到今天: Peach Fuzzer 和 WebScarab 已被存档,而 AFL++、libFuzzer、OSS-Fuzz、boofuzz 和 OWASP ZAP 是仍在维护的选项。
  • ⚙️ 已知限制: 模糊测试发现的是崩溃问题,而不是逻辑缺陷,因此它是对代码审查和渗透测试的补充,而不是替代。

模糊测试(Fuzzing)教程

什么是模糊测试?

模糊测试或模糊测试 模糊测试是一种软件测试技术,它通过向软件系统中注入无效或随机数据(称为模糊数据)来发现编码错误和安全漏洞。模糊测试的目的是利用自动化或半自动化技术注入数据,并测试系统是否存在各种异常情况,例如系统崩溃或内置代码故障。

模糊测试最初由威斯康星大学麦迪逊分校的巴顿·米勒(Barton Miller)开发。他创造这个术语是因为调制解调器链路上的线路噪声导致他使用的程序崩溃。他的学生在1989年运行了第一批模糊测试器,发现他们测试的UNIX实用程序中大约有三分之一崩溃或挂起。模糊测试是一种…… 软件测试 技术,它是一种 安全测试.

下图显示了基本的模糊测试循环,其中生成的数据被推送到被测应用程序并观察其响应。

模糊测试工作流程:模糊测试器生成格式错误的输入,并将其提供给被测应用程序。

为什么要做模糊测试?

模糊测试之所以能在测试计划中占有一席之地,是因为它能探索那些测试用例未曾考虑到的输入情况。团队采用模糊测试的主要原因如下。

  • 模糊测试通常能发现最严重的安全漏洞和缺陷,因为崩溃是输入路径未处理的直接证据。
  • 模糊测试与以下方式结合使用时能获得更有效的结果: 黑色 Box 测试与验证、Beta 测试和其他调试方法。
  • 模糊测试用于检查软件的漏洞,它是一种非常经济高效的测试技术,因为输入是生成的而不是手动编写的。
  • 模糊测试是黑盒测试技术之一。模糊测试也是黑客用来查找系统漏洞的最常用方法之一,因此首先进行模糊测试可以消除攻击者最容易入侵的途径。

模糊测试的类型

模糊测试工具通常根据其对目标程序的了解程度进行分类。模糊测试工具了解得越多,就能深入到代码的更深处。

类型 模糊测试器知道什么 典型用途
黑匣子模糊测试 它不了解内部结构;它只负责输入和输出。 快速冒烟测试针对二进制文件或实时端点进行。
白盒模糊测试 完整的源代码,通常与符号执行相结合,以解决难以到达的分支问题。 对源代码可获取的组件进行深入分析。
灰盒模糊测试 不进行源代码审查,但会提供运行时反馈,例如输入执行到哪个代码分支。 AFL++ 和 libFuzzer 等现代引擎的默认设置。

第二次、更早的分裂将……分开 愚蠢的模糊测试 ,来自 智能模糊测试一个笨拙的模糊测试器会直接翻转比特位,完全不了解输入格式,因此它的大部分数据都会被遇到的第一个解析器拒绝。而一个智能的模糊测试器则能够理解校验和、长度字段和消息结构,因此它的输入能够通过验证,并最终到达底层逻辑。 基于覆盖范围的模糊测试 正是灰盒改进使模糊测试成为主流:引擎对二进制文件进行插桩,保留任何到达新分支的输入,并改变幸存下来的输入,因此语料库稳步演化到未探索的代码,而不是从随机噪声重新开始。

如何进行模糊测试

模糊测试的步骤包括以下基本测试步骤:

步骤1)识别目标系统 — 选择要攻击的二进制文件、库、服务或协议端点,并确认您是否有权对其进行测试。

步骤2)识别输入 — 列出目标读取的每个入口点:文件、命令行参数、环境变量、网络数据包、表单字段和 API 有效负载。

步骤3)生成模糊测试数据 — 通过改变有效样本、从格式模型生成样本或将两者结合起来生成畸形输入。

步骤 4)使用模糊数据执行测试 — 使用生成的输入运行目标程序,理想情况下,应该在一个循环中运行,以便在每次失败后自动重新启动该过程。

步骤 5)监控系统行为 — 注意崩溃、挂起、断言失败、失控的内存使用和清理器报告,而不仅仅是检查打印输出。

步骤6)记录缺陷 — 保存触发每次失败的确切输入,将其缩减到最小可复现案例,并将其归档到堆栈中。 trac附件已附上。

模糊测试器示例

模糊测试器也根据其构建输入的方式进行分类,以下三种方法是最常遇到的。

  • 基于变异的模糊测试器 修改现有数据样本以创建新的测试数据。这是一种非常简单直接的方法:它从协议的有效样本开始,然后不断地修改每个字节或文件。
  • 基于生成的模糊测试器 根据模型的输入定义新数据。它们根据规范从头开始生成输入。
  • 基于协议的模糊测试器 模糊测试依赖于对被测协议格式的深入了解,而这种了解来源于规范。它涉及将规范数组写入工具,然后使用基于模型的测试生成技术遍历规范,并在数据内容、顺序等方面添加不规则之处。这也被称为语法测试、文法测试或鲁棒性测试。模糊测试器可以从现有测试用例生成测试用例,也可以使用有效或无效的输入。

基于协议的模糊测试有两个局限性:

  1. 在规范成熟之前,测试无法进行。
  2. 许多有用的协议都是已发布协议的扩展。如果模糊测试基于已发布的规范, 测试覆盖率 新协议的使用将受到限制。

最简单的模糊测试技术是向软件发送随机输入,可以是协议数据包,也可以是事件。这种发送随机输入的技术对于查找许多应用程序和服务中的漏洞非常有效。还有其他一些技术也很容易实现。要实现这些技术,我们只需要更改现有的输入,而更改输入只需交换其位即可。

模糊测试检测到的错误类型

因为模糊测试是根据程序的行为而不是预期值来判断运行结果的,所以它发现的缺陷可以分为三类。

  • 断言失败和内存泄漏: 这种方法广泛用于大型应用程序,因为其中的漏洞会影响内存安全,这是一个严重的漏洞。 Buffer 溢出、释放后使用和越界读取都会出现在这里。
  • 输入无效: 在模糊测试中,模糊器用于生成无效输入,以测试错误处理程序,这对于无法控制输入的软件至关重要。简单的模糊测试可以看作是一种自动化测试的方法。 负面测试.
  • 正确性错误: 模糊测试还可以用于检测某些类型的“正确性”错误,例如数据库损坏或搜索结果不佳。差分模糊测试是检测这些错误的常用方法,它将相同的输入提供给两个不同的实现并比较它们的响应。

模糊测试工具

用于网络安全的工具可以广泛用于模糊测试,例如: Burp Suite 还有 Peach Fuzzer。以下一些经典名称现已存档,因此每个条目旁边都注明了它们的当前状态。

  • 桃子模糊器Peach Fuzzer 提供比扫描器更强大的安全覆盖范围。其他测试工具只能搜索已知威胁,而 Peach Fuzzer 使用户能够发现已知和未知威胁。Peach Tech 已被 GitLab 收购,社区版 v3 已停止维护;其后续维护版本为…… GitLab 协议模糊测试器社区版.
  • 尖峰代理SPIKE Proxy 是一款专业级工具,用于查找 Web 应用程序中的应用层漏洞。它涵盖了基础知识,例如: SQL 在完全开放的环境下进行注入和跨站脚本攻击 Python 基础设施,并且可供使用 Linux 和 Windows该网站已多年无人维护,此处收录仅供历史参考。
  • WebScarabWebScarab 是用以下语言编写的: Java 因此,它可移植到多个平台。WebScarab 框架使用 HTTP 和 HTTPS 协议进行通信,并作为拦截代理运行:它允许操作员在服务器接收请求之前审查和修改浏览器创建的请求,并在浏览器接收响应之前审查和更新服务器生成的响应。WebScarab 发现的任何漏洞都会添加到其已报告问题列表中。该代码库已于 2024 年 4 月存档,目前为只读模式。
  • OWASP WSFuzzerWSFuzzer 是一个用 GPL 许可协议编写的程序。 Python 它针对的是网络服务,其最新版本基于HTTP。 SOAP 服务 是主要目标。它作为 WebScarab 的一部分发布,并随其一起退役; OWASP ZAP 及其 Fuzzer 插件 建议更换这些部件。
  • 保留的替代方案: AFL++libFuzzer 是本地代码的标准覆盖率引导引擎, OSS模糊 为开源项目持续免费运行这些服务,并且 boofuzz 涵盖网络协议模糊测试 Python更详细的列表请参见指南。 安全测试工具.

模糊测试最佳实践

如果模糊测试器只是对准目标而置之不理,通常很难发现什么问题。以下实践将能够产生缺陷报告的测试活动与仅仅消耗 CPU 时间的测试活动区分开来。

  • 从一个好的种子语料库开始。 收集应用程序已接受的真实有效输入。修改真实文件比修改随机字节能更快地将数据送达解析代码。
  • 编写一个小型、快速的线束。 入口点每次运行应该只做一件事,避免网络调用和磁盘写入,并快速返回,因为吞吐量是以每秒执行次数来衡量的。
  • 打开消毒液。 静默内存损坏通常不会导致程序崩溃。AddressSanitizer 和 UndefinedBehaviorSanitizer 会将其转化为可立即诊断的故障。
  • 长时间跑,并且持续跑。 一两个小时的运行可以发现浅层的 bug;深入的 bug 通常需要几个小时,因此模糊测试应该放在每晚的 CI 作业中,而不是手动执行。
  • 尽量减少并去重每次崩溃。 将失败的输入缩小到最小形式,并按堆栈对崩溃进行分组。 trac否则,一个 bug 就会变成数百个工单。
  • 保留回归语料库。 将每个可复现的输入添加到每次构建时运行的永久集中,这样已修复的缺陷就不会悄悄地再次出现。
  • 依法锁定目标。 未经书面许可对运行中的第三方服务进行模糊测试,与攻击并无区别。

模糊测试的优点

如果使用得当,模糊测试能够带来其他技术难以匹敌的价值。

  • 模糊测试可以改进软件安全测试。
  • 模糊测试中发现的漏洞有时很严重,而且往往与攻击者使用的漏洞相同,包括崩溃、内存泄漏和未处理的异常。
  • 如果由于时间和资源的限制,测试人员未能发现任何错误,那么模糊测试也能发现这些错误。
  • 输入内容由机器生成,因此无需额外的人工干预,覆盖范围就能在一夜之间不断扩大。

模糊测试的缺点

模糊测试成本低廉的那些特性,也限制了它所能证明的内容。

  • 单靠模糊测试无法全面了解整体安全威胁或漏洞情况。
  • 模糊测试在处理不会导致程序崩溃的安全威胁(例如某些病毒、蠕虫和木马)方面效果较差。
  • 模糊测试只能检测出相对简单的故障或威胁,它不会推理业务逻辑。
  • 要有效运行,需要大量的机器时间。
  • 使用随机输入设置边界值条件非常成问题,尽管测试人员现在使用由用户输入驱动的确定性算法解决了大部分此类问题。

模糊测试与渗透测试

这两种活动都是为了查找安全缺陷,但它们回答的问题不同,很少可以互换。

标准 模糊测试 渗透测试
通过驱动 一个自动生成错误输入的引擎 一位经验丰富的测试人员对系统进行推理
寻找 崩溃、死机和内存安全故障 可利用的弱点,包括逻辑和配置缺陷
深度 输入覆盖范围极广,推理却很浅。 覆盖面窄,推理深刻
输出 重现输入和堆栈 traces 一份包含漏洞利用路径和风险评级的调查报告
最美好的时刻 在构建管道中持续进行 定期针对候选版本

实际上,这两者相辅相成:模糊测试可以清除廉价的、可自动化的崩溃,从而使测试人员有限的时间能够用于只有人类才能发现的缺陷。

常见问题

种子语料库是模糊测试器用来测试的初始有效输入集。小型、多样化的真实文件效果最佳,因为每个文件都已通过解析器验证,使引擎能够将资源用于更深层次的代码,而不是仅仅进行第一次有效性检查。

模糊测试框架是一个小型函数,它将一个包含模糊测试字节的缓冲区传递给被测代码。它应该避免全局状态操作、文件写入和网络调用,以便引擎能够每秒运行数千次。

一两个小时只能发现一些浅层的漏洞。而真正有效的宣传活动会持续数小时甚至数天,因为新的报道会以爆发式的方式出现。报道曲线的停滞期,而不是时间的长短,才是宣传活动停止奏效的可靠信号。

AddressSanitizer 捕获缓冲区溢出和释放后使用错误,UndefinedBehaviorSanitizer 捕获整数和指针误用,MemorySanitizer 捕获对未初始化内存的读取错误。如果没有它们,许多数据损坏会悄无声息地发生,模糊测试器根本不会报告任何故障。

重现该问题,将输入最小化到最小的失败案例,并将其与共享同一堆栈的崩溃案例归为一组。 trac然后,通过通常的方式提交一张工单。 缺陷管理流程 并将输入添加到回归语料库中。

它们都具有随机性,但没有意图。 猴子测试 模糊测试会向运行中的界面抛出任意用户操作,而模糊测试则会针对特定的输入解析器并测量代码覆盖率,因此它可以引导自身找到先前输入从未到达过的代码。

语言模型正被用于为未经模糊测试的 API 构建框架,为特殊格式合成种子输入,以及对崩溃报告进行聚类和汇总。引擎仍然提供覆盖率反馈;模型的主要作用是消除手动设置工作。

GitHub 副驾驶 可以根据现有的 API 签名生成 libFuzzer 入口点、构建文件和种子生成器。 Rev仔细查看结果,因为悄悄吞噬错误的安全机制不会报告任何崩溃。

总结一下这篇文章: