
Распределённый, управляемый покрытием кода, основанный на снимках фаззер для целей в пользовательском режиме и режиме ядра на Windows и Linux, с эмуляторными и гипервизорными бэкендами.
what the fuzzРаспределённый, управляемый покрытием кода, кроссплатформенный фаззер на основе снимков, предназначенный для атаки на цели в пользовательском и/или режиме ядра, работающие под управлением Microsoft Windows и Linux (экспериментально!).
what the fuzz или wtf — это распределённый, управляемый покрытием кода, настраиваемый, кроссплатформенный фаззер на основе снимков, предназначенный для атаки на цели в пользовательском и/или режиме ядра, работающие под управлением Microsoft Windows или Linux (экспериментально, см. linux_mode). Выполнение цели может осуществляться внутри эмулятора с помощью bochscpu (самый медленный, наиболее точный), внутри виртуальной машины Windows с использованием Windows Hypervisor Platform APIs или внутри виртуальной машины Linux с использованием KVM APIs (самый быстрый).
Он выявил уязвимости повреждения памяти в широком спектре программного обеспечения: IDA Pro, популярная AAA-игра, ядро Windows, клиент Microsoft RDP, драйвер дисплея NVIDIA GPU и др.
Скомпилированные бинарные файлы доступны либо из артефактов CI, либо из раздела Releases как для Windows, так и для Linux.
Если вы хотите узнать больше об истории проекта или о том, как использовать его на реальной цели, рекомендую ознакомиться с этими статьями, чтобы начать 🔥
Лучший способ опробовать возможности — поработать с модулями fuzzer_hevd / fuzzer_tlv_server. Вы можете скачать архивы target-hevd.7z / target-tlv_server.7z и извлечь их в каталог targets/. Архивы содержат структуру каталогов, ожидаемую для каждой цели:
inputs — папка, куда помещаются ваши входные тестовые примеры,outputs — папка, куда сохраняются текущие файлы minset,coverage — папка, в которой ожидаются файлы .cov,crashes — папка, куда сохраняются аварийные завершения,state — папка, где хранятся дамп памяти (mem.dmp), состояние ЦП (regs.json) и хранилище символов (symbol-store.json). Хранилище символов — это простой файл JSON, используемый в системах Linux для указания мест установки точек останова, поскольку на этих платформах нет поддержки символов / dbgeng. wtf генерирует этот файл во время выполнения каждый раз, когда вы запускаете цель на Windows.Далее предполагается, что вы скачали файл target-hevd.7z, прикреплённый к последнему релизу, и извлекли его в каталог targets вашего клона wtf. У вас должен появиться путь wtf/targets/hevd, в котором находятся каталоги inputs, outputs и т.д.
Сервер, по сути, является мозгом и отслеживает всё состояние: агрегированное покрытие кода, корпус, генерирует и распространяет тестовые примеры среди клиентов.
Вот как вы можете запустить локальный серверный узел:```text wtf.exe master --name hevd --max_len=1028 --runs=10000000
Опция `max_len` используется для ограничения размера генерируемого тестового примера, `runs` — количество генерируемых тестовых примеров, `address` указывает, где **wtf** должен прослушивать, `target` — это директория с деревом каталогов, которое мы описали выше (пользователь также может переопределить эти директории с помощью `--input` / `--output` / `--crashes`), а `name` задает имя вашего модуля фаззинга, чтобы мастер мог вызвать вашу функцию-генератор, если вы ее определили.
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/4a03fc75eed3ed5a92f7f10def697dbf220ae36b0701f05b37759eb432fc0fd9.webp">
</p>
### Узлы фаззинга
Клиентские узлы запускают тестовый пример, сгенерированный и распределенный сервером, и передают результат обратно серверу (покрытие кода, результат и т.д.).
Вот как вы запустите клиентский узел, использующий бэкенд *bochscpu*:```text
wtf.exe fuzz --name hevd --limit 10000000
Подкоманда fuzz используется с опцией name, чтобы указать, какой модуль фаззера нужно использовать; backend задаёт бэкенд выполнения, а limit — максимальное количество инструкций для выполнения на один тестовый пример (в зависимости от бэкенда значение этой опции различается).
Если вы хотите запустить тестовый пример (или папку, заполненную тестовыми примерами), вы можете использовать подкоманду run.
Вот как вы могли бы бы запустить тестовый пример crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0:
$HOME/FUZZAN/fuzzer.py $HOME/FUZZAN/seeds/example $HOME/FUZZAN/workdir/ -b frida -t 60000 -m 10000 -p 1000 -c -v $HOME/FUZZAN/workdir/crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0```
wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/49b3ca8582d6314724e5615c687472499d5f8f41d04c9543c0a6a070c51f56f8.webp">
</p>
### Minseting корпуса
Для minset корпуса необходимо использовать серверный узел и столько клиентских узлов, сколько нужно, как при фаззинге. Вы можете просто установить `runs` optins в 0.
Вот как вы можете minset корпус из `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
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/340b4b251e378eaf21c6f5ffdd4f013bd2cef89f23a604cffd0ebf897d27c71f.webp">
</p>
Для символизации трасс выполнения следует использовать [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
Если вам требуется более широкая контекстная осведомленность, бэкенд bochscpu позволяет генерировать трассы выполнения, которые можно загрузить в Tenet — проводник трасс. В примере ниже я начинаю с сбоя в memmove и возвращаюсь назад, чтобы выяснить, откуда берется исходный указатель (user-mode!):```
wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 --trace-type=tenet
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/34df2f5ca8f378db664d3f01bcbefdd43409e300d256d50e3f4f630eb06cc7be.webp">
</p>
### Генерация трасс покрытия кода
Чтобы сгенерировать трассы покрытия кода, вы можете просто использовать подкоманду `run` с опцией `--trace-type=cov`.
Вот как можно сгенерировать трассы покрытия кода для всех файлов внутри папки `minset` и сохранить их в папку `coverage-traces`:```
wtf.exe run --name hevd --input minset --trace-path=coverage-traces --trace-type=cov
Эти трассировки невозможно напрямую загрузить в lighthouse, так как они не символизованы.
Вот как можно символизовать все файлы внутри папки coverage-traces и записать результаты в coverage-traces-symbolized:```
symbolizer-rs.exe --trace coverage-traces -o coverage-traces-symbolized --style modoff
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/8a3c3ca18571abef16a9f87f736fe4d52ac102f41095eab4db32533cb5b198b4.webp">
</p>
И наконец, вы можете загрузить их в [lighthouse](https://github.com/gaasedelen/lighthouse):
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/35d9c25c3d26abf5761fec9e7848ba09d090c7aab32322a740732d82e85964e8.webp">
</p>
Также, если вас не интересует индивидуальное покрытие кода, главный процесс поддерживает файл `coverage.cov`, содержащий уникальное агрегированное покрытие кода, которое было отработано. Это позволяет быстро проверять глобальное покрытие кода во время работы фаззера.
## Как это работает?
**wtf** работает в пользовательском и режиме ядра через *execution backend* и полагается на пользователя для вставки тестовых примеров в цель. В отличие от других классических инструментов фаззинга, **wtf** не выполняет большую часть тяжелой работы — это делает пользователь. Пользователь должен очень хорошо знать целевую программу, и подключение цели — это итеративный процесс, который займет время. Однако он предлагает большую гибкость, если вы готовы заняться хакингом :)
Обычный рабочий процесс для подключения цели выглядит следующим образом:
1. Запустите вашу цель в виртуальной машине Hyper-V с Windows, одним виртуальным процессором и 4 ГБ ОЗУ.
1. Приведите цель в нужное состояние с помощью [KD](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/). Например, для атаки на обработчик IOCTL драйвера [HEVD](https://github.com/hacksysteam/HackSysExtremeVulnerableDriver) я остановил цель в пользовательском режиме непосредственно перед вызовом клиентом [DeviceIoControl](https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol). Это будет зависеть от ваших целей, но, вероятно, вы захотите остановиться как можно ближе к коду, который хотите фаззить.
```
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)}
```
1. Используйте [snapshot](https://github.com/0vercl0k/snapshot) для создания дампа ядра при сбое и файла `regs.json`, содержащего состояние ЦП. Рекомендую сохранять эти файлы в каталоге `state` внутри вашего каталога `target` (например, `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 <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!
```
1. Создайте [модуль фаззера](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc), напишите код, который [вставляет тестовый пример](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L20) в вашу цель, и определите [различные](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L81) [условия](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L104) [для обнаружения сбоев](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L115) или [окончания тестового примера](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L69).
1. Вы также можете создать свой собственный мутатор/генератор, унаследовавшись от интерфейса [Mutator_t](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/mutator.h). Файл [fuzzer_tlv_server.cc](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_tlv_server.cc) — хороший пример, чтобы понять, как можно реализовать свой собственный.
На этом этапе вы должны начать итеративно проверять, что модуль фаззера работает как ожидается. Исполнительные бэкенды — это черный ящик, поэтому вам следует генерировать трассы выполнения, чтобы убедиться, что код проходит по правильным путям и делает правильные вещи. На этом этапе я в основном использую бэкенд [bochscpu](https://github.com/yrp604/bochscpu), так как он полностью детерминирован, быстро запускается, позволяет генерировать трассы выполнения, покрытие кода получается автоматически и т.д. В целом, это более удобная среда для разработки и прототипирования.
Когда вы будете удовлетворены модулем, можно начать настраивать его работу с бэкендами [winhv](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/whv_backend.h) / [kvm](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/kvm_backend.h), если вам нужно запускать его под ними. Одно из основных отличий бэкенда *bochscpu* от других заключается в том, что другие используют программные точки останова для получения информации о покрытии кода. В результате вам потребуется загрузить модули, для которых нужно покрытие, в [IDA](https://hex-rays.com/IDA-pro/) и использовать скрипт [gen_coveragefile_ida.py](https://github.com/0vercl0k/wtf/blob/HEAD/scripts/gen_coveragefile_ida.py) для создания простого JSON-файла, который загружается wtf. Вы можете сгенерировать этот JSON-файл самостоятельно с помощью любого другого инструмента: по сути, это список виртуальных адресов базовых блоков.
Вы также можете нацеливаться на приложения [WoW64](https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details), используя команду `!wow64exts.sw` в Windbg для переключения в 64-битный контекст непосредственно перед созданием снимка (спасибо [@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 — пример такого сервера, где выполнение функции разбора только с одним тесткейсом не позволит обнаружить ошибки.
Чтобы справиться с этой задачей, посмотрите fuzzer_tlv_server.cc, который показывает пример решения этой проблемы.
wtf включает два популярных общих мутатора: libfuzzer и honggfuzz. Возможно, вы захотите предоставить свой собственный или генерировать тесткейсы самостоятельно.
Для этого вы можете создать подкласс интерфейса Mutator_t и зарегистрировать функцию, которая создает экземпляр вашего мутатора, при определении вашего модуля фаззинга:```c++ class CustomMutator_t : public Mutator_t { public: static std::unique_ptr<Mutator_t> Create(std::mt19937_64 &Rng, const size_t TestcaseMaxSize) { return std::make_unique<CustomMutator_t>(Rng, TestcaseMaxSize); } // ... };
Target_t target("target", Init, InsertTestcase, Restore, CustomMutator_t::Create);
Посмотрите на класс [CustomMutator_t](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_tlv_server.cc) в модуле [fuzzer_tlv_server.cc](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_tlv_server.cc) для полного примера.
## Бэкенды выполнения
В этом разделе я кратко упомяну различные различия между бэкендами выполнения.
### bochscpu
- ✅ Полное покрытие кода всей системы (покрытие граней доступно через `--edges`),
- ✅ Страничная подкачка по требованию,
- ✅ Таймаут — это количество инструкций, что очень точно,
- ✅ Полные трассы выполнения поддерживаются,
- ✅ Полностью детерминировано,
- ❌Скорость кажется хорошей для коротких выполнений, но не для длинных (~в 100 раз медленнее, чем KVM, когда я фаззил IDA).
### whv
- ✔ Покрытие кода через программные точки останова,
- ❌ Страничная подкачка по требованию, поэтому запуск медленный (так как нужно загрузить полный дамп краша в память),
- ✔ Таймаут реализован с помощью таймера,
- ✅ Полные трассы выполнения поддерживаются, но они медленные (выход из VMX затратен),
- ✔ Детерминировано при ручной обработке источника недетерминизма (например, патч `nt!ExGenRamdom`, использующего `rdrand`),
- ✔ Скорость кажется приемлемой для длительных выполнений (хотя в whv много узких мест; ~в 10 раз медленнее, чем kvm, когда я фаззил IDA).
### KVM
- ✔ Покрытие кода через программные точки останова,
- ✅ Страничная подкачка по требованию поддерживается через UFDD,
- ✔ Таймаут реализован с помощью таймера. ✅ Если оборудование поддерживает виртуализацию PMU, она используется для генерации [PMI](https://forum.osdev.org/viewtopic.php?f=1&t=27040) после X завершённых инструкций (`MSR_IA32_FIXED_CTR0`),
- ✅ Полные трассы выполнения поддерживаются, но они медленные (выход из VMX затратен),
- ✔ Детерминировано при ручной обработке источника недетерминизма (например, патч `nt!ExGenRamdom`, использующего `rdrand`),
- ✅ Самый быстрый для длительных выполнений (~500 млн - 1.5 млрд инструкций; ~в 100 раз быстрее, чем *bochscpu*, ~в 10 раз быстрее, чем *whv*, когда я фаззил IDA).
## Сборка
Система [CI](https://github.com/0vercl0k/wtf/actions/workflows/wtf.yml) собирает **wtf** на Ubuntu с использованием как [clang++](https://clang.llvm.org/) / [g++](https://gcc.gnu.org/gcc-11/), на Windows с использованием [Visual Studio](https://visualstudio.microsoft.com/vs/community/) от Microsoft и на OSX с использованием [clang++](https://clang.llvm.org/).
Чтобы собрать его самостоятельно, нужно запустить *Командную строку разработчика Visual Studio* и либо запустить [build-release.bat](https://github.com/0vercl0k/wtf/blob/HEAD/src/build/build-release.bat), который использует генератор [Ninja](https://ninja-build.org/), либо [build-release-msvc.bat](https://github.com/0vercl0k/wtf/blob/HEAD/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
Особая благодарность: