
Crea fuzzers HTTP black-box conscientes de la estructura en Rust con mutadores, planificadores, observadores, decisores y procesadores componibles para pruebas personalizadas de web y API.
Tranquilo, no es otra herramienta de línea de comandos, ¡esta es una librería! 😁
Más concretamente, FeroxFuzz es una librería de fuzzing HTTP consciente de la estructura.
El objetivo principal al escribir FeroxFuzz era sacar algunas piezas centrales de feroxbuster y llevarlas a un lugar donde pudieran ser útiles para otras personas. Al hacerlo, espero que cualquiera que quiera escribir herramientas web y/o fuzzers web puntuales en Rust pueda hacerlo con el mínimo esfuerzo.
El diseño general de FeroxFuzz se deriva de LibAFL. FeroxFuzz implementa la mayoría de los componentes enumerados en LibAFL: A Framework to Build Modular and Reusable Fuzzers (pre-print). Cuando FeroxFuzz se desvía, normalmente es por el soporte de código asíncrono.
Al igual que LibAFL, FeroxFuzz es una librería de fuzzing componible. Sin embargo, a diferencia de LibAFL, FeroxFuzz está enfocada exclusivamente en el fuzzing HTTP de caja negra.
A continuación se muestra una representación visual de los diferentes componentes, hooks y flujo de control utilizados por FeroxFuzz.

FeroxFuzz es muy capaz y se creó para satisfacer todas las necesidades que tengo planificadas para un nuevo feroxbuster. Sin embargo, todavía espero que la API de FeroxFuzz cambie, al menos ligeramente, cuando comience el trabajo en la nueva versión de feroxbuster.
Hasta que la API se consolide, los cambios incompatibles pueden ocurrirán.
La forma más fácil de empezar es incluir FeroxFuzz en el Cargo.toml de tu proyecto.
[dependencies]
feroxfuzz = { version = "1.0.0-rc.13" }
Además de la carpeta examples/, la documentación de la API incluye una extensa documentación de los componentes junto con ejemplos de su uso.
El siguiente ejemplo (examples/async-simple.rs) muestra el mínimo absoluto para escribir un fuzzer con FeroxFuzz.
Si usas el código fuente, el ejemplo se puede ejecutar desde el directorio feroxfuzz/ con el siguiente comando:
nota: a menos que tengas un servidor web ejecutándose en tu máquina en el puerto 8000, tendrás que cambiar el destino pasado en
Request::from_url
cargo run --example async-simple
#[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()?;
// with some client that can handle the actual http request/response stuff
// we can build a feroxfuzz client, specifically an asynchronous client in this
// instance.
//
// 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, in that they'll
// put the current corpus item wherever the keyword is found, as long as its found
// in data marked fuzzable (see ShouldFuzz directives below)
let mutator = ReplaceKeyword::new(&"FUZZ", "words");
// fuzz directives control which parts of the request should be fuzzed
// anything not marked fuzzable is considered to be static and won't be mutated
//
// ShouldFuzz directives map to the various components of an HTTP request
let request = Request::from_url(
"http://localhost:8000/?admin=FUZZ",
Some(&[ShouldFuzz::URLParameterValues]),
)?;
// a `StatusCodeDecider` provides a way to inspect each response's status code and decide upon some Action
// based on the result of whatever comparison function (closure) is passed to the StatusCodeDecider's
// constructor
//
// in plain english, the `StatusCodeDecider` below will check to see if the request's http response code
// received is equal to 200/OK. If the response code is 200, then the decider will recommend the `Keep`
// action be performed. If the response code is anything other than 200, then the recommendation will
// be to `Discard` the response.
//
// `Keep`ing the response means that the response will be allowed to continue on for further processing
// later in the fuzz loop.
let decider = StatusCodeDecider::new(200, |status, observed, _state| {
if status == observed {
Action::Keep
} else {
Action::Discard
}
});
// a `ResponseObserver` is responsible for gathering information from each response and providing
// that information to later fuzzing components, like Processors. It knows things like the response's
// status code, content length, the time it took to receive the response, and a bunch of other stuff.
let response_observer: ResponseObserver<AsyncResponse> = ResponseObserver::new();