- 模糊测试通过自动发送格式错误的输入来发现软件和系统中的安全漏洞。
- AFL 和 AFL++ 是智能模糊测试器,它们使用插桩编译器,通过覆盖率来指导自身并找到新的执行路径。
- 使用 AFLTriage 和 GDB 等工具分析崩溃,可以评估整数下溢等漏洞的可利用性。
- 将模糊测试集成到开发周期中可以提高系统的鲁棒性,有助于满足安全标准,并降低发生重大事故的风险。
如果您有兴趣 面向二进制的网络安全和漏洞利用你迟早会接触到模糊测试,尤其是 AFL 和 AFL++。我们说的不是理论上的实验室工具,而是每天都在用来发现知名程序中真实缺陷的工具,就像之前发现的漏洞一样…… 像 7-Zip 这样的热门应用程序.
接下来,我们将以友好而详尽的方式看到: 如何使用 AFL 和 AFL++ 对二进制文件进行模糊测试我们将详细解释什么是模糊测试,它在安全性方面为何如此强大,以及如何将其集成到现代开发工作流程中。我们还将分析一个真实的漏洞案例(允许远程代码执行的整数下溢漏洞),并介绍一些辅助工具,例如: GDB 及其扩展、AFLTriage 和操作模块.
什么是 AFL/AFL++?为什么它们被认为是智能模糊测试工具?
AFL(美国长毛垂耳兔)及其进化版 AFL++ 这些是开源模糊测试工具,旨在自动测试二进制文件,查找溢出、内存错误或导致崩溃甚至可利用漏洞的意外行为等故障。
AFL 的精妙之处不仅在于发送随机数据,而在于…… 智能模糊测试器或覆盖引导模糊测试器从一个或多个有效示例条目(所谓的)开始 测试用例),AFL 不断生成变体,并且由于程序的检测功能,可以检测到新的变异何时导致二进制文件采取不同的执行路径。
为了实现这种检测功能,AFL 有自己的编译器,例如: afl-gcc 和 afl-g++这些函数会在编译过程中向代码中插入一些小的“钩子”。这些钩子会通知 AFL 已发现某个特定的输入。 项目流程中的新路线这样一来,你就可以专注于真正提供额外覆盖范围的突变,而不会浪费时间在不会改变任何事情的数据上。
这种方法使得 AFL 类似于“二进制文件的 dirb”:一个广为人知的工具,一旦理解了基本流程就很容易使用,而且功能强大到足以发现…… 生产程序存在严重缺陷即使有更高级或更专业的模糊测试器。
模糊测试简介及其在网络安全中的作用
模糊测试,或简称模糊测试,是一种技术 自动化安全性和稳健性测试其目的是向应用程序发送大量格式错误、随机或异常的数据,目的是观察程序是否会崩溃、出现故障或行为异常。
对软件进行模糊测试时,目的是找出诸如以下错误: 缓冲区溢出、输入验证失败、内存管理问题或竞态条件这些缺陷中有很多会导致任意代码执行、信息泄露或拒绝服务攻击。
典型的模糊测试工作流程包含三个清晰的阶段:首先,生成测试数据集(可以是完全随机的、按照特定格式生成的,也可以是通过修改有效输入生成的);然后…… 在监控下执行被测二进制文件 对其行为进行监控;最后,记录并分析崩溃或异常行为,以便开发人员可以调查问题的根源。
模糊测试策略有很多种。有些工具侧重于…… 代际模糊创建符合明确定义格式(例如,文件结构或网络协议)的条目;其他条目则基于…… 有效输入的变异这正是AFL和AFL++的优势所在。此外,还有一些更随机的方法,以及一些依赖于对被测系统特定知识来找出其薄弱环节的方法。
在现代网络安全中,模糊测试是一项至关重要的辅助手段:它有助于发现系统中的漏洞。 在软件投入生产之前主动采取措施并有助于遵守 ISO 27001 等法规和标准或数据保护要求。此外,它还能降低因关键可利用漏洞落入攻击者手中而造成的声誉风险。
真实案例:解压缩器中的整数下溢和远程代码执行漏洞
为了理解 AFL 和模糊测试的真正价值,基于特定漏洞进行分析非常有用。 非常流行的文件解压缩程序中的整数下溢这是一个远程代码执行 (RCE) 错误,在使用 Zstandard 算法处理压缩文件时触发,其 CVSS 得分很高,但不是最高分,正是因为它需要更具体的上下文(例如,正在处理特定的文件)。
当一个具有明确下界的整数变量出现下溢时,就会发生整数下溢。 它用于减法运算,直到达到其最小值为止。由于许多整数类型不允许负值,因此当“低于”零时,由于这些数字在内部的表示方式,不会得到一个小于零的数字,而是会跳到该范围内的最大可能值。
想象一个整数变量,理论上它永远不会为负数。如果你不断地减去一个常数直到得到 0,然后继续减去, 你不会得到-1、-2等等。相反,该值会反转,并变成允许范围上限的一个巨大数字。如果将这个荒谬的数字用作内存操作的索引或大小,则可能导致超出预期限制的读写操作。
当使用可能发生下溢的整数变量时,问题就变得十分严重。 管理缓冲区或计算偏移量一个典型的伪示例是在 memcpy 类型的函数中间接使用用户控制的变量作为偏移量,在一个循环中,每次迭代都会递减一个常量,直到它达到零或非正数。
在这种情况下,如果初始值始终无法“干净利落地”满足退出条件,下溢最终会导致数值变得非常大,循环将继续运行,并且 记忆的大规模损坏这会导致程序崩溃,并且,通过足够的漏洞利用,有可能执行任意代码。
AFL如何帮助定位这类漏洞
一旦理解了整数下溢的概念,就能明白为什么 AFL 和 AFL++ 如此有用:从 一个或几个完全有效的输入文件模糊测试器可以生成数万种变体,直到找到能够触发受影响的解压缩路径中的特定错误的变体为止。
在模糊测试过程中,AFL 会监控被插桩的二进制文件,以确定何时输入会导致执行不同的流程控制路径。每次检测到新的路径时, 将该条目标记为有趣 并将其保存下来,作为未来变异的基础。这样一来,不仅可以探索常规的执行路径,还可以探索那些激活非常规代码路径的数据组合,例如使用特殊参数的解压缩过程。
对于整数下溢,AFL 可以找到一个样本,该样本会导致循环中涉及的变量永远无法达到预期的输出值。结果会生成一个可检测的崩溃,该崩溃信息将存储在相应的 AFL 结果文件夹中,以便进行进一步分析。
执行几分钟或几小时后,AFL通常会发出信号 首次相关崩溃并非所有发现的漏洞都能被利用,但它们清楚地表明二进制文件的内存管理、输入验证或内部逻辑中的某些东西没有按预期工作。
这时,像 AFLTriage 这样的支持工具或像 GDB 这样的调试器就派上了用场,它们可以让你更深入地了解崩溃情况,观察寄存器和内存的状态,并评估故障是否可以被可靠地利用,或者它是否只是一个对攻击者来说没什么用的挂起。
AFL/AFL++ 和分析环境的基本安装
在标准 Linux 系统上搭建使用 AFL 进行模糊测试的最小环境非常简单。在许多发行版中,只需安装与已插桩编译器对应的软件包即可,例如,可以使用类似这样的命令: 安装 afl-g++ 或等效的 AFL++ 软件包 把整套都带来。
这样做会同时安装模糊测试工具(例如……) 模糊不清例如,可以使用一些专用编译器(afl-gcc、afl-g++ 以及它们在 AFL++ 中的现代变体)。当您需要使用插桩技术重新编译目标代码时,就需要使用这些编译器。插桩技术对于充分发挥覆盖率引导模糊测试的优势至关重要。
除了模糊测试器本身之外,一个良好的调试环境几乎是必不可少的。一个扩展的 GDB 就足够了。 类似PEDA或GEF的插件 它提供了额外的命令、便捷的内存格式化、堆栈可视化以及用于检查寄存器和内存映射的快捷方式,使生活变得更加轻松。
如果您还没有 GDB,可以使用发行版的软件包管理器进行安装。然后,只需集成您选择的插件(例如,从 GEF 或 PEDA 的存储库下载并将其添加到 GDB 配置文件中,或者在调试器本身中使用 `source` 命令)。
有了这个“功能强大的 GDB”,你就可以分析那些在模糊测试下崩溃的已插桩二进制文件,下载 导致崩溃的输入 并一步一步地跟踪执行过程中发生故障的具体步骤。
使用 AFL 编译目标二进制文件以进行模糊测试
一旦你准备好了 AFL 或 AFL++,下一步就是进行真正的模糊测试了。 使用 AFL 编译器编译目标并且要采取适当的措施,以便进行分析和潜在的利用,以防此次演习的目标是进攻性研究。
在大型项目中,例如解压缩程序或归档实用程序套件,启用调试符号(选项)是一个好主意。 调试模式或类似 -g 的标志并尽量减少优化(例如,使用 -O0 而不是 -O2)。这极大地有助于理解源代码中与观察到的崩溃相关的具体情况。
通常你需要找到相应的Makefile或构建文件, 修改定义编译标志的行。这可能包括激活 DEBUG 变量、强制执行特定的优化级别,以及最重要的是,用 afl-gcc 和 afl-g++(或等效的 AFL++ 工具)替换 CC 和 CXX。
例如,您可以使用类似 CC=afl-gcc 和 CXX=afl-g++ 的参数以及相应的 Makefile 文件来启动构建。最终会生成一个经过插桩的二进制文件,其中包含必要的钩子,以便 AFL 可以在整个执行过程中测量代码覆盖率。
编译完成后,最好使用类似这样的命令检查生成的二进制文件是否包含调试符号和正确的架构。 关于可执行文件的文件这样可以确保你不会盲目地进行模糊测试,并且可以轻松调试出现的任何崩溃。
准备测试用例并执行 AFL
在对你的二进制文件启动 AFL 之前,你需要准备一套…… 最低有效条目如果您正在测试一个解压缩程序,该程序无法处理使用特定算法压缩的文件,则您需要创建至少一个使用该压缩方法的格式良好的文件。
通常的做法是创建一个目录,例如名为 testcases 的目录,并在其中存储一个或多个可运行的示例。这些示例将作为 AFL 的起点。 产生越来越具攻击性的突变尝试发现新的执行路径,最终导致崩溃。
启动 AFL 的典型命令如下所示:指定会话或同步名称、包含测试用例的输入文件夹、存储结果的输出路径,以及一些附加参数,例如超时时间,以避免陷入无限循环。
命令行还指定了如何使用目标程序执行。 记分牌@@AFL 会自动将其替换为它生成的每个测试用例的路径。因此,您可以指定类似这样的路径: ./binary 参数 @@ AFL 将负责使用不同的文件启动可执行文件数千次。
需要注意的是,AFL 最初可能会报错,提示系统配置存在问题(例如资源限制、核心转储等)。在这种情况下,通常会有一个辅助脚本或工具来调整内核和会话参数,以优化模糊测试环境。在重复测试之前,应使用适当的权限运行该脚本或工具。
使用 AFLTriage 和 GDB 进行碰撞分析
经过一段时间的模糊测试后,AFL 将逐渐在输出文件夹中填充有趣的新条目,最重要的是,还有 导致崩溃的文件这些文件通常存储在特定的子目录中(例如,崩溃文件存储在 session 文件夹中)。
为了避免逐一审查每个输入内容而感到崩溃,可以使用像 AFLTriage 这样的工具来做到这一点。 自动针对每个崩溃文件运行该二进制文件 并生成结构化报告,详细说明故障发生的位置和方式。
启动 AFLTriage 时,您需要指定输入目录(崩溃文件夹)、用于保存报告的输出目录以及使用 @@ 运行二进制文件的命令。该工具将分析每个案例并 它将生成包含相关信息的文本文件。 对于分析师而言。
这些报告的典型内容包括导致崩溃的信号类型、失败的方向、基本堆栈跟踪,以及在很多情况下,代码出错的函数或宏(例如,特定的复制操作、压缩循环或每次迭代中减去大小的 COPY_CHUNKS 宏)。
一旦找到一个有趣的崩溃,下一步就是在 GDB 中重现它。为此,需要启动调试器,并将二进制文件和导致崩溃的输入文件作为参数,然后在 GDB 内部执行程序。当崩溃重现后,需要记录以下状态: 关键记录,例如 RAX、RDX 等。检查正在执行的确切指令,并查阅内存映射(vmmap)以查看正在读取或写入哪个区域。
从崩溃到潜在利用:GDB 中的分析模块
看到某个二进制文件崩溃并不总是意味着该漏洞可以被利用。这时,像该模块这样的 GDB 扩展就派上用场了。 可利用的旨在分析崩溃的背景,并粗略地对故障是否可以被利用进行分类。
这类扩展程序会研究诸如指令指针的有效性、堆栈指针的状态、读写尝试所在地址的内存权限以及导致崩溃的信号性质等各个方面。凭借所有这些信息,它们可以提供…… 对开发潜力的指示性结论 (例如,将漏洞分类为可能可利用、或许可利用或不太重要)。
如果整数下溢最终写入只读内存,该插件可能会提示这是一个潜在的可利用漏洞,因为写入操作在预期范围之外执行,而该区域不应该被触碰。
并非所有场景都如此简单,但将 AFL 用于发现崩溃,AFLTriage 用于对案例进行分组和描述,以及带有扩展功能的 GDB 用于评估可利用性,这种组合构成了一种…… 非常强大的二元审计管道之后的工作纯粹是漏洞利用的研究和开发,其复杂程度会因二进制文件和主动保护措施的不同而有很大差异。
分析的另一个关键部分,尤其是在发布 CVE 编号和相关补丁时,是审查漏洞在源代码中的修复方式。这通常涉及 所用数据类型的变化 (例如,从有符号字节到无符号字节,扩大范围)以及添加额外的检查(如果超过某些限制则返回错误代码),以防止下溢或内存损坏。
生态系统中其他相关的模糊测试工具和工具
尽管 AFL 和 AFL++ 是模糊测试原生二进制文件最知名的选择之一,但模糊测试生态系统远比这广泛得多,如果您经常使用原生二进制文件,就值得密切关注。 软件安全.
最常用的选项之一是 LibFuzzer,这是一个最初由 Google 开发的库,它可以直接与 C/C++ 代码集成并执行测试。 在特定功能层面进行模糊测试非常适合用高度可控的输入测试各个组件。
此外还有 Peach Fuzzer,这是一个更通用、更可配置的平台,允许您定义 协议、文件格式和复杂结构的精确模型它在可靠性至关重要的领域非常受欢迎,例如工业系统或物联网环境。
在 Web 领域,OWASP ZAP 等工具包含专门针对 Web 应用程序的模糊测试模块,能够发送 篡改请求、畸形加载和注入测试 HTTP 端点用于检测 SQL 注入、输入验证问题或表单处理错误等漏洞。
所有这些都与 AFL/AFL++ 二进制模糊测试相辅相成,因为它能够覆盖从底层本地代码到最终用户最直接接触的应用层的所有层级。所有方法的共同目标都是一样的:在攻击者之前自动查找漏洞。
如何将模糊测试集成到开发周期中
要让模糊测试持续发挥真正的价值,仅仅偶尔运行一下 AFL 或 AFL++ 就置之不理是不够的。这样做效果要好得多。 将模糊测试集成到软件开发生命周期中类似于单元测试或集成测试的方式。
第一步是定义 系统中哪些部分最为关键文件解析器、网络模块、处理用户数据的组件或对外暴露的服务。可以围绕这些目标设计特定的模糊测试活动,并使用精心挑选的测试用例。
接下来,你需要根据具体情况选择合适的工具。对于复杂的本地二进制文件,AFL++ 或 LibFuzzer 通常是理想的选择;而对于 Web API 或 HTTP 应用程序,使用面向 Web 的工具则更为合适。关键在于模糊测试能够正确执行。 可重复且可自动化.
与 CI/CD 系统的集成非常有用:可以配置流水线,以便在某些分支或某些部署之前,启动一个有限时间的模糊测试电池,保存发现的任何新崩溃,并在出现潜在的严重漏洞时使构建失败。
最后,需要理解的是,模糊测试是一个迭代过程。每次修复一个 bug 后,都应该对受影响的组件再次进行模糊测试,以验证补丁是否有效,以及是否引入了新的 bug。 没有新的回归或漏洞这样一来,模糊测试就成为持续保护的额外一层,而不是孤立的审计。
综上所述,以上所有内容表明 AFL、AFL++ 和模糊测试如何使我们能够从仅基于人工审查的安全方法转向更全面的方法。 更加自动化和彻底它能够发现那些原本会被隐藏的错误。通过处理实际案例,例如常用解压缩器中的下溢漏洞,也有助于理解这并非仅仅是理论,而是已被利用的问题。因此,保持软件更新并定期进行模糊测试,是系统能否安全抵御攻击的关键所在。
对字节世界和一般技术充满热情的作家。我喜欢通过写作分享我的知识,这就是我在这个博客中要做的,向您展示有关小工具、软件、硬件、技术趋势等的所有最有趣的事情。我的目标是帮助您以简单而有趣的方式畅游数字世界。

