Introduction
Welcome to the user guide for fuzzer. This guide is designed to help you understand how to install, configure, and effectively use the fuzzer to test your applications for bugs and vulnerabilities.
The fuzzer is built with flexibility and extensibility in mind, supporting a variety of fuzzing modes, input formats, and advanced features such as coverage-guided fuzzing, corpus management, and comparison logging. Whether you're new to fuzzing or an experienced security researcher, this guide will provide you with the knowledge to utilize the fuzzer to its full potential.
Let's begin by setting up the fuzzer and getting it running on your system.
Installation
Prerequisites
Before installing the fuzzer, ensure that you have the following prerequisites installed on your system:
- Rust and Cargo: Install Rust and Cargo via rustup.
Installing the Fuzzer
To include the fuzzer in your Rust project, add it as a dependency in your Cargo.toml
file:
[dependencies]
fuzzer = { path = "https://github.com/0xb-s/fuzzer" }
Getting Started
This section will guide you through setting up and running the fuzzer for the first time. We'll cover the basic steps needed to configure the fuzzer and execute a simple fuzzing session.
Overview
To get started with the fuzzer, you'll need to:
- Configure the Fuzzer: Set up the
FuzzerConfig
with your desired options. - Define Target Functions: Create one or more functions that the fuzzer will test.
- Initialize the Fuzzer: Create an instance of the
Fuzzer
and add your target functions. - Run the Fuzzer: Execute the fuzzing session and monitor progress.
In the next section, we'll walk through a quick start example that demonstrates these steps.
Quick Start Example
Let's dive into a simple example to illustrate how to use the fuzzer in your project.
Step 1: Set Up the Project
Create a new Rust project or use an existing one. Ensure that the fuzzer is added to your Cargo.toml
dependencies as described in the Installation section.
Step 2: Configure the Fuzzer
use fuzzer::mutator_options::{MutationType, MutatorOptions}; use fuzzer::utils::{FuzzMode, InputFormat}; use fuzzer::{Fuzzer, FuzzerConfig, FuzzerError, TargetFunction}; use std::future::Future; use std::pin::Pin; use std::time::Duration; #[tokio::main] async fn main() { let mutator_options = MutatorOptions { mutation_rate: 0.1, max_mutations: 5, mutation_types: vec![ MutationType::BitFlip, MutationType::ByteFlip, MutationType::BlockMutation, ], ..Default::default() }; let config = FuzzerConfig::builder() .input_format(InputFormat::Text) .fuzz_mode(FuzzMode::Mutation) .timeout(Duration::from_secs(1)) .max_iterations(1000) .seed(42) .mutator_options(mutator_options) .stop_on_first_crash(false) .stats_interval(100) .max_input_size(256) .min_input_size(1) .build(); let mut fuzzer = Fuzzer::new(config); let from_utf8_target = |input: &[u8]| -> Pin<Box<dyn Future<Output = Result<(), FuzzerError>> + Send>> { let input_owned = input.to_owned(); Box::pin(async move { match std::str::from_utf8(&input_owned) { Ok(valid_str) => { println!("Valid UTF-8 string: {}", valid_str); Ok(()) } Err(e) => Err(FuzzerError::ExecutionError(format!( "from_utf8 error: {}", e ))), } }) }; let target = TargetFunction::new_async("FromUtf8", from_utf8_target); fuzzer.add_target(target); if let Err(e) = fuzzer.run().await { eprintln!("Fuzzer encountered an error: {}", e); } }
Step 3: Run the Fuzzer
Execute your program:
cargo run
The fuzzer will start running, generating inputs, mutating them, and feeding them to your target function. It will report progress and any crashes it encounters.
Configuration
Configuring the fuzzer allows you to customize its behavior and optimize it for specific testing needs. This section will explain the options available for setting up the fuzzer, including general configuration options, mutator options, and sanitizers.
To configure the fuzzer, you'll use the FuzzerConfig
builder pattern. This allows you to chain multiple configuration options and create a customized FuzzerConfig
object that controls the behavior of the fuzzing process.
Setting Up the Configuration
Here's a basic example of how to create and apply configuration settings to the fuzzer:
#![allow(unused)] fn main() { use fuzzer::FuzzerConfig; use fuzzer::utils::{FuzzMode, InputFormat}; use std::time::Duration; let config = FuzzerConfig::builder() .input_format(InputFormat::JSON) .fuzz_mode(FuzzMode::Mutation) .timeout(Duration::from_secs(2)) .max_iterations(5000) .seed(12345) .stop_on_first_crash(true) .enable_logging(true) .save_crashes(true) .thread_count(4) .build(); }
Mutator Options
The MutatorOptions
structure allows you to control how inputs are mutated during the fuzzing process. These options help define the types and frequency of mutations that the fuzzer will apply, enabling you to customize the way inputs evolve and explore potential vulnerabilities in the target function.
Setting Up Mutator Options
To configure mutation behaviors, create a MutatorOptions
instance and pass it to your fuzzer configuration:
#![allow(unused)] fn main() { use fuzzer::mutator_options::{MutatorOptions, MutationType}; let mutator_options = MutatorOptions { mutation_rate: 0.2, max_mutations: 10, mutation_types: vec![MutationType::BitFlip, MutationType::ByteFlip], enable_crossover: true, crossover_rate: 0.05, block_mutation_size: 16, arithmetics_range: 10, interesting_values: vec![vec![0x00], vec![0xFF], vec![0x7F]], ..Default::default() }; }
After defining the options, pass mutator_options into the FuzzerConfig:
#![allow(unused)] fn main() { let config = FuzzerConfig::builder() .mutator_options(mutator_options) .build(); }
Sanitizer Options
Sanitizers are tools that help detect various types of bugs and vulnerabilities during runtime by instrumenting code to catch errors such as memory corruption, data races, and undefined behavior. The SanitizerOptions
structure lets you enable or disable specific sanitizers to improve the effectiveness of your fuzzing process.
Enabling Sanitizers
To activate sanitizers, set sanitizer_enabled
to true
in your fuzzer configuration. Then, use SanitizerOptions
to specify which sanitizers to enable. Here’s an example:
#![allow(unused)] fn main() { use fuzzer::FuzzerConfig; use fuzzer::sanitizer_options::SanitizerOptions; let sanitizer_options = SanitizerOptions { address: true, thread: false, memory: false, undefined_behavior: true, leak: true, }; let config = FuzzerConfig::builder() .sanitizer_enabled(true) .sanitizer_options(sanitizer_options) .build(); }