MCPcopy Create free account
hub / github.com/0vercl0k/wtf

github.com/0vercl0k/wtf @v0.5.7

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.5.7 ↗ · + Follow
9,014 symbols 23,220 edges 811 files 2,020 documented · 22% updated 8d agov0.5.7 · 2025-03-01★ 1,77321 open issues

Browse by type

Functions 6,937 Types & classes 2,077
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

what the fuzz

<strong>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!).</strong>






<img src='https://github.com/0vercl0k/wtf/actions/workflows/wtf.yml/badge.svg?branch=main'/>






<img src='pics/wtf.webp'/>

Overview

what the fuzz or wtf is a distributed, code-coverage guided, customizable, cross-platform snapshot-based fuzzer designed for attacking user and or kernel-mode targets running on Microsoft Windows or Linux (experimental, see linux_mode). Execution of the target can be done inside an emulator with bochscpu (slowest, most precise), inside a Windows VM with the Windows Hypervisor Platform APIs or inside a Linux VM with the KVM APIs (fastest).

It uncovered memory corruption vulnerabilities in a wide range of softwares: IDA Pro, a popular AAA game, the Windows kernel, the Microsoft RDP client, NVIDIA GPU Display driver, etc.

Compiled binaries are available from either the CI artifacts or from the Releases section for both Windows & Linux.

If you would like to read more about its history or how to use it on a real target, I recommend to take a look at those posts to get started 🔥 - Building a new snapshot fuzzer & fuzzing IDA - Fuzzing Modern UDP Game Protocols With Snapshot-based Fuzzers by Markus Gaasedelen - Fuzzing RDPEGFX with "what the fuzz" by Colas Le Guernic, Jérémy Rubert, and Anonymous - A Journey to Network Protocol Fuzzing – Dissecting Microsoft IMAP Client Protocol by Wayne Chin Yick Low

Usage

The best way to try the features out is to work with the fuzzer_hevd / fuzzer_tlv_server modules. You can grab the target-hevd.7z / target-tlv_server.7z archives and extract them into the targets/ directory. The archives contain the directory trees that are expected for every targets:

  • inputs is the folder where your input test-cases go into,
  • outputs is the folder where the current minset files are saved into,
  • coverage is the folder where the .cov files are expected to be in,
  • crashes is where the crashes gets saved in,
  • state is where the memory dump (mem.dmp) as well as the CPU state (regs.json) and the symbol store are stored in (symbol-store.json). The symbol store is a simple JSON file that is used on Linux systems to know where to put breakpoints as there is no support for symbols / dbgeng on those platforms. wtf generates this file at runtime everytime you run your target on Windows.

What follows assume that you downloaded the target-hevd.7z file attached to the latest release, and extracted it in the targets directory of your clone of wtf. You should have wtf/targets/hevd in which you find the inputs / outputs, etc. directories.

Starting a server node

The server is basically the brain and keeps track of all the state: the aggregated code-coverage, the corpus, it generates and distributes the test-cases to client.

This is how you might choose to launch a local server node:

wtf.exe master --name hevd --max_len=1028 --runs=10000000

The max_len option is used to limit the size of the generated test-case, runs is the number of test-cases it will generate, address specify where wtf needs to be listening on, target is a directory with the directory tree we described above (the user can also choose to override those directories with --input / --output / --crashes) and name specifies your fuzzing module name so that the master can invoke your generator function if you have defined one.

Fuzzing nodes

The client nodes run a test-case that has been generated and distributed by the server and communicates the result back to the server (code-coverage, result, etc.).

This is how you would start a client node that uses the bochscpu backend:

wtf.exe fuzz --name hevd --limit 10000000

The fuzz subcommand is used with the name option to specify which fuzzer module needs to be used, backend specifies the execution backend and limit the maximum number of instruction to execute per testcase (depending on the backend, this option has different meaning).

Running a test-case

If you would like to run a test-case (or a folder filled with test-cases), you can use the run subcommand.

This is how you would would run the crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 test-case:

wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0

Minseting a corpus

To minset a corpus, you need to use a server node and as many client nodes as you need like you would for a fuzzing job. You can simply set the runs optins to 0.

This is how you would minset the corpus in outputs into the minset directory (also highlights how you can override the inputs and outputs directories):

wtf.exe master --name hevd --max_len=1028 --runs=0 --inputs=outputs --outputs=minset

Generating execution traces

The main mechanism available to instrospect in an execution backend is to generate an execution trace. bochscpu is the fastest backend to do that, because exiting VMX mode is very expensive on the other backends.

This is how you would generate an execution trace for the crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 test-case:

wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 --trace-type=rip

To symbolize execution traces you should use symbolizer-rs. This is how you would symbolize the crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0.trace execution trace generated above:

symbolizer-rs.exe --trace crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0.rip.trace

Generating Tenet traces

If you see yourself needing more contextual awareness, the bochscpu backend allows you to generate execution traces that can be loaded in the Tenet trace explorer. In the below, I start from a crash in memmove and walk back to find out where the source pointer is coming from (user-mode!):

wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 --trace-type=tenet

Generating code-coverage traces

To generate code-coverage traces you can simply use the run subcommand with the --trace-type=cov option.

This is how you would generate code-coverage traces for all the files inside the minset folder and store them in the coverage-traces folder:

wtf.exe run --name hevd --input minset --trace-path=coverage-traces --trace-type=cov

Those traces aren't directly loadable into lighthouse because they aren't symbolized.

This is how you would symbolize all the files inside the coverage-traces folder and write the results into coverage-traces-symbolized:

symbolizer-rs.exe --trace coverage-traces -o coverage-traces-symbolized --style modoff

And finally, you can load those up in lighthouse:

Also if you don't care about individual code-coverage, the master maintains a coverage.cov file contains the unique aggregated code-coverage that has been exercised. It makes it easy to check on the global code-coverage really quick during a fuzzing job.

How does it work?

wtf runs user & kernel mode through an execution backend and relies on the user to insert test-cases in the target. Unlike other classical fuzzer tools, wtf doesn't do much of the heavy lifting; the user does. The user needs to know the harnessed target very well and onboarding a target is an iterative process that will take time. It has a lot of flexibility to offer if you are ready to get hacking though :)

The usual workflow to harness a target is as follows:

  1. Get your target running into a Hyper-V VM running Windows with one virtual CPU and 4GB of RAM.
  2. Put your target into the desired state using KD. For example, to target HEVD's IOCTL handler, I chose to stop the target in user-mode right before the client invokes DeviceIoControl. This will vary depending on your targets but you probably want it to be close to the code you want to 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. Use snapshot to generate the kernel crash-dump as well as the regs.json file that contains the CPU state. I recommend to dump those file in a state directory under your target directory (targets/hevd/state for example):

    ``` 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. Create a fuzzer module, write the code that inserts a test-case into your target and define the various conditions to detect crashes or the end of a test-case.

  5. You can also create your own mutator / generator by subclassing the Mutator_t interface. The fuzzer_tlv_server.cc is a good example to understand how you would go about implementing your own.

At this point you should start to iterate and verify that the fuzzer module works as expected. The execution backends are a blackbox so you should generate execution traces to make sure it goes through the right paths, does the right things. During this phase I mainly use the bochscpu backend as it is fully deterministic, starts fast, generating execution traces is possible, code-coverage comes for free, etc. Overall, it's a nicer environment to develop and prototype in.

Once you are satisfied with the module, you can start to look at making it work with the winhv / kvm backends if you need it to run under those. One major difference between the bochscpu backend & the others, is that the others use software breakpoints to provide code-coverage information. As a result, you'll need

Extension points exported contracts — how you extend this code

Core symbols most depended-on inside this repo

Shape

Method 4,261
Function 2,676
Class 1,957
Enum 119
Interface 1

Languages

C++88%
Rust5%
Python4%
C2%
TypeScript1%

Modules by API surface

src/libs/fmt/test/gtest/gmock/gmock.h747 symbols
src/libs/fmt/test/gtest/gtest/gtest.h708 symbols
src/libs/fmt/test/gtest/gmock-gtest-all.cc667 symbols
src/libs/json/single_include/nlohmann/json.hpp548 symbols
src/libs/yas/tests/base/externals/nlohmann_json.hpp318 symbols
src/libs/fmt/include/fmt/core.h272 symbols
src/libs/fmt/include/fmt/format.h178 symbols
scripts/gen_coveragefile_ida.py177 symbols
src/libs/CLI11/include/CLI/App.hpp157 symbols
src/libs/fmt/include/fmt/chrono.h136 symbols
src/libs/json/include/nlohmann/json.hpp104 symbols
src/libs/CLI11/include/CLI/Option.hpp103 symbols

For agents

$ claude mcp add wtf \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page