This year I'm honored to have the chance of contributing to the well-known and amazing fuzzing framework LibAFL. This report outlines my work from the last 10 weeks and gives an overview over the state of the project.
LibAFL is a fuzzing framework written in RUST. Fuzzing is an automatic bug detection method. During the fuzzing process, fuzzer(the fuzzing program) will feed the target with different inputs, rates and generates more valuable inputs by different techniques. LibAFL split a fuzzer into different components and provides essential parts in each component, which makes it possible to write your customized fuzzer.
The goal of this project is to make nyx fuzzer available in LibAFL. nyx is a highly optimized and coverage-guided hypervisor fuzzer that uses a fast snapshot restoration mechanism and a novel mutation engine based on affine types.
As the result of this project, I created a sub-create libafl_nyx
and designed nyx_executor
to support single-thread(standalone mode) or multi-threads(parallel mode) fuzzing using nyx as the backend. I also wrote two example fuzzers for libxml2
to illustrate the code usage.
NYX relies on a collection of tools:
- QEMU-NYX: a customized version of QEMU which supports: Hypervisor based snapshots, Intel-PT based tracing, and REDQUEEN style magic byte resolution
- packer: image packer for nyx VMs
- libnyx: rust API to build hypervisor-based snapshot fuzzers.
libnyx
can be easily used as a crate dependency. For the other part, build.rs
is responsible for submodule checkout, packer
's initramfs
build and QEMU-NYX
build.
Nyx has two modes:
- In standalone mode, no VM snapshot is serialized and stored in the working directory. That might be useful if you want to run the fuzzer with only one process (meaning one VM).
- In parallel mode, the first fuzzer process (parent) has to create the VM snapshot while all other child processes will wait for the snapshot files to appear in the working directory.
NyxHelper is responsible for NYX instance spawning in both standalone mode and parallel mode. It will first create NYX process according to the process type(ALONE
in standalone and PARENT
,CHILD
in parallel), then it will start a dry-run to test the connection.
In parallel mode, you need to explicitly pass parent_cpu_id
to distinguish PARENT
from CHILD
. The reason behind it is that in LibAFL's Launcher
(layer to create multi-threads fuzzer), one fuzzer only communicates with other fuzzers through message passing. So specifying parent_cpu_id
during creation reduces the design complexity.
struct NyxExecutor takes NyxHelper
and observers
as input. It provides a unified API for standalone and parallel modes. It is used for input set, program execution in VM and exception handle.
nyx_libxml2_standalone and nyx_libxml2_parallel are two example fuzzers to show the usage of nyx's standalone and parallel mode. It uses cargo-make
to setup the environment and has been tested on ubuntu-22.04
.
For the full focumentation, you can see nyx.md
During GSOC, I have also done some CI/CD and doc improvements. You can view them in Pull Request.