Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
test-fuzz — Macros Rust e subcomando do Cargo para automatizar fuzzing com afl.rs, incluindo geração de corpus e implementação de harness, integrado ao framework de testes do Rust. | Kitploit
Ferramentas/GitHubGitHub/trailofbits/test-fuzz
Análise Dinâmica (Sandboxing)Análise de VulnerabilidadesAnálise de CódigoFuzzing
GitHubtrailofbits/test-fuzz

test-fuzz

Macros Rust e subcomando do Cargo para automatizar fuzzing com afl.rs, incluindo geração de corpus e implementação de harness, integrado ao framework de testes do Rust.

Ver Repositório
20827há 11 diasRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Site
Compartilhar

test-fuzz

test-fuzz é um subcomando do Cargo e uma coleção de macros Rust para automatizar certas tarefas relacionadas a fuzzing com afl.rs, incluindo:

  • gerar um corpus de fuzzing
  • implementar um harness de fuzzing

test-fuzz realiza essas tarefas (em parte) usando os recursos de teste do Rust. Por exemplo, para gerar um corpus de fuzzing, o test-fuzz registra os argumentos de um alvo cada vez que ele é chamado durante uma invocação de cargo test. Da mesma forma, o test-fuzz implementa um harness de fuzzing como um teste adicional em um binário gerado por cargo-test. Essa integração estreita com os recursos de teste do Rust é o que motiva o nome test-fuzz.

Conteúdo

  1. [Instalação]
  2. [Visão geral]
  3. [Componentes]
    • test_fuzz macro
    • test_fuzz_impl macro
    • [comando cargo test-fuzz]
    • [Funções e macros de conveniência]
  4. [Recursos do pacote test-fuzz]
  5. [Arquivos de corpus gerados automaticamente]
  6. [Variáveis de ambiente]
  7. [Limitações]
  8. [Dicas e truques]
  9. [Política de versionamento semântico]
  10. [Licença]

Instalação

Instale o cargo-test-fuzz e o afl.rs com o seguinte comando:```sh cargo install cargo-test-fuzz cargo-afl

root@kitploit:~
## Visão geral

Fuzzing com `test-fuzz` consiste essencialmente em três etapas:\*

1. **Identifique um alvo de fuzzing**:
   - Adicione as seguintes `dependencies` ao arquivo `Cargo.toml` do crate alvo:
     ```toml
     serde = "*"
     test-fuzz = "*"
     ```
   - Preceda a função alvo com a macro [`test_fuzz`]:
     ```rust
     #[test_fuzz::test_fuzz]
     fn foo(...) {
         ...
     }
     ```
2. **Gere um corpus** executando `cargo test`:   ```
   cargo test
  1. Faça fuzz do seu alvo executando cargo test-fuzz: ``` cargo test-fuzz foo
    root@kitploit:~

* Uma etapa adicional e preliminar pode ser necessária após uma reinicialização:```sh cargo afl system-config

root@kitploit:~
Note que o comando acima executa `sudo` internamente. Portanto, você pode ser solicitado a inserir sua senha.

## Componentes

### Macro `test_fuzz`

Preceder uma função com a macro `test_fuzz` indica que a função é um alvo de fuzzing.

Os efeitos principais da macro `test_fuzz` são:

- Adiciona instrumentação ao alvo para serializar seus argumentos e escrevê-los em um arquivo de corpus cada vez que o alvo for chamado. A instrumentação é condicionada por `#[cfg(test)]` de modo que os arquivos de corpus são gerados apenas ao executar testes (no entanto, veja [`enable_in_production`] abaixo).
- Adiciona um teste para ler e desserializar argumentos da entrada padrão e aplicar o alvo a eles. O teste verifica uma variável de ambiente, definida por [`cargo test-fuzz`], para que o teste não bloqueie ao tentar ler da entrada padrão durante uma invocação normal de `cargo test`. O teste é encapsulado em um módulo para reduzir a probabilidade de colisão de nomes. Atualmente, o nome do módulo é `target_fuzz`, onde `target` é o nome do alvo (no entanto, veja [`rename`] abaixo).

#### Argumentos

##### `bounds = "where_predicates"`

Imponha `where_predicates` (por exemplo, limites de trait) na struct usada para serializar/desserializar argumentos. Isso pode ser necessário, por exemplo, se o tipo de argumento de um alvo for um tipo associado. Para um exemplo, veja [associated_type.rs] neste repositório.

##### `generic_args = "parameters"`

Use `parameters` como os parâmetros de tipo do alvo ao realizar fuzzing. Exemplo:```rust
#[test_fuzz(generic_args = "String")]
fn foo<T: Clone + Debug + Serialize>(x: &T) {
    ...
}

Nota: Os argumentos do alvo devem ser serializáveis para cada instanciação de seus parâmetros de tipo. Mas os argumentos do alvo são necessários desserializáveis apenas quando o alvo é instanciado com parameters.

impl_generic_args = "parameters"

Use parameters como os parâmetros de tipo Self do alvo ao fazer fuzzing. Exemplo:```rust #[test_fuzz_impl] impl<T: Clone + Debug + Serialize> for Foo { #[test_fuzz(impl_generic_args = "String")] fn bar(&self, x: &T) { ... } }

root@kitploit:~
Nota: os argumentos do alvo devem ser serializáveis para **cada** instanciação de seus parâmetros de tipo `Self`. Porém, os argumentos do alvo só precisam ser desserializáveis quando o `Self` do alvo for instanciado com `parameters`.

##### `convert = "X, Y"`

Ao serializar os argumentos do alvo, converta valores do tipo `X` para o tipo `Y` usando a implementação de `From<X>` em `Y`, ou do tipo `&X` para o tipo `Y` usando a implementação do trait não padrão `test_fuzz::FromRef<X>` em `Y`. Ao desserializar, converta esses valores de volta para o tipo `X` usando a implementação do trait não padrão `test_fuzz::Into<X>` em `Y`.

Ou seja, o uso de `convert = "X, Y"` deve ser acompanhado por determinadas implementações. Se `X` implementa [`Clone`], então `Y` pode implementar o seguinte:```rust
impl From<X> for Y {
    fn from(x: X) -> Self {
        ...
    }
}

Se X não implementar Clone, então Y deve implementar o seguinte:```rust impl test_fuzz::FromRef for Y { fn from_ref(x: &X) -> Self { ... } }

root@kitploit:~
Além disso, `Y` deve implementar o seguinte (independentemente de `X` implementar [`Clone`]):```rust
impl test_fuzz::Into<X> for Y {
    fn into(self) -> X {
        ...
    }
}

A definição de test_fuzz::Into é idêntica à de std::convert::Into. A razão para usar uma trait não padrão é evitar conflitos que possam surgir de implementações abrangentes de traits padrão.

enable_in_production

Gere arquivos de corpus quando não estiver executando testes, desde que a variável de ambiente TEST_FUZZ_WRITE esteja definida. O padrão é gerar arquivos de corpus apenas durante a execução de testes, independentemente de TEST_FUZZ_WRITE estar definida. Ao executar um alvo fora do diretório do seu pacote, defina TEST_FUZZ_MANIFEST_PATH para o caminho do arquivo Cargo.toml do pacote.

AVISO: Definir enable_in_production pode introduzir um vetor de negação de serviço. Por exemplo, definir essa opção para uma função que é chamada muitas vezes com argumentos diferentes pode encher o disco. A verificação de TEST_FUZZ_WRITE tem a finalidade de fornecer alguma defesa contra essa possibilidade. Ainda assim, considere essa opção com cuidado antes de usá-la.

execute_with = "function"

Em vez de chamar o alvo diretamente:

  • construir um closure do tipo FnOnce() -> R, onde R é o tipo de retorno do alvo, de modo que chamar o closure chame o alvo;
  • chamar function com o closure.

Chamar o alvo dessa maneira permite que function configure o ambiente da chamada. Isso pode ser útil, por exemplo, para fuzzing de Substrate externalities.

no_auto_generate

Não tente auto-generate corpus files para o alvo.

only_generic_args

Registre os argumentos genéricos do alvo durante a execução de testes, mas não gere arquivos de corpus e não implemente um harness de fuzzing. Isso pode ser útil quando o alvo é uma função genérica, mas não está claro quais parâmetros de tipo devem ser usados para fuzzing.

O fluxo de trabalho pretendido é: habilite only_generic_args, depois execute cargo test seguido por cargo test-fuzz --display generic-args. Um dos argumentos genéricos resultantes pode ser utilizável como parameters de generic_args. Da mesma forma, argumentos genéricos resultantes de cargo test-fuzz --display impl-generic-args podem ser utilizáveis como parameters de impl_generic_args.

Observe, no entanto, que o fato de um alvo ter sido chamado com certos parâmetros durante os testes não implica que os argumentos do alvo sejam serializáveis/desserializáveis quando esses parâmetros são usados. Os resultados de --display generic-args/--display impl-generic-args são meramente sugestivos.

rename = "name"

Trate o alvo como se seu nome fosse name ao adicionar um módulo ao escopo delimitador. A expansão do macro test_fuzz adiciona uma definição de módulo ao escopo delimitador. Por padrão, o módulo é nomeado da seguinte forma:

  • Se o alvo não aparecer em um bloco impl, o módulo será nomeado target_fuzz__, onde target é o nome do alvo.
  • Se o alvo aparecer em um bloco impl, o módulo será nomeado path_target_fuzz__, onde path é o último segmento do caminho do tipo Self do impl.

Contudo, o uso dessa opção faz com que o módulo seja nomeado, em vez disso, name_fuzz__. Exemplo:```rust #[test_fuzz(rename = "bar")] fn foo() {}

// Without the use of rename, a name collision and compile error would result. mod foo_fuzz__ {}

root@kitploit:~
#### Atributos de campo do Serde em argumentos de função

A macro `test_fuzz` permite que [atributos de campo do Serde] sejam aplicados a argumentos de função. Isso fornece outra ferramenta para lidar com tipos difíceis.

O exemplo a seguir ilustra isso. As traits `serde::Serialize` e `serde::Deserialize` não podem ser derivadas para `Context` porque ela contém um `Mutex`. No entanto, `Context` implementa `Default`. Assim, aplicar `#[serde(skip)]` ao argumento `Context` faz com que ele seja ignorado durante a serialização e que assuma seu valor padrão durante a desserialização.```rust
use std::sync::Mutex;

// Traits `serde::Serialize` and `serde::Deserialize` cannot be derived for `Context` because it
// contains a `Mutex`.
#[derive(Default)]
struct Context {
    lock: Mutex<()>,
}

impl Clone for Context {
    fn clone(&self) -> Self {
        Self {
            lock: Mutex::new(()),
        }
    }
}

#[test_fuzz::test_fuzz]
fn target(#[serde(skip)] context: Context, x: i32) {
    assert!(x >= 0);
}

Note que quando atributos de campo Serde são aplicados a um argumento, a macro test_fuzz não realiza nenhuma outra conversions no argumento.

macro test_fuzz_impl

Sempre que a macro test_fuzz for usada em um bloco impl, o impl deve ser precedido pela macro test_fuzz_impl. Exemplo:```rust #[test_fuzz_impl] impl Foo { #[test_fuzz] fn bar(&self, x: &str) { ... } }

root@kitploit:~
A razão para este requisito é a seguinte. A expansão da macro [`test_fuzz`] adiciona uma definição de módulo ao escopo circundante. No entanto, uma definição de módulo não pode aparecer dentro de um bloco `impl`. Anteceder o `impl` com a macro `test_fuzz_impl` faz com que o módulo seja adicionado fora do bloco `impl`.

Se você vir um erro como o seguinte, provavelmente significa que falta um uso da macro `test_fuzz_impl`:```
error: module is not supported in `trait`s or `impl`s

test_fuzz_impl atualmente não tem opções.

Comando cargo test-fuzz

O comando cargo test-fuzz é usado para interagir com alvos de fuzzing e para manipular seus corpora, crashes, hangs e filas de trabalho. Exemplos de invocações incluem:

  1. Listar alvos de fuzzing ``` cargo test-fuzz --list
    root@kitploit:~
  2. Exibir o corpus do alvo foo ``` cargo test-fuzz foo --display corpus
    root@kitploit:~
  3. Alvo de fuzz foo ``` cargo test-fuzz foo
    root@kitploit:~
  4. Reproduzir crashes encontrados para o alvo foo ``` cargo test-fuzz foo --replay crashes
    root@kitploit:~

Uso```

Usage: cargo test-fuzz [OPTIONS] [TARGETNAME] [-- ...]

Arguments: [TARGETNAME] String that fuzz target's name must contain [ARGS]... Arguments for the fuzzer

Options: --backtrace Display backtraces --consolidate Move one target's crashes, hangs, and work queue to its corpus; to consolidate all targets, use --consolidate-all --coverage Generate coverage for corpus, crashes, hangs, or work queue. Note that generating coverage for instrumented fuzz targets is not supported. --cpus Fuzz using at most cpus; default is all but one --display Display corpus, crashes, generic args, impl generic args, hangs, or work queue. By default, an uninstrumented fuzz target is used. To display with instrumentation, append -instrumented to , e.g., --display corpus-instrumented. --exact Target name is an exact name rather than a substring --exit-code Exit with 0 if the time limit was reached, 1 for other programmatic aborts, and 2 if an error occurred; implies --no-ui, does not imply --run-until-crash or --max-total-time --features Space or comma separated list of features to activate --list List fuzz targets --manifest-path Path to Cargo.toml --max-total-time Fuzz at most of time (equivalent to -- -V ) --no-default-features Do not activate the default feature --no-run Compile, but don't fuzz --no-ui Disable user interface -p, --package Package containing fuzz target --persistent Enable persistent mode fuzzing --pretty Pretty-print debug output when generating coverage, displaying, or replaying --release Build in release mode --replay Replay corpus, crashes, hangs, or work queue. By default, an uninstrumented fuzz target is used. To replay with instrumentation, append -instrumented to , e.g., --replay corpus-instrumented. --reset Clear fuzzing data for one target, but leave corpus intact; to reset all targets, use --reset-all --resume Resume target's last fuzzing session --run-until-crash Stop fuzzing once a crash is found --slice If there are not sufficiently many cpus to fuzz all targets simultaneously, fuzz them in intervals of [default: 1200] --test Integration test containing fuzz target --timeout Number of seconds to consider a hang when fuzzing or replaying (equivalent to -- -t <TIMEOUT * 1000> when fuzzing) --verbose Show build output when generating coverage, displaying, or replaying -h, --help Print help -V, --version Print version

Baixar ferramenta

Try cargo afl fuzz --help to see additional fuzzer options.

root@kitploit:~
Quando você usa a opção `--display`, qualquer saída escrita em stderr pelo alvo é exibida. Isso inclui saída de declarações `eprintln!`, bem como de macros de depuração como `dbg!`. Isso pode ser útil para entender o que está acontecendo em seu código ao processar entradas específicas.

As opções `--display` e `--replay` podem ser passadas juntas, permitindo que você visualize e reproduza entradas do corpus em um único comando, por exemplo:```
cargo test-fuzz foo --display corpus --replay corpus

Funções de conveniência e macros

Aviso: Esses utilitários estão excluídos do versionamento semântico e podem ser removidos em versões futuras de test-fuzz.

dont_care!

A macro dont_care! pode ser usada para implementar serde::Serialize/serde::Deserialize para tipos que são fáceis de construir e cujos valores você não se importa de registrar. Intuitivamente, dont_care!($ty, $expr) diz:

  • Ignorar valores do tipo $ty ao serializar.
  • Inicializar valores do tipo $ty com $expr ao desserializar.

Mais especificamente, dont_care!($ty, $expr) expande para o seguinte:```rust impl serde::Serialize for $ty { fn serialize(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> where S: serde::Serializer, { ().serialize(serializer) } }

impl<'de> serde::Deserialize<'de> for $ty { fn deserialize(deserializer: D) -> std::result::Result<Self, D::Error> where D: serde::Deserializer<'de>, { <()>::deserialize(deserializer).map(|_| $expr) } }

root@kitploit:~
Se `$ty` é uma struct unitária, então `$expr` pode ser omitido. Ou seja, `dont_care!($ty)` é equivalente a `dont_care!($ty, $ty)`.

#### `leak!`

A macro `leak!` pode ajudar a serializar argumentos-alvo que são referências e cujos tipos implementam a trait [`ToOwned`]. Ela é destinada a ser usada com a opção [`convert`].

Especificamente, uma invocação da seguinte forma declara um tipo `LeakedX` e implementa as traits `From` e `test_fuzz::Into` para ele:```rust
leak!(X, LeakedX);

Pode-se então usar LeakedX com a opção convert da seguinte forma:```rust #[test_fuzz::test_fuzz(convert = "&X, LeakedX")

root@kitploit:~
Um exemplo onde `X` é [`Path`] aparece em [conversion.rs] neste repositório.

De modo mais geral, uma invocação da forma `leak!($ty, $ident)` expande-se para o seguinte:```rust
#[derive(Clone, std::fmt::Debug, serde::Deserialize, serde::Serialize)]
struct $ident(<$ty as ToOwned>::Owned);

impl From<&$ty> for $ident {
    fn from(ty: &$ty) -> Self {
        Self(ty.to_owned())
    }
}

impl test_fuzz::Into<&$ty> for $ident {
    fn into(self) -> &'static $ty {
        Box::leak(Box::new(self.0))
    }
}

serialize_ref / deserialize_ref

serialize_ref e deserialize_ref funcionam de forma semelhante a leak!, mas são destinados a serem usados com os atributos de campo serialize_with e deserialize_with do Serde (respectivamente).```rust fn serialize_ref<S, T>(x: &&T, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, T: serde::Serialize, { ::serialize(*x, serializer) }

fn deserialize_ref<'de, D, T>(deserializer: D) -> Result<&'static T, D::Error> where D: serde::Deserializer<'de>, T: serde:🇩🇪:DeserializeOwned + std::fmt::Debug, { let x = ::deserialize(deserializer)?; Ok(Box::leak(Box::new(x))) }

root@kitploit:~
#### `serialize_ref_mut` / `deserialize_ref_mut`

`serialize_ref_mut` e `deserialize_ref_mut` são semelhantes a `serialize_ref` e `deserialize_ref` (respectivamente), exceto que operam em referências mutáveis em vez de referências imutáveis.

## Funcionalidades do pacote `test-fuzz`

As funcionalidades nesta seção se aplicam ao pacote `test-fuzz` como um todo. Ative-as na especificação de dependência do `test-fuzz`, conforme descrito em [The Cargo Book]. Por exemplo, para ativar a funcionalidade `cast_checks`, use:```toml
test-fuzz = { version = "*", features = ["cast_checks"] }

O pacote test-fuzz atualmente suporta os seguintes recursos:

cast_checks

Use cast_checks para verificar automaticamente funções-alvo em busca de conversões inválidas.

Observe que esse recurso habilita cast_checks apenas para funções anotadas com a test_fuzz macro, não para as funções que elas chamam.

Formatos Serde

O test-fuzz pode serializar argumentos-alvo em vários formatos Serde. A seguir estão os recursos usados para selecionar um formato.

  • serde_postcard - Postcard (padrão)

  • serde_bincode - Bincode

Arquivos de corpus gerados automaticamente

O cargo-test-fuzz pode gerar automaticamente valores para tipos que implementam certos traits. Se todos os tipos de argumento de um alvo implementarem tais traits, o cargo-test-fuzz pode gerar automaticamente arquivos de corpus para o alvo.

Os traits que o cargo-test-fuzz atualmente suporta e os valores gerados para eles são os seguintes:

Legenda

  • Add - core::ops::Add
  • Bounded - num_traits::bounds::Bounded
  • Default - std::default::Default
  • Div - core::ops::Div
  • One - num_traits::One
  • Sub - core::ops::Sub

Variáveis de ambiente

TEST_FUZZ_LOG

Durante a expansão de macros:

  • Se TEST_FUZZ_LOG estiver definida como 1, escreva todos os alvos de fuzzing instrumentados e as definições de módulo para a saída padrão.
  • Se TEST_FUZZ_LOG estiver definida como o nome de um crate, escreva os alvos de fuzzing instrumentados e as definições de módulo desse crate para a saída padrão.

Isso pode ser útil para depuração.

TEST_FUZZ_MANIFEST_PATH

Ao executar um alvo fora do diretório de seu pacote, encontre o arquivo Cargo.toml do pacote neste local. Talvez seja necessário definir esta variável de ambiente quando enable_in_production for usado.

TEST_FUZZ_WRITE

Gere arquivos de corpus quando não estiver executando testes para os alvos para os quais enable_in_production estiver definido.

Limitações

Argumentos clonáveis

Os argumentos de um alvo devem implementar o trait Clone. A razão para essa exigência é que os argumentos são necessários em dois lugares: em uma função interna do test-fuzz que escreve arquivos de corpus e no corpo da função-alvo. Para resolver esse conflito, os argumentos são clonados antes de serem passados para a primeira.

Argumentos serializáveis / desserializáveis

Em geral, os argumentos de um alvo devem implementar os traits serde::Serialize e serde::Deserialize, por exemplo, por deriving them. Dizemos "em geral" porque o test-fuzz sabe lidar com certos casos especiais que normalmente não seriam serializáveis/desserializáveis. Por exemplo, um argumento do tipo &str é convertido para String ao serializar e de volta para &str ao desserializar. Consulte também generic_args e impl_generic_args acima.

Variáveis globais

Os harnesses de fuzzing que o test-fuzz implementa não inicializam variáveis globais. Embora execute_with forneça alguma solução, não é uma solução completa. Em geral, fuzzing de uma função que depende de variáveis globais exige métodos ad-hoc.

convert e generic_args / impl_generic_args

Essas opções são incompatíveis no seguinte sentido. Se o tipo de argumento de um alvo de fuzzing for um parâmetro de tipo, convert tentará corresponder ao parâmetro de tipo, não ao tipo no qual o parâmetro é definido. Suportar o último pareceria exigir simular a substituição de tipo como o compilador a executaria. No entanto, isso não está implementado atualmente.

Dicas e truques

  • #[cfg(test)] is not enabled para testes de integração. Se o seu alvo for testado apenas por testes de integração, considere usar enable_in_production e TEST_FUZZ_WRITE para gerar um corpus. (No entanto, observe o aviso que acompanha enable_in_production.)

  • Se você souber o pacote no qual o seu alvo reside, passar -p <package> para cargo test/cargo test-fuzz pode reduzir significativamente os tempos de build. Da mesma forma, se você souber que o seu alvo é chamado por apenas um teste de integração, passar --test <name> pode reduzir os tempos de build.

  • O Rust won't allow you to implementar para tipos de outros repositórios. Mas talvez você consiga outros repositórios para tornar seus tipos serializáveis. Além disso, pode ser útil para obter os repositórios das dependências.

Política de versionamento semântico

Reservamo-nos o direito de alterar o formato de corpora, crashes, hangs e filas de trabalho e de considerar tais alterações como não disruptivas.

Licença

O test-fuzz é licenciado e distribuído sob a licença AGPLv3 com a Macros and Inline Functions Exception. Em linguagem simples, usar a test_fuzz macro, a test_fuzz_impl macro ou as convenience functions and macros do test-fuzz no seu software não exige que ele seja coberto pela licença AGPLv3.

TraitsValores
BoundedT::min_value(), T::max_value()
Bounded + Add + OneT::min_value() + T::one()
Bounded + Add + Div + TwoT::min_value() / T::two() + T::max_value() / T::two()
Bounded + Add + Div + Two + OneT::min_value() / T::two() + T::max_value() / T::two() + T::one()
Bounded + Sub + OneT::max_value() - T::one()
DefaultT::default()
  • Two - test_fuzz::runtime::traits::Two (essencialmente Add + One)
  • serde::Serialize
    patch
    cargo-clone
  • Serde attributes podem ser úteis ao implementar serde::Serialize/serde::Deserialize para tipos difíceis.