
Fuzzer distribuido, guiado por cobertura de código y basado en instantáneas para objetivos en modo usuario y kernel en Windows y Linux, con backends de emulador e hipervisor.
what the fuzzUn fuzzer distribuido, guiado por cobertura de código, basado en instantáneas y multiplataforma, diseñado para atacar objetivos en modo usuario y/o núcleo que se ejecuten en Microsoft Windows y modo usuario de Linux (¡experimental!).
what the fuzz o wtf es un fuzzer distribuido, guiado por cobertura de código, personalizable, basado en instantáneas y multiplataforma, diseñado para atacar objetivos en modo usuario y/o núcleo que se ejecuten en Microsoft Windows o Linux (experimental, consulte linux_mode). La ejecución del objetivo se puede realizar dentro de un emulador con bochscpu (el más lento, el más preciso), dentro de una máquina virtual de Windows con las APIs de la Plataforma de Hipervisor de Windows o dentro de una máquina virtual de Linux con las APIs de KVM (la más rápida).
Ha descubierto vulnerabilidades de corrupción de memoria en una amplia gama de software: IDA Pro, un popular juego AAA, el kernel de Windows, el cliente RDP de Microsoft, el controlador de pantalla GPU de NVIDIA, etc.
Los binarios compilados están disponibles tanto desde los artefactos de CI como desde la sección de Lanzamientos para Windows y Linux.
Si desea leer más sobre su historia o cómo usarlo en un objetivo real, recomiendo echar un vistazo a estas publicaciones para comenzar 🔥
La mejor manera de probar las características es trabajar con los módulos fuzzer_hevd / fuzzer_tlv_server. Puede descargar los archivos target-hevd.7z / target-tlv_server.7z y extraerlos en el directorio targets/. Los archivos contienen los árboles de directorios esperados para cada objetivo:
inputs es la carpeta donde se colocan los casos de prueba de entrada,outputs es la carpeta donde se guardan los archivos del minset actual,coverage es la carpeta donde se espera que estén los archivos .cov,crashes es donde se guardan los fallos,state es donde se almacenan el volcado de memoria (mem.dmp), el estado de la CPU (regs.json) y el almacén de símbolos (symbol-store.json). El almacén de símbolos es un simple archivo JSON que se utiliza en sistemas Linux para saber dónde colocar puntos de interrupción, ya que no hay soporte para símbolos / dbgeng en esas plataformas. wtf genera este archivo en tiempo de ejecución cada vez que se ejecuta el objetivo en Windows.Lo que sigue asume que ha descargado el archivo target-hevd.7z adjunto a la última versión y lo ha extraído en el directorio targets de su clon de wtf. Debería tener wtf/targets/hevd en el que encontrará los directorios inputs / outputs, etc.
El servidor es básicamente el cerebro y realiza un seguimiento de todo el estado: la cobertura de código agregada, el corpus, genera y distribuye los casos de prueba al cliente.
Así es como podría elegir lanzar un nodo servidor local:```text wtf.exe master --name hevd --max_len=1028 --runs=10000000
La opción `max_len` se utiliza para limitar el tamaño del caso de prueba generado, `runs` es el número de casos de prueba que generará, `address` especifica dónde debe escuchar **wtf**, `target` es un directorio con el árbol de directorio que describimos anteriormente (el usuario también puede optar por sobrescribir esos directorios con `--input` / `--output` / `--crashes`) y `name` especifica el nombre de tu módulo de fuzzing para que el maestro pueda invocar tu función generadora si has definido una.
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/4a03fc75eed3ed5a92f7f10def697dbf220ae36b0701f05b37759eb432fc0fd9.webp">
</p>
### Nodos de fuzzing
Los nodos cliente ejecutan un caso de prueba que ha sido generado y distribuido por el servidor y comunican el resultado de vuelta al servidor (cobertura de código, resultado, etc.).
Así es como iniciarías un nodo cliente que utiliza el backend *bochscpu*:
wtf client --address localhost:26001 --corpus /tmp/corpus --crashes /tmp/crashes
wtf.exe fuzz --name hevd --limit 10000000
```
El subcomando `fuzz` se utiliza con la opción `name` para especificar qué módulo de fuzzing debe usarse, `backend` especifica el backend de ejecución y `limit` el número máximo de instrucciones a ejecutar por caso de prueba (dependiendo del backend, esta opción tiene un significado diferente).
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/3e534c8f6ad3507bfb61307e00957fcbf9b32de3645979be895cccfb5ceb9b26.webp">
</p>
### Ejecutar un caso de prueba
Si desea ejecutar un caso de prueba (o una carpeta llena de casos de prueba), puede usar el subcomando `run`.
Así es como se ejecutaría el caso de prueba `crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0`:
```bash
$AFL_BASE/afl-fuzz -S secondary_instance -i /path/to/seeds -o output_dir ./program
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 a corpus
Para minset un corpus, necesitas usar un nodo servidor y tantos nodos cliente como necesites, igual que harías para un trabajo de fuzzing. Puedes simplemente establecer las opciones `runs` a 0.
Así es como minsetarías el corpus en `outputs` dentro del directorio `minset` (también destaca cómo puedes sobrescribir los directorios `inputs` y `outputs`):```
wtf.exe master --name hevd --max_len=1028 --runs=0 --inputs=outputs --outputs=minset
El mecanismo principal disponible para inspeccionar en un backend de ejecución es generar una traza de ejecución. bochscpu es el backend más rápido para hacerlo, porque salir del modo VMX es muy costoso en los otros backends.
Así es como se generaría una traza de ejecución para el caso de prueba 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>
Para simbolizar los rastros de ejecución deberías usar [symbolizer-rs](https://github.com/0vercl0k/symbolizer-rs). Así es como simbolizarías el rastro de ejecución `crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0.trace` generado anteriormente:```
symbolizer-rs.exe --trace crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0.rip.trace
Si necesitas más conciencia contextual, el backend bochscpu te permite generar trazas de ejecución que se pueden cargar en el explorador de trazas Tenet. A continuación, comienzo desde un fallo en memmove y retrocedo para descubrir de dónde proviene el puntero de origen (¡modo usuario!):```
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>
### Generación de trazas de cobertura de código
Para generar trazas de cobertura de código, puedes usar simplemente el subcomando `run` con la opción `--trace-type=cov`.
Así es como generarías trazas de cobertura de código para todos los archivos dentro de la carpeta `minset` y las almacenarías en la carpeta `coverage-traces`:```
wtf.exe run --name hevd --input minset --trace-path=coverage-traces --trace-type=cov
Esos traces no se pueden cargar directamente en lighthouse porque no están simbolizados.
Así es como simbolizarías todos los archivos dentro de la carpeta coverage-traces y escribirías los resultados en 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>
Y finalmente, puedes cargarlos en [lighthouse](https://github.com/gaasedelen/lighthouse):
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/35d9c25c3d26abf5761fec9e7848ba09d090c7aab32322a740732d82e85964e8.webp">
</p>
Además, si no te importa la cobertura de código individual, el *master* mantiene un archivo `coverage.cov` que contiene la cobertura de código agregada única que se ha ejercitado. Facilita verificar rápidamente la cobertura de código global durante un trabajo de fuzzing.
## ¿Cómo funciona?
**wtf** ejecuta modo usuario y modo kernel a través de un *backend de ejecución* y depende del usuario para insertar casos de prueba en el objetivo. A diferencia de otras herramientas clásicas de fuzzing, **wtf** no hace gran parte del trabajo pesado; el usuario lo hace. El usuario necesita conocer muy bien el objetivo instrumentado y la incorporación de un objetivo es un proceso iterativo que llevará tiempo. Sin embargo, ofrece mucha flexibilidad si estás listo para ponerte manos a la obra :)
El flujo de trabajo habitual para instrumentar un objetivo es el siguiente:
1. Haz que tu objetivo funcione en una máquina virtual Hyper-V con Windows, una CPU virtual y 4 GB de RAM.
1. Pon tu objetivo en el estado deseado usando [KD](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/). Por ejemplo, para apuntar al manejador de IOCTL de [HEVD](https://github.com/hacksysteam/HackSysExtremeVulnerableDriver), elegí detener el objetivo en modo usuario justo antes de que el cliente invoque [DeviceIoControl](https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol). Esto variará según tu objetivo, pero probablemente quieras que esté cerca del código que deseas fuzzear.
```
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. Usa [snapshot](https://github.com/0vercl0k/snapshot) para generar el volcado de memoria del kernel y el archivo `regs.json` que contiene el estado de la CPU. Recomiendo guardar esos archivos en un directorio `state` dentro de tu directorio `target` (por ejemplo, `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. Crea un [módulo de fuzzing](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc), escribe el código que [inserta un caso de prueba](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L20) en tu objetivo y define [las](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L81) [diversas](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L104) [condiciones](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L115) para [detectar crashes](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L115) o [el final de un caso de prueba](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L69).
1. También puedes crear tu propio mutador/generador subclasificando la interfaz [Mutator_t](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/mutator.h). El [fuzzer_tlv_server.cc](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_tlv_server.cc) es un buen ejemplo para entender cómo implementar el tuyo propio.
En este punto deberías empezar a iterar y verificar que el módulo de fuzzing funciona como se espera. Los backends de ejecución son una caja negra, por lo que deberías generar trazas de ejecución para asegurarte de que pasa por las rutas correctas y hace lo correcto. Durante esta fase, uso principalmente el backend [bochscpu](https://github.com/yrp604/bochscpu) ya que es completamente determinista, se inicia rápido, es posible generar trazas de ejecución, la cobertura de código viene de forma gratuita, etc. En general, es un entorno más agradable para desarrollar y prototipar.
Una vez que estés satisfecho con el módulo, puedes empezar a ver cómo hacerlo funcionar con los backends [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) si necesitas que se ejecute bajo ellos. Una diferencia importante entre el backend *bochscpu* y los otros es que los otros usan *breakpoints* de software para proporcionar información de cobertura de código. Como resultado, necesitarás cargar los módulos para los que deseas cobertura en [IDA](https://hex-rays.com/IDA-pro/) y usar el script [gen_coveragefile_ida.py](https://github.com/0vercl0k/wtf/blob/HEAD/scripts/gen_coveragefile_ida.py) para generar un archivo JSON simple que será cargado por wtf. Eres libre de generar este archivo JSON tú mismo usando cualquier herramienta que desees: básicamente es una lista de direcciones virtuales de bloques básicos.
También puedes apuntar a aplicaciones [WoW64](https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details) usando el comando `!wow64exts.sw` de Windbg para cambiar al contexto de 64 bits justo antes de crear la instantánea (¡gracias a [@cube0x8](https://twitter.com/cube0x8) por compartir este truco!):```
32.kd:x86> !wow64exts.sw
The context is partially valid. Only x86 user-mode context is available.
Switched to Host mode
32.kd> !snapshot
Los objetivos complejos suelen tener también estados complejos, y es probable que necesites entregar más de un caso de prueba en una sesión para desencadenar problemas complejos. El archivo tlv_server.cc es un ejemplo de un servidor de este tipo, donde ejercitar la función de análisis con un solo caso de prueba no será suficiente para descubrir los errores.
Para manejar este caso, consulta fuzzer_tlv_server.cc que muestra un ejemplo de cómo resolver este problema.
wtf incluye dos mutadores genéricos populares: libfuzzer & honggfuzz. Es posible que quieras proporcionar el tuyo propio o generar casos de prueba por tu cuenta.
Para hacerlo, puedes subclasificar la interfaz Mutator_t y registrar la función que instancia tu mutador cuando definas tu módulo de fuzzing:```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);
Consulte la clase [CustomMutator_t](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_tlv_server.cc) en el módulo [fuzzer_tlv_server.cc](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_tlv_server.cc) para un ejemplo completo.
## Backends de ejecución
En esta sección menciono brevemente varias diferencias entre los backends de ejecución.
### bochscpu
- ✅ Cobertura de código de sistema completo (cobertura de aristas disponible mediante `--edges`),
- ✅ Paginación bajo demanda,
- ✅ El tiempo de espera es el número de instrucciones, lo cual es muy preciso,
- ✅ Se admiten trazas completas de ejecución,
- ✅ Completamente determinista,
- ❌Speed parece buena para ejecuciones cortas pero no para largas (~100x más lento que KVM cuando fuzzeaba IDA).
### whv
- ✔ Cobertura de código mediante puntos de interrupción de software,
- ❌ Paginación bajo demanda, por lo que el inicio es lento (ya que necesita cargar el volcado de memoria completo en la memoria),
- ✔ El tiempo de espera se implementa con un temporizador,
- ✅ Se admiten trazas completas de ejecución pero son lentas (salir de VMX es costoso),
- ✔ Determinista si se maneja manualmente la fuente de no determinismo (por ejemplo, parcheando `nt!ExGenRamdom` que usa `rdrand`),
- ✔ La velocidad parece estar bien para ejecuciones largas (aunque hay muchos cuellos de botella en whv; ~10x más lento que kvm cuando fuzzeaba IDA).
### KVM
- ✔ Cobertura de código mediante puntos de interrupción de software,
- ✅ La paginación bajo demanda es compatible mediante UFDD,
- ✔ El tiempo de espera se implementa con un temporizador. ✅ Si el hardware soporta virtualización PMU, se utiliza para generar un [PMI](https://forum.osdev.org/viewtopic.php?f=1&t=27040) después de X instrucciones retiradas (`MSR_IA32_FIXED_CTR0`),
- ✅ Se admiten trazas completas de ejecución pero son lentas (salir de VMX es costoso),
- ✔ Determinista si se maneja manualmente la fuente de no determinismo (por ejemplo, parcheando `nt!ExGenRamdom` que usa `rdrand`),
- ✅ El más rápido para ejecuciones largas (~500m - 1.5 mil millones de instrucciones; ~100x más rápido que *bochscpu*, ~10x más rápido que *whv* cuando fuzzeaba IDA).
## Build
El [CI](https://github.com/0vercl0k/wtf/actions/workflows/wtf.yml) compila **wtf** en Ubuntu usando tanto [clang++](https://clang.llvm.org/) / [g++](https://gcc.gnu.org/gcc-11/), en Windows usando [Visual Studio](https://visualstudio.microsoft.com/vs/community/) de Microsoft y en OSX usando [clang++](https://clang.llvm.org/).
Para compilarlo usted mismo, necesita iniciar un *Símbolo del sistema para desarrolladores de Visual Studio* y ejecutar ya sea [build-release.bat](https://github.com/0vercl0k/wtf/blob/HEAD/src/build/build-release.bat) que usa el generador [Ninja](https://ninja-build.org/) o [build-release-msvc.bat](https://github.com/0vercl0k/wtf/blob/HEAD/src/build/build-release-msvc.bat) para generar un archivo de solución de 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
Agradecimientos especiales a: