Skip to content
 编辑

Fuzzing binary-only targets

对纯二进制目标进行模糊测试

AFL++、libFuzzer 及其他模糊测试工具在拥有目标源代码的情况下非常出色。这些工具能够提供快速且基于覆盖率指导的模糊测试。

但对于只有二进制程序而没有源代码的情况,标准的 afl-fuzz -n(非插桩模式)并不高效。

为了快速对黑盒二进制程序进行动态插桩,AFL++ 仍然提供了多种支持。下面将介绍如何使用 AFL++ 对这些二进制文件进行模糊测试。

简要说明:

如果可以使用 persistent mode,且稳定性足够高,那么 persistent mode 下的 FRIDA mode 和 QEMU mode 速度最快。

否则,可以尝试 Zafl、RetroWrite、Dyninst;如果这些方法也失败,可以使用标准 FRIDA/QEMU mode,并设置 AFL_ENTRYPOINT 到所需位置。

对于非 Linux 平台的目标,请使用 unicorn_mode。

使用 AFL++ 对纯二进制目标进行模糊测试

QEMU mode

QEMU mode 是针对程序的“原生“解决方案。它位于 ./qemu_mode/ 目录中, 编译后可以通过 afl-fuzz -Q 命令选项访问。这是最简单的替代方案,即使是跨平台二进制文件也适用。

对于 Linux 程序及其库,这通过一个运行在鲜为人知的 “user space emulation” mode 下的 QEMU 版本来实现的。QEMU 是一个独立于 AFL++ 的项目,但可以通过以下方式轻松构建此功能:

cd qemu_mode
./build_qemu_support.sh

推荐的 QEMU mode 设置如下:

然后根据剩余核心数量运行尽可能多的 -Q 模式实例,或者使用二进制重写器(如 Dyninst、RetroWrite、ZAFL 等)。二进制重写器都有各自的优势和注意事项。ZAFL 是最好的,但不能用于商务/商业环境。

如果二进制重写器适用于你的目标,那么就可以正常使用 afl-fuzz,其速度将是 QEMU mode 的两倍(但比 QEMU persistent mode 慢)。

QEMU mode 的速度降低了约 50%。不过,有多种方法可以提高速度:

有关其他说明和注意事项,请参考 qemu_mode/README.md。如果可以,应使用持久模式,参见 qemu_mode/README.persistent.md。 该模式比编译时插桩慢约 2 至 5 倍,而且不利于并行化。

请注意,还有 honggfuzz: https://github.com/google/honggfuzz,它现在也有 QEMU mode,但性能仅为 1.5%。

如果您想不费精力地编写一个自定义的模糊器,我们强烈推荐您查看我们同样支持 QEMU 的姊妹项目 libafl: https://github.com/AFLplusplus/LibAFL

WINE+QEMU

Wine mode 可以通过 QEMU 插桩运行二进制文件。它需要安装 Wine、Python3 和 pefile Python 包。

该模式已经包含在 AFL++ 中。

更多信息请参考: qemu_mode/README.wine.md.

FRIDA mode

在 FRIDA mode 下,可以像使用 QEMU mode 一样轻松地对纯二进制目标进行模糊测试。大多数情况下,FRIDA mode 比 QEMU mode 稍快。此外,FRIDA mode 较新,并且支持 MacOS(包括 Intel 和 M1 芯片)。

构建 FRIDA mode 的方法:

cd frida_mode
gmake

更多说明和注意事项请参考: frida_mode/README.md.

如果可能,使用 persistent mode,参见 instrumentation/README.persistent_mode.md。尽管其速度比编译时插桩慢 2-5 倍,并且不利于并行化,但对于纯二进制模糊测试来说,如果可以使用,它能够显著提高速度。

此外,还可以通过 FRIDA 进行远程模糊测试,例如,如果您希望在 iPhone 或 Android 设备上进行模糊测试,可以使用 https://github.com/ttdennis/fpicker/ 作为中间工具,它利用 AFL++ 进行模糊测试。

如果您想不费精力地编写一个自定义的模糊器,我们强烈推荐您查看我们同样支持 Frida 并且已经有现成的工作示例的姊妹项目 libafl: https://github.com/AFLplusplus/LibAFL

Nyx mode

Nyx 是一个基于 KVM 和 QEMU 构建的全系统仿真模糊测试环境,支持快照功能。它仅适用于 Linux,目前仅支持 x86_x64 架构。

对于纯二进制模糊测试,需要一个特殊的 5.10 内核。

更多信息请参考:nyx_mode/README.md.

Unicorn

Unicorn 是 QEMU 的一个分支,因此其插桩方式非常相似。但与 QEMU 不同的是,Unicorn 不提供全系统甚至用户态的仿真。如果需要,运行时环境和/或加载器必须从零开始编写。此外,Unicorn 移除了块链接功能,这意味着 AFL++ 的 QEMU mode 中的速度提升无法移植到 Unicorn。

对于非 Linux 二进制文件,可以使用 AFL++ 的 unicorn_mode,该模式可以模拟任意内容,但代价是较低的速度以及需要用户编写脚本。

构建 Unicorn 模式的方法:

cd unicorn_mode
./build_unicorn_support.sh

更多信息请参考: unicorn_mode/README.md.

共享库

如果目标是对共享库进行模糊测试,有两种方法可以选择。 对于这两种方法,都需要编写一个小型测试程序(harness)来加载并调用该库。 然后使用 FRIDA mode 或 QEMU mode 进行模糊测试,同时设置 AFL_INST_LIBS=1AFL_QEMU/FRIDA_INST_RANGES

另一种精确度较低且速度较慢的选择是使用 utils/afl_untracer/ 进行模糊测试,并以 afl-untracer.c 为模板。这种方法比 FRIDA 模式慢。

更多信息请参考: utils/afl_untracer/README.md.

Coresight

Coresight 是 ARM 针对 Intel PT 的解决方案。从 AFL++ v3.15 开始,Coresight 提供了一种追踪器实现,位于 coresight_mode/ 中,速度比 QEMU 更快,但无法并行运行。目前只能追踪一个进程(开发中)。

更多信息请参考: coresight_mode/README.md.

二进制重写器

二进制重写器是一种替代方案。与 AFL++ 提供的解决方案相比,重写器速度更快,但并非总能成功运行。

ZAFL

ZAFL 是一个静态重写平台,支持 x86-64 的 C/C++ 二进制文件,无论是去符号表的还是未去符号表的,PIE(位置无关可执行文件)与非 PIE 文件都可支持。除了常规的插桩,ZAFL 的 API 还支持一些转换过程(例如,laf-Intel、上下文敏感性、InsTrim 等)。

ZAFL 的基准插桩速度通常达到 afl-clang-fast 的 90-95%。

https://git.zephyr-software.com/opensrc/zafl

RetroWrite

RetroWrite 是一种可以与 AFL++ 结合使用的静态二进制重写工具。如果您有一个 x86_64 或 arm64 的二进制文件,且该文件不包含 C++ 异常处理机制,并且对于 x86_64 文件仍保留符号表并编译为位置无关代码(PIC/PIE),RetroWrite 可能是一个适合的解决方案。它会将二进制文件反编译为 ASM 文件,然后用 afl-gcc 对其进行插桩。

通过 RetroWrite 进行静态插桩的二进制文件,在性能上接近编译器插桩的文件,并优于基于 QEMU 的插桩方式。

https://github.com/HexHive/retrowrite

Dyninst

Dyninst 是一个二进制插桩框架,与 Pintool 和 DynamoRIO 类似。不过,Pintool 和 DynamoRIO 在运行时插桩,而 Dyninst 则是在加载时插桩目标,并在插桩后运行或将更改保存到二进制文件中。这对某些场景非常有用,例如模糊测试,但对其他场景(例如恶意软件分析)效果不佳。

使用 Dyninst,可以在每个基本块中插入 AFL++ 的插桩代码,然后将修改后的二进制文件保存下来。之后,使用 afl-fuzz 对保存后的目标文件进行模糊测试。 听起来很不错?确实如此。不过,插入指令后可能会改变进程空间中的地址,因此保证一切正常运行是一个非同小可的问题。因此,修改后的二进制文件经常会在运行时崩溃。

该方法的速度损失大约在 15%-35%,具体取决于使用 afl-dyninst 时的优化选项。

https://github.com/vanhauser-thc/afl-dyninst

Mcsema

从理论上讲,您可以使用 Mcsema 将二进制文件反编译为 LLVM IR,然后利用 llvm_mode 对其进行插桩处理。

https://github.com/lifting-bits/mcsema

Binary tracers

Pintool & DynamoRIO

Pintool 和 DynamoRIO 是动态插桩引擎,可用于运行时获取基本块信息。Pintool 仅适用于 Intel x32/x64 架构的 Linux、Mac OS 和 Windows 系统。 DynamoRIO 除此之外还支持 ARM 和 AARCH64 架构,并且速度比 Pintool 快 10 倍。

DynamoRIO的最大问题是速度(因此也包括 Pintool)。 DynamoRIO 的速度降低了 98-99%,而 Pintool 的速度降低了 99.5%。

因此,只有在其他方法都失败时,才推荐使用 DynamoRIO。而 Pintool 应仅在 DynamoRIO 不可用时作为备选。

DynamoRIO 解决方案:

Pintool 解决方案

Intel PT

如果您拥有较新的 Intel CPU,可以利用 Intel 的处理器追踪(PT)功能进行插桩分析。Intel PT 的最大问题是缓冲区较小,而且通过 PT 收集的调试信息编码复杂。 这使得解码非常耗费 CPU,因此速度很慢。 导致总体速度下降了约 70-90%(取决于实现方式和其他因素)。

AFL 的两种 Intel-PT 实现:

  1. https://github.com/junxzm1990/afl-pt => 需要 Ubuntu 14.04.05(不含更新)和 4.4 内核

  2. https://github.com/hunter-ht-2018/ptfuzzer => 需要 Ubuntu 14.04.05(不含更新)和 4.4 内核。需要在内核引导选项中启用 “nopti”。此实现比 afl-pt 更快。

另一种工具 honggfuzz https://github.com/google/honggfuzz 的 IPT 性能仅为 6%。

非 AFL++ 解决方案

还有很多二进制模糊框架。有些非常适合 CTF,但不支持大型二进制文件;有些非常慢,但有很好的路径发现能力;有些工具设置复杂。

结束语

以上就是全部内容!如果您有最新消息、纠正或更新,欢迎发送邮件至 vh@thc.org.