
Construa fuzzers HTTP black-box cientes de estrutura em Rust com mutadores, escalonadores, observadores, decisores e processadores componíveis para testes personalizados de web e API.
Relaxa, não é outra ferramenta de linha de comando, essa aqui é uma biblioteca! 😁
Mais especificamente, o FeroxFuzz é uma biblioteca de fuzzing HTTP ciente de estrutura.
O objetivo principal ao escrever o FeroxFuzz foi mover algumas partes centrais do feroxbuster para um lugar onde pudessem ser geralmente úteis para outras pessoas. Ao fazer isso, minha esperança é que qualquer pessoa que queira escrever ferramentas web e/ou fuzzers web de uso único em Rust possa fazê-lo com o mínimo de esforço.
O design geral do FeroxFuzz é derivado do LibAFL. O FeroxFuzz implementa a maioria dos componentes listados em LibAFL: A Framework to Build Modular and Reusable Fuzzers (pre-print). Quando o FeroxFuzz se desvia, normalmente é para dar suporte a código assíncrono.
Semelhante ao LibAFL, o FeroxFuzz é uma biblioteca de fuzzing componível. No entanto, diferente do LibAFL, o FeroxFuzz é focado exclusivamente em fuzzing HTTP de caixa preta.
Abaixo está uma representação visual dos diferentes componentes, hooks e fluxo de controle utilizados pelo FeroxFuzz.

O FeroxFuzz é muito capaz e foi feito para atender a todas as minhas necessidades planejadas para um novo feroxbuster. No entanto, ainda espero que a API do FeroxFuzz mude, pelo menos um pouco, à medida que o trabalho na nova versão do feroxbuster começar.
Até que a API se solidifique, mudanças disruptivas podem vão ocorrer.
A maneira mais fácil de começar é incluir o FeroxFuzz no Cargo.toml do seu projeto.
[dependencies]
feroxfuzz = { version = "1.0.0-rc.13" }
Além da pasta examples/, a documentação da API possui documentação extensa dos componentes junto com exemplos de uso.
O exemplo abaixo (examples/async-simple.rs) mostra o mínimo necessário para escrever um fuzzer usando o FeroxFuzz.
Se estiver usando o código-fonte, o exemplo pode ser executado a partir do diretório feroxfuzz/ usando o seguinte comando:
observação: a menos que você tenha um servidor web rodando na sua máquina @ porta 8000, você precisará alterar o alvo passado em
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();