The following example demonstrates the minimal boilerplate required to create an asynchronous fuzzer. It involves setting up a Wordlist corpus, an AsyncClient (using reqwest), a ReplaceKeyword mutator, a StatusCodeDecider to filter results, and an AsyncFuzzer to orchestrate the process.
Note: You must replace the target URL in Request::from_url with a valid endpoint for your testing.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// create a new corpus from the given list of words
let words = Wordlist::from_file("./examples/words")?
.name("words")
.build();
// pass the corpus to the state object, which will be shared between all of the fuzzers and processors
let mut state = SharedState::with_corpus(words);
// bring-your-own client, this example uses the reqwest library
let req_client = reqwest::Client::builder().build()?;
// feroxfuzz provides both a blocking and an asynchronous client implementation
// using reqwest.
let client = AsyncClient::with_client(req_client);
// ReplaceKeyword mutators operate similar to how ffuf/wfuzz work
let mutator = ReplaceKeyword::new(&"FUZZ", "words");
// fuzz directives control which parts of the request should be fuzzed
let request = Request::from_url(
"http://localhost:8000/?admin=FUZZ",
Some(&[ShouldFuzz::URLParameterValues]),
)?;
// StatusCodeDecider inspects response status codes to decide on an Action
let decider = StatusCodeDecider::new(200, |status, observed, _state| {
if status == observed {
Action::Keep
} else {
Action::Discard
}
});
// ResponseObserver gathers information from each response
let response_observer: ResponseObserver<AsyncResponse> = ResponseObserver::new();
// ResponseProcessor provides access to the observer and the decider's Action
let response_printer = ResponseProcessor::new(
|response_observer: &ResponseObserver<AsyncResponse>, action, _state| {
if let Some(Action::Keep) = action {
println!(
"[{}] {} - {} - {:?}",
response_observer.status_code(),
response_observer.content_length(),
response_observer.url(),
response_observer.elapsed()
);
}
},
);
// Schedulers manage how the fuzzer gets entries from the corpus
let scheduler = OrderedScheduler::new(state.clone())?;
// Macros to build component collections
let deciders = build_deciders!(decider);
let mutators = build_mutators!(mutator);
let observers = build_observers!(response_observer);
let processors = build_processors!(response_printer);
let threads = 40;
// The Fuzzer is the main component that wraps all other components
let mut fuzzer = AsyncFuzzer::new(threads)
.client(client)
.request(request)
.scheduler(scheduler)
.mutators(mutators)
.observers(observers)
.processors(processors)
.deciders(deciders)
.post_loop_hook(|state| {
println!("\n•*´¨`*•.¸¸.•* Finished fuzzing loop •*´¨`*•.¸¸.•*\n");
println!("{state:#}");
})
.build();
fuzzer.fuzz_once(&mut state).await?;
println!("{state:#}");
Ok()
}