Mozilla浏览器模糊测试:攻击与防御技术全解析

本文详细介绍了Mozilla在Firefox浏览器中实施的模糊测试技术栈,包括构建插桩、测试用例生成、结果管理和开发者协作流程,展示了如何通过自动化工具链发现和修复安全漏洞。

浏览器模糊测试在Mozilla的应用

Tyson Smith, Jesse Schwartzentruber, Sylvestre Ledru
2021年5月20日

引言

Mozilla长期以来一直在对Firefox及其底层组件进行模糊测试。实践证明,这是发现质量和安全问题最高效的方法之一。我们通常在多个层面实施模糊测试:既包括整体浏览器的模糊测试,也会花费大量时间对独立代码(例如使用libFuzzer)或完整组件(如使用独立shell的JS引擎)进行测试。本文将专门讨论浏览器模糊测试,并深入介绍我们开发的流水线。这条统一流水线是模糊测试团队多年工作的成果,旨在整合浏览器模糊测试工作,为开发者提供持续可操作的问题,并简化内部和外部模糊测试工具的集成。

构建插桩

为了尽可能提高效率,我们采用多种错误检测方法。这些方法包括AddressSanitizer(含LeakSanitizer)、ThreadSanitizer和UndefinedBehaviorSanitizer等清理工具,以及启用断言和其他运行时检查的调试构建。我们还使用rr和Valgrind等调试器。每种工具都提供了不同的视角来帮助发现特定类型的错误,但许多工具彼此不兼容,或需要自定义构建才能运行或提供最佳结果。除了提供调试和错误检测功能外,某些工具(如代码覆盖率和libFuzzer)必须依赖构建插桩才能工作。每个操作系统和架构组合都需要独特的构建,并且可能仅支持这些工具的子集。

最后,每个变体都有多个活跃分支,包括Release、Beta、Nightly和Extended Support Release(ESR)。Firefox CI Taskcluster实例会定期构建这些分支。

下载构建

Taskcluster使得查找和下载最新构建进行测试变得容易。上文我们讨论了不同插桩类型创建的变体数量,我们需要在自动化环境中对它们进行模糊测试。由于构建、工件、架构、操作系统和解压的组合数量庞大,下载是一项非 trivial 的任务。

为了降低构建管理的复杂性,我们开发了一个名为fuzzfetch的工具。Fuzzfetch可以轻松指定所需的构建参数,并下载和解压构建。它还支持下载指定版本,以便与二分查找工具配合使用。

测试用例生成方式

由于本文的目的是解释整个流水线,我们不会花太多时间解释模糊器。如果您感兴趣,请阅读“使用WebIDL模糊测试Firefox”和树内文档。我们结合使用公开可用的和自定义构建的模糊器来生成测试用例。

执行、报告和扩展方式

对于针对浏览器的模糊器,Grizzly管理和运行测试用例并监控结果。创建适配器使我们能够轻松在Grizzly中运行现有的模糊器。

为了充分利用给定机器上的可用资源,我们并行运行多个Grizzly实例。

对于每个模糊器,我们创建容器来封装运行所需的配置。这些配置存在于Orion monorepo中。每个模糊器都有一个配置,包含部署细节和资源分配,具体取决于模糊器的优先级。Taskcluster持续部署这些配置以分发工作和管理模糊测试节点。

Grizzly Target处理检测问题,如挂起、崩溃和其他缺陷。Target是Grizzly和浏览器之间的接口。检测到的问题会自动打包并报告给FuzzManager服务器。FuzzManager服务器提供自动化和用户界面,用于对结果进行分类。

其他更有针对性的模糊器使用JS shell,基于libFuzzer的目标使用模糊测试接口。许多第三方库也在OSS-Fuzz中进行模糊测试。这些值得提及,但不在本文讨论范围内。

管理结果

大规模针对各种目标运行多个模糊器会产生大量数据。这些崩溃不适合直接输入到Bugzilla等错误跟踪系统中。我们有工具来管理这些数据并准备报告。

FuzzManager客户端库在崩溃变体和重复结果离开模糊测试节点之前将其过滤掉。唯一的结果会报告给FuzzManager服务器。FuzzManager Web界面允许创建签名,帮助将报告分组到桶中,以辅助客户端检测重复结果。

模糊器通常生成数百甚至数千行长的测试用例。FuzzManager桶会自动扫描,以在Taskcluster中排队减少任务。这些减少任务使用Grizzly Reduce和Lithium应用不同的减少策略,通常去除大部分不必要的数据。每个桶会持续处理,直到成功完成减少。然后工程师可以对最小化的测试用例进行最终检查,并将其附加到错误报告中。最终结果通常用作Firefox测试套件中的崩溃测试。

模糊器的代码覆盖率也会定期测量。再次使用FuzzManager收集代码覆盖率数据并生成覆盖率报告。

创建最佳错误报告

我们的目标是创建可操作的错误报告,以便尽快修复问题,同时最小化开发人员的开销。

我们通过提供以下内容来实现:

  • 崩溃信息,如日志和堆栈跟踪
  • 构建和环境信息
  • 减少的测试用例
  • Pernosco会话
  • 回归范围(通过Bugmon进行二分查找)
  • 通过Bugmon验证

Grizzly Replay是一个工具,构成Bugmon和Grizzly Reduce的基本执行引擎,并使得收集rr跟踪以提交给Pernosco变得容易。它使得在自动化环境和手动使用中重新运行浏览器测试用例变得简单。它简化了处理顽固测试用例和触发多个结果的测试用例的工作。

如前所述,我们也在使用Pernosco。Pernosco是一个为rr跟踪提供Web界面的工具,使开发人员无需直接访问执行环境即可使用它们。这是一个由同名公司开发的惊人工具,极大地帮助调试大规模并行应用程序。当测试用例太不可靠而无法减少或附加到错误报告时,它也非常有帮助。创建rr跟踪并上传可以使停滞的错误报告变得可操作。

Grizzly和Pernosco的结合还有一个额外的好处,即使不频繁、难以重现的问题变得可操作。一个非常不一致的问题的测试用例可以运行数百或数千次,直到在rr下发生所需的崩溃。跟踪会自动收集并准备提交给Pernosco并由开发人员修复,而不是因为不可操作而被忽略。

与开发人员互动方式

为了请求新功能得到适当评估,可以通过fuzzing@mozilla.com或Matrix联系模糊测试团队。这也是任何原因联系我们的好方式。我们很乐意帮助您解决任何与模糊测试相关的问题或想法。当我们收到关于我们认为需要关注的新倡议和功能的信息时,我们也会主动联系。一旦开始对组件进行模糊测试,我们主要通过Bugzilla进行沟通。如前所述,我们努力打开可操作的问题或增强其他人记录的现有问题。

Bugmon用于自动二分回归范围。这尽快通知适当的人员,并在错误标记为FIXED后验证错误。关闭错误会自动将其从FuzzManager中移除,因此如果类似的错误进入代码库,可以再次识别。

在模糊测试期间发现的某些问题会阻止我们有效地对功能或构建变体进行模糊测试。这些被称为模糊阻塞器,它们有几种不同的形式。从产品角度来看,这些问题可能看似无害,但它们可以阻止模糊器针对重要代码路径,甚至完全阻止对目标进行模糊测试。适当优先处理这些问题并尽快修复它们非常有帮助,并且深受模糊测试团队的赞赏。

PrefPicker管理用于模糊测试的Firefox偏好设置集。当在偏好后面添加功能时,考虑将其添加到PrefPicker模糊测试模板中,以便在模糊测试期间启用它。定期审计PrefPicker模糊测试模板可以帮助确保区域不被遗漏,并尽可能有效地使用资源。

衡量成功

与其他领域一样,衡量是评估成功的关键部分。我们利用Bugzilla的元错误功能来帮助我们跟踪模糊器识别的问题。我们努力为每个模糊器和每个新模糊测试的组件设置一个元错误。

例如,Domino的元错误列出了该工具识别的所有问题(超过1100个!)。使用这些Bugzilla数据,我们能够展示多年来各种模糊器的影响。

Domino随时间报告的错误数量

这些仪表板有助于评估模糊器的投资回报率。

结论

模糊测试流水线中有许多组件。这些组件不断演进,以跟上调试工具、执行环境和浏览器内部的变化。开发人员总是在添加、删除和更新浏览器功能。错误被检测、分类和记录。保持一切持续运行并尽可能针对更多代码需要持续不断的努力。

如果您从事Firefox工作,可以通过及时告知我们可能影响或需要模糊测试的新功能和倡议,优先处理模糊阻塞器,以及管理PrefPicker中的模糊测试偏好来提供帮助。如果您对模糊测试感兴趣,请参与错误赏金计划。我们的工具公开可用,我们鼓励进行错误狩猎。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计