0vercl0k/wtf
GitHub: 0vercl0k/wtf
wtf 是一款分布式、跨平台的快照式模糊测试框架,专为攻击 Windows 内核及用户模式目标而设计。
Stars: 1740 | Forks: 148
what the fuzz
A distributed, code-coverage guided, cross-platform snapshot-based fuzzer designed for attacking user and or kernel-mode targets running on Microsoft Windows and Linux user-mode (experimental!).
## 概述
**what the fuzz**(或 **wtf**)是一个分布式、基于代码覆盖率引导、可定制、跨平台的基于快照的 Fuzzer,专为攻击运行在 Microsoft Windows 或 Linux 上的用户模式和/或内核模式目标而设计(**实验性**,参见 [linux_mode](linux_mode/))。目标的执行可以在带有 [bochscpu](https://github.com/yrp604/bochscpu) 的模拟器内进行(最慢,最精确),也可以在带有 [Windows Hypervisor Platform APIs](https://docs.microsoft.com/en-us/virtualization/api/hypervisor-platform/hypervisor-platform) 的 Windows 虚拟机内进行,或者在带有 [KVM APIs](https://www.kernel.org/doc/html/latest/virt/kvm/api.html) 的 Linux 虚拟机内进行(最快)。
它在多种软件中发现了内存损坏漏洞:[IDA Pro](https://github.com/0vercl0k/fuzzing-ida75)、一款流行的 [AAA 游戏](https://blog.ret2.io/2021/07/21/wtf-snapshot-fuzzing/)、[Windows 内核](https://microsoft.fandom.com/wiki/Architecture_of_Windows_NT)、[Microsoft RDP 客户端](https://www.hexacon.fr/slides/Hexacon2022-Fuzzing_RDPEGFX_with_wtf.pdf)、[NVIDIA GPU 显示驱动](https://nvidia.custhelp.com/app/answers/detail/a_id/5383)等。
编译好的二进制文件可以从 [CI artifacts](https://github.com/0vercl0k/wtf/actions/workflows/wtf.yml) 或 [Releases](https://github.com/0vercl0k/wtf/releases) 部分获取,支持 Windows 和 Linux。
如果你想了解更多关于它的历史或如何在真实目标上使用它,我建议阅读以下文章来入门 🔥
- [Building a new snapshot fuzzer & fuzzing IDA](https://doar-e.github.io/blog/2021/07/15/building-a-new-snapshot-fuzzer-fuzzing-ida/)
- [Fuzzing Modern UDP Game Protocols With Snapshot-based Fuzzers](https://blog.ret2.io/2021/07/21/wtf-snapshot-fuzzing/),作者 [Markus Gaasedelen](https://twitter.com/gaasedelen)
- [Fuzzing RDPEGFX with "what the fuzz"](https://thalium.github.io/blog/posts/rdpegfx/),作者 [Colas Le Guernic](https://github.com/clslgrnc)、Jérémy Rubert 和 Anonymous
- [A Journey to Network Protocol Fuzzing – Dissecting Microsoft IMAP Client Protocol](https://www.fortinet.com/blog/threat-research/analyzing-microsoft-imap-client-protocol),作者 [Wayne Chin Yick Low](https://www.fortinet.com/blog/search?author=Wayne+Chin+Yick+Low)
- [Trail Of Bits' Testing Handbook](https://appsec.guide/) 中的 [Snapshot Fuzzing](https://appsec.guide/docs/fuzzing/snapshot-fuzzing/) 章节
- [Attacking EDRs Part 4: Fuzzing Defender's Scanning and Emulation Engine (mpengine.dll)](https://labs.infoguard.ch/posts/attacking_edr_part4_fuzzing_defender_scanning_and_emulation_engine/),作者 [Manuel Feifel](https://x.com/p0w1_)
## 使用方法
试用功能的最佳方式是使用 [fuzzer_hevd](src/wtf/fuzzer_hevd.cc) / [fuzzer_tlv_server](src/wtf/fuzzer_tlv_server.cc) 模块。你可以下载 [target-hevd.7z](https://github.com/0vercl0k/wtf/releases) / [target-tlv_server.7z](https://github.com/0vercl0k/wtf/releases) 归档文件并将它们解压到 `targets/` 目录中。这些归档文件包含了每个目标所期望的目录树结构:
- `inputs` 是存放输入测试用例的文件夹,
- `outputs` 是存放当前最小集文件的文件夹,
- `coverage` 是存放 `.cov` 文件的文件夹,
- `crashes` 是存放崩溃信息的文件夹,
- `state` 是存放内存转储 (`mem.dmp`)、CPU 状态 (`regs.json`) 和符号存储区 (`symbol-store.json`) 的文件夹。符号存储区是一个简单的 JSON 文件,在 Linux 系统上用于确定断点的位置,因为这些平台不支持符号 / dbgeng。每次你在 Windows 上运行目标时,**wtf** 都会在运行时生成此文件。
接下来的内容假设你下载了附在最新版本中的 [target-hevd.7z](https://github.com/0vercl0k/wtf/releases) 文件,并将其解压到了你克隆的 **wtf** 的 `targets` 目录中。你应该拥有 `wtf/targets/hevd`,其中包含 `inputs` / `outputs` 等目录。
### 启动服务器节点
服务器基本上是大脑,负责跟踪所有状态:聚合的代码覆盖率、语料库,它生成测试用例并将其分发给客户端。
这是你启动本地服务器节点的方式:
```
wtf.exe master --name hevd --max_len=1028 --runs=10000000
```
`max_len` 选项用于限制生成的测试用例的大小,`runs` 是它将生成的测试用例数量,`address` 指定 **wtf** 需要监听的地址,`target` 是一个包含我们上述描述的目录树的目录(用户也可以选择使用 `--input` / `--output` / `--crashes` 来覆盖这些目录),`name` 指定你的 fuzzing 模块名称,以便 master 可以调用你的生成器函数(如果你已定义)。
### Fuzzing 节点
客户端节点运行由服务器生成和分发的测试用例,并将结果(代码覆盖率、结果等)传回服务器。
这是你启动一个使用 *bochscpu* 后端的客户端节点的方式:
```
wtf.exe fuzz --name hevd --limit 10000000
```
`fuzz` 子命令与 `name` 选项一起使用,以指定需要使用的 fuzzer 模块,`backend` 指定执行后端,`limit` 指定每个测试用例要执行的最大指令数(根据后端的不同,此选项具有不同的含义)。
### 运行测试用例
如果你想运行一个测试用例(或一个充满测试用例的文件夹),你可以使用 `run` 子命令。
这是你运行 `crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0` 测试用例的方式:
```
wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0
```
### 最小化语料库
要最小化语料库,你需要使用一个服务器节点和尽可能多的客户端节点,就像你进行 fuzzing 工作一样。你可以简单地将 `runs` 选项设置为 0。
这是你将 `outputs` 中的语料库最小化到 `minset` 目录的方式(也突出了你可以如何覆盖 `inputs` 和 `outputs` 目录):
```
wtf.exe master --name hevd --max_len=1028 --runs=0 --inputs=outputs --outputs=minset
```
### 生成执行跟踪
在执行后端中进行内省的主要机制是生成执行跟踪。*bochscpu* 是执行此操作的最快后端,因为在其他后端上退出 VMX 模式非常昂贵。
这是你为 `crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0` 测试用例生成执行跟踪的方式:
```
wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 --trace-type=rip
```
要符号化执行跟踪,你应该使用 [symbolizer-rs](https://github.com/0vercl0k/symbolizer-rs)。这是你符号化上面生成的 `crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0.trace` 执行跟踪的方式:
```
symbolizer-rs.exe --trace crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0.rip.trace
```
## 生成 Tenet 跟踪
如果你发现自己需要更多的上下文感知能力,*bochscpu* 后端允许你生成可以加载到 [Tenet](https://github.com/gaasedelen/tenet) 跟踪浏览器中的执行跟踪。在下面的内容中,我从 `memmove` 中的一个崩溃开始,并回溯以找出源指针的来源(用户模式!):
```
wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 --trace-type=tenet
```
### 生成代码覆盖率跟踪
要生成代码覆盖率跟踪,你可以简单地使用带有 `--trace-type=cov` 选项的 `run` 子命令。
这是你为 `minset` 文件夹中的所有文件生成代码覆盖率跟踪并将它们存储在 `coverage-traces` 文件夹中的方式:
```
wtf.exe run --name hevd --input minset --trace-path=coverage-traces --trace-type=cov
```
这些跟踪不能直接加载到 [lighthouse](https://github.com/gaasedelen/lighthouse) 中,因为它们没有经过符号化。
这是你符号化 `coverage-traces` 文件夹中的所有文件并将结果写入 `coverage-traces-symbolized` 的方式:
```
symbolizer-rs.exe --trace coverage-traces -o coverage-traces-symbolized --style modoff
```
最后,你可以将它们加载到 [lighthouse](https://github.com/gaasedelen/lighthouse) 中:
另外,如果你不关心单独的代码覆盖率,master 会维护一个 `coverage.cov` 文件,其中包含已执行的唯一聚合代码覆盖率。这使得在 fuzzing 作业期间可以非常快速地检查全局代码覆盖率。
## 它是如何工作的?
**wtf** 通过*执行后端*运行用户和内核模式,并依赖用户将测试用例插入目标中。与其他经典的 fuzzer 工具不同,**wtf** 并不承担大部分繁重的工作;用户才是。用户需要非常了解被测目标,并且目标接入是一个需要时间的迭代过程。如果你准备好开始黑客行为,它提供了很大的灵活性 :)
接入目标的通常工作流程如下:
1. 让你的目标在运行 Windows 且具有一个虚拟 CPU 和 4GB 内存的 Hyper-V VM 中运行。
2. 使用 [KD](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/) 将目标置于所需状态。例如,为了针对 [HEVD](https://github.com/hacksysteam/HackSysExtremeVulnerableDriver) 的 IOCTL 处理程序,我选择在客户端调用 [DeviceIoControl](https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol) 之前在用户模式下停止目标。这将根据你的目标而有所不同,但你可能希望它接近你想要 fuzz 的代码。
kd> r
rax=000000dfd98ff3d0 rbx=0000000000000088 rcx=0000000000000088
rdx=00000000deadbeef rsi=0000000000000000 rdi=0000000000000000
rip=00007ff6f5bb111e rsp=000000dfd98ff380 rbp=0000000000000000
r8=000000dfd98ff3d0 r9=0000000000000400 r10=000002263e823055
r11=00007ff6f5bcb54d r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl nz na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
hevd_client!main+0xae:
00007ff6`f5bb111e ff15dc1e0100 call qword ptr [hevd_client!_imp_DeviceIoControl (00007ff6`f5bc3000)] ds:002b:00007ff6`f5bc3000={KERNEL32!DeviceIoControlImplementation (00007ff8`3e2e6360)}
3. 使用 [snapshot](https://github.com/0vercl0k/snapshot) 生成内核崩溃转储以及包含 CPU 状态的 `regs.json` 文件。我建议将这些文件转储到你的 `target` 目录下的 `state` 目录中(例如 `targets/hevd/state`):
kd> .load c:\work\codes\snapshot\target\release\snapshot.dll
kd> !snapshot -h
[snapshot] Usage: snapshot [OPTIONS] [STATE_PATH]
Arguments:
[STATE_PATH] The path to save the snapshot to
Options:
-k, --kind
The kind of snapshot to take [default: full] [possible values: active-kernel, full]
-h, --help Print help
kd> !snapshot c:\work\codes\wtf\targets\hevd\state
[snapshot] Dumping the CPU state into c:\work\codes\wtf\targets\hevd\state\regs.json..
[snapshot] Dumping the memory state into c:\work\codes\wtf\targets\hevd\state\mem.dmp..
Creating c:\\work\\codes\\wtf\\targets\\hevd\\state\\mem.dmp - Full memory range dump
0% written.
5% written. 1 min 50 sec remaining.
10% written. 1 min 17 sec remaining.
15% written. 1 min 30 sec remaining.
[...]
Wrote 4.0 GB in 1 min 32 sec.
The average transfer rate was 44.5 MB/s.
Dump successfully written
[snapshot] Done!
4. 创建一个 [fuzzer 模块](src/wtf/fuzzer_hevd.cc),编写[插入测试用例](src/wtf/fuzzer_hevd.cc#L20)到你的目标的代码,并定义[各种](src/wtf/fuzzer_hevd.cc#L81) [条件](src/wtf/fuzzer_hevd.cc#L104)来[检测崩溃](src/wtf/fuzzer_hevd.cc#L115)或[测试用例的结束](src/wtf/fuzzer_hevd.cc#L115)。
5. 你也可以通过继承 [Mutator_t](src/wtf/mutator.h) 接口来创建你自己的变异器 / 生成器。[fuzzer_tlv_server.cc](src/wtf/fuzzer_tlv_server.cc) 是一个很好的例子,可以帮助你理解如何实现自己的变异器。
此时,你应该开始迭代并验证 fuzzer 模块是否按预期工作。执行后端是一个黑盒,所以你应该生成执行跟踪以确保它通过正确的路径,做正确的事情。在这个阶段,我主要使用 [bochscpu](https://github.com/yrp604/bochscpu) 后端,因为它是完全确定性的,启动速度快,可以生成执行跟踪,代码覆盖率是免费获取的等等。总的来说,它是一个更适合开发和原型的环境。
一旦你对模块感到满意,如果你需要让它在这些环境下运行,你可以开始考虑让它在 [winhv](src/wtf/whv_backend.h) / [kvm]() 后端上工作。*bochscpu* 后端与其他后端的一个主要区别是,其他后端使用软件断点来提供代码覆盖率信息。因此,你需要将你想要获取覆盖率的模块加载到 [IDA](https://hex-rays.com/IDA-pro/) 中,并使用 [gen_coveragefile_ida.py](scripts/gen_coveragefile_ida.py) 脚本生成一个由 wtf 加载的简单 JSON 文件。你可以自由使用你喜欢的任何工具生成这个 JSON 文件:它基本上是一个基本块虚拟地址的列表。
你也可以通过使用 `!wow64exts.sw` Windbg 命令在创建快照之前切换到 64 位上下文来针对 [WoW64](https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details) 应用程序(感谢 [@cube0x8](https://twitter.com/cube0x8) 分享这个技巧!):
```
32.kd:x86> !wow64exts.sw
The context is partially valid. Only x86 user-mode context is available.
Switched to Host mode
32.kd> !snapshot
```
## 如何向我的目标传递多数据包?
复杂的目标通常也具有复杂的状态,你可能需要在一个会话中传递多个测试用例才能触发复杂的问题。[tlv_server.cc](src/tlv_server/tlv_server.cc) 就是这样一个服务器的例子,仅用一个测试用例 exercising 解析函数不足以发现错误。
要处理这种情况,请查看 [fuzzer_tlv_server.cc](src/wtf/fuzzer_tlv_server.cc),其中展示了如何解决此问题的示例。
## 如何提供自定义变异器 / 生成器?
**wtf** 附带了两个流行的通用变异器:[libfuzzer](src/wtf/mutator.h) 和 [honggfuzz](src/wtf/mutator.h)。你可能想要提供自己的变异器或自己生成测试用例。
为此,你可以继承 [Mutator_t](src/wtf/mutator.h) 接口,并在定义你的 fuzzing 模块时注册实例化你的变异器的函数:
```
class CustomMutator_t : public Mutator_t {
public:
static std::unique_ptr Create(std::mt19937_64 &Rng,
const size_t TestcaseMaxSize) {
return std::make_unique(Rng, TestcaseMaxSize);
}
// ...
};
Target_t target("target", Init, InsertTestcase, Restore, CustomMutator_t::Create);
```
查看 [fuzzer_tlv_server.cc](src/wtf/fuzzer_tlv_server.cc) 模块中的 [CustomMutator_t](src/wtf/fuzzer_tlv_server.cc) 类以获取完整示例。
## 执行后端
在本节中,我简要介绍各执行后端之间的差异。
### bochscpu
- ✅ 全系统代码覆盖率(可通过 `--edges` 获取边缘覆盖率),
- ✅ 按需分页,
- ✅ 超时是指令数,非常精确,
- ✅ 支持完整的执行跟踪,
- ✅ 完全确定性,
- ❌ 对于短执行速度似乎不错,但对于长执行则不然(当我 fuzzing IDA 时,比 KVM 慢约 100 倍)。
### whv
- ✔ 通过软件断点实现代码覆盖率,
- ❌ 按需分页,因此启动缓慢(因为需要将完整的崩溃转储加载到内存中),
- ✔ 超时通过计时器实现,
- ✅ 支持完整的执行跟踪,但速度较慢(退出 VMX 成本很高),
- ✔ 如果手动处理非确定性源(例如,修补使用 `rdrand` 的 `nt!ExGenRamdom`),则是确定性的,
- ✔ 对于长执行速度似乎还可以(尽管在 whv 中有很多瓶颈;当我 fuzzing IDA 时,比 kvm 慢约 10 倍)。
### KVM
- ✔ 通过软件断点实现代码覆盖率,
- ✅ 通过 UFDD 支持按需分页,
- ✔ 超时通过计时器实现。✅ 如果硬件支持 PMU 虚拟化,则使用它在 X 条已执行指令后生成 [PMI](https://forum.osdev.org/viewtopic.php?f=1&t=27040)(`MSR_IA32_FIXED_CTR0`),
- ✅ 支持完整的执行跟踪,但速度较慢(退出 VMX 成本很高),
- ✔ 如果手动处理非确定性源(例如,修补使用 `rdrand` 的 `nt!ExGenRamdom`),则是确定性的,
- ✅ 对于长执行速度最快(~500m - 15 亿条指令;当我 fuzzing IDA 时,比 *bochscpu* 快约 100 倍,比 *whv* 快约 10 倍)。
## 构建
[CI](https://github.com/0vercl0k/wtf/actions/workflows/wtf.yml) 在 Ubuntu 上使用 [clang++](https://clang.llvm.org/) / [g++](https://gcc.gnu.org/gcc-11/),在 Windows 上使用 Microsoft 的 [Visual Studio](https://visualstudio.microsoft.com/vs/community/),在 OSX 上使用 [clang++](https://clang.llvm.org/) 构建 **wtf**。
要自己构建它,你需要启动一个 *Visual Studio Developer Command Prompt* 并运行 [build-release.bat](src/build/build-release.bat)(使用 [Ninja](https://ninja-build.org/) 生成器)或 [build-release-msvc.bat](src/build/build-release-msvc.bat) 来生成 Visual Studio 解决方案文件:
```
(base) wtf\src\build>build-release.bat
[...]
[2/2] Linking CXX executable wtf.exe
(base) wtf\src\build_msvc>..\build\build-release-msvc.bat
[...]
Finished generating code
wtf.vcxproj -> wtf\src\build_msvc\RelWithDebInfo\wtf.exe
Building Custom Rule wtf/src/CMakeLists.txt
```
## 作者
* Axel '[0vercl0k](https://twitter.com/0vercl0k)' Souchet
## 贡献者
特别感谢:
- [@yrp604](https://github.com/yrp604) 在整个项目中提供了宝贵的意见,
- [@masthoon](https://github.com/masthoon) 建议编写一个针对 [HEVD](https://github.com/hacksysteam/HackSysExtremeVulnerableDriver) 安全模式的演示,
- [Markus Gaasedelen](https://github.com/0vercl0k/wtf/pull/12/) 添加了 Tenet 支持,
- [@y0ny0ns0n](https://github.com/y0ny0ns0n) 贡献了 [multi-input fuzzing example](https://github.com/0vercl0k/wtf/pull/67),
- [Colas Le Guernic](https://github.com/clslgrnc) / Jérémy Rubert / Anonymous 实现了 [bochscpu 的边缘覆盖率](https://github.com/0vercl0k/wtf/pull/137),
- [@1ndahous3](https://github.com/1ndahous3) 贡献了 [generic ioctl fuzzer module](https://github.com/0vercl0k/wtf/pull/155),
- 来自 Cisco ASIG 的 Jason Crowder / [Kyle Ossinger](https://k0ss.net/) 贡献了 [Linux 模式](https://github.com/0vercl0k/wtf/pull/192),
- 以及所有其他贡献者 🙏
[  ](https://github.com/0vercl0k/wtf/graphs/contributors)标签:ASN解析, Bochscpu, Bro, C++, Fuzzer, Fuzzing, IDA Pro, KVM, pocsuite3, RFI远程文件包含, Rust, Web报告查看器, Windows Hypervisor, Windows 安全, XXE攻击, 云资产清单, 内存破坏, 内核模式, 分布式系统, 响应大小分析, 安全测试, 快照模糊测试, 情报收集, 攻击性安全, 数据擦除, 文档安全, 漏洞研究, 灰盒测试, 用户模式, 网络流量审计, 虚拟化, 覆盖率引导, 软件测试, 逆向工程