Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
test-fuzz — Macros de Rust y subcomando de Cargo para automatizar el fuzzing con afl.rs, incluida la generación de corpus y la implementación de harnesses, integrado con el framework de pruebas de Rust. | Kitploit
Herramientas/GitHubGitHub/trailofbits/test-fuzz
Análisis Dinámico (Sandboxing)Análisis de VulnerabilidadesAnálisis de CódigoFuzzing
GitHubtrailofbits/test-fuzz

test-fuzz

Macros de Rust y subcomando de Cargo para automatizar el fuzzing con afl.rs, incluida la generación de corpus y la implementación de harnesses, integrado con el framework de pruebas de Rust.

Ver Repositorio
20827hace 11 díasRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Sitio web
Compartir

test-fuzz

test-fuzz es un subcomando de Cargo y una colección de macros de Rust para automatizar ciertas tareas relacionadas con el fuzzing con afl.rs, incluyendo:

  • generar un corpus de fuzzing
  • implementar un harness de fuzzing

test-fuzz logra esto (en parte) utilizando las facilidades de prueba de Rust. Por ejemplo, para generar un corpus de fuzzing, test-fuzz registra los argumentos de un objetivo cada vez que se le llama durante una ejecución de cargo test. Del mismo modo, test-fuzz implementa un harness de fuzzing como una prueba adicional en un binario generado por cargo-test. Esta integración estrecha con las facilidades de prueba de Rust es lo que motiva el nombre test-fuzz.

Contenido

  1. Instalación
  2. Descripción general
  3. Componentes
    • Macro test_fuzz
    • Macro test_fuzz_impl
    • Comando cargo test-fuzz
    • Funciones y macros de conveniencia
  4. Características del paquete test-fuzz
  5. Archivos de corpus generados automáticamente
  6. Variables de entorno
  7. Limitaciones
  8. Consejos y trucos
  9. Política de versionado semántico
  10. Licencia

Instalación

Instala cargo-test-fuzz y afl.rs con el siguiente comando:```sh cargo install cargo-test-fuzz cargo-afl

root@kitploit:~
## Overview

El fuzzing con `test-fuzz` es esencialmente tres pasos:\*

1. **Identificar un objetivo de fuzzing**:
   - Agrega las siguientes `dependencies` al archivo `Cargo.toml` del crate de destino:
     ```toml
     serde = "*"
     test-fuzz = "*"
     ```
   - Antepón la macro [`test_fuzz`] a la función objetivo:
     ```rust
     #[test_fuzz::test_fuzz]
     fn foo(...) {
         ...
     }
     ```
2. **Generar un corpus** ejecutando `cargo test`:   ```
   cargo test
  1. Fuzzea tu objetivo ejecutando cargo test-fuzz: ``` cargo test-fuzz foo
    root@kitploit:~

* Puede ser necesario un paso preliminar adicional después de un reinicio:```sh cargo afl system-config

root@kitploit:~
Ten en cuenta que el comando anterior ejecuta `sudo` internamente. Por lo tanto, es posible que se te solicite introducir tu contraseña.

## Componentes

### Macro `test_fuzz`

Anteponer la macro `test_fuzz` a una función indica que la función es un objetivo de fuzzing.

Los efectos principales de la macro `test_fuzz` son:

- Añadir instrumentación al objetivo para serializar sus argumentos y escribirlos en un archivo de corpus cada vez que se llama al objetivo. La instrumentación está protegida por `#[cfg(test)]` para que los archivos de corpus se generen solo al ejecutar pruebas (sin embargo, véase [`enable_in_production`] más abajo).
- Añadir una prueba que lea y deserialice argumentos desde la entrada estándar y aplique el objetivo a ellos. La prueba comprueba una variable de entorno, establecida por [`cargo test-fuzz`], para que la prueba no se bloquee al intentar leer desde la entrada estándar durante una invocación normal de `cargo test`. La prueba está contenida en un módulo para reducir la probabilidad de una colisión de nombres. Actualmente, el nombre del módulo es `target_fuzz`, donde `target` es el nombre del objetivo (sin embargo, véase [`rename`] más abajo).

#### Argumentos

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

Imponga `where_predicates` (p. ej., límites de trait) en la estructura utilizada para serializar/deserializar argumentos. Esto puede ser necesario, por ejemplo, si el tipo de argumento de un objetivo es un tipo asociado. Para ver un ejemplo, véase [associated_type.rs] en este repositorio.

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

Use `parameters` como los parámetros de tipo del objetivo al realizar fuzzing. Ejemplo:```rust
#[test_fuzz(generic_args = "String")]
fn foo<T: Clone + Debug + Serialize>(x: &T) {
    ...
}

Note: Los argumentos del objetivo deben ser serializables para cada instanciación de sus parámetros de tipo. Pero los argumentos del objetivo solo deben ser deserializables cuando el objetivo se instancia con parameters.

impl_generic_args = "parameters"

Usa parameters como los parámetros de tipo Self del objetivo al hacer fuzzing. Ejemplo:```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: Los argumentos del objetivo deben ser serializables para **cada** instanciación de sus parámetros de tipo `Self`. Pero los argumentos del objetivo deben ser deserializables solo cuando el `Self` del objetivo esté instanciado con `parameters`.

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

Al serializar los argumentos del objetivo, convierte valores de tipo `X` a tipo `Y` usando la implementación de `From<X>` de `Y`, o de tipo `&X` a tipo `Y` usando la implementación del trait no estándar `test_fuzz::FromRef<X>` de `Y`. Al deserializar, convierte esos valores de vuelta al tipo `X` usando la implementación del trait no estándar `test_fuzz::Into<X>` de `Y`.

Es decir, el uso de `convert = "X, Y"` debe ir acompañado de ciertas implementaciones. Si `X` implementa [`Clone`], entonces `Y` puede implementar lo siguiente:```rust
impl From<X> for Y {
    fn from(x: X) -> Self {
        ...
    }
}

Si X no implementa Clone, entonces Y debe implementar lo siguiente:```rust impl test_fuzz::FromRef for Y { fn from_ref(x: &X) -> Self { ... } }

root@kitploit:~
Además, `Y` debe implementar lo siguiente (independientemente de si `X` implementa [`Clone`]):```rust
impl test_fuzz::Into<X> for Y {
    fn into(self) -> X {
        ...
    }
}

The definition of test_fuzz::Into is identical to that of std::convert::Into. The reason for using a non-standard trait is to avoid conflicts that could arise from blanket implementations of standard traits.

enable_in_production

Genera archivos de corpus cuando no se están ejecutando pruebas, siempre que la variable de entorno TEST_FUZZ_WRITE esté establecida. El comportamiento predeterminado es generar archivos de corpus solo al ejecutar pruebas, independientemente de si TEST_FUZZ_WRITE está establecida. Al ejecutar un target desde fuera del directorio de su paquete, establece TEST_FUZZ_MANIFEST_PATH en la ruta del archivo Cargo.toml del paquete.

ADVERTENCIA: Establecer enable_in_production podría introducir un vector de denegación de servicio. Por ejemplo, establecer esta opción para una función que se llama muchas veces con distintos argumentos podría llenar el disco. La comprobación de TEST_FUZZ_WRITE pretende proporcionar cierta defensa contra esta posibilidad. No obstante, considera esta opción cuidadosamente antes de usarla.

execute_with = "function"

En lugar de llamar directamente al target:

  • construye un closure de tipo FnOnce() -> R, donde R es el tipo de retorno del target, de modo que llamar al closure llame al target;
  • llama a function con el closure.

Llamar al target de esta manera permite que function configure el entorno de la llamada. Esto puede ser útil, por ejemplo, para fuzzing de [Substrate externalities].

no_auto_generate

No intentes [auto-generate corpus files] para el target.

only_generic_args

Registra los argumentos genéricos del target al ejecutar pruebas, pero no genera archivos de corpus y no implementa un harness de fuzzing. Esto puede ser útil cuando el target es una función genérica, pero no está claro qué parámetros de tipo deberían usarse para el fuzzing.

El flujo de trabajo previsto es: habilita only_generic_args, luego ejecuta cargo test seguido de cargo test-fuzz --display generic-args. Uno de los argumentos genéricos resultantes podría ser utilizable como parameters de generic_args. De manera similar, los argumentos genéricos resultantes de cargo test-fuzz --display impl-generic-args podrían ser utilizables como parameters de impl_generic_args.

Ten en cuenta, sin embargo, que el hecho de que un target se haya llamado con ciertos parámetros durante las pruebas no implica que los argumentos del target sean serializables/deserializables cuando se usan esos parámetros. Los resultados de --display generic-args/--display impl-generic-args son meramente sugerentes.

rename = "name"

Trata el target como si su nombre fuera name al añadir un módulo al ámbito que lo contiene. La expansión de la macro test_fuzz añade una definición de módulo al ámbito que lo contiene. Por defecto, el módulo se nombra de la siguiente manera:

  • Si el target no aparece en un bloque impl, el módulo se llama target_fuzz__, donde target es el nombre del target.
  • Si el target aparece en un bloque impl, el módulo se llama path_target_fuzz__, donde path es el último segmento de la ruta del tipo Self del impl.

Sin embargo, el uso de esta opción hace que el módulo se denomine en su lugar name_fuzz__. Ejemplo:```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 de Serde en argumentos de función

La macro `test_fuzz` permite aplicar [atributos de campo de Serde] a los argumentos de función. Esto proporciona otra herramienta para lidiar con tipos difíciles.

El siguiente es un ejemplo. Los traits `serde::Serialize` y `serde::Deserialize` no pueden derivarse para `Context` porque contiene un `Mutex`. Sin embargo, `Context` implementa `Default`. Por lo tanto, aplicar `#[serde(skip)]` al argumento `Context` hace que se omita al serializar y que tome su valor predeterminado al deserializar.```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);
}

Ten en cuenta que cuando se aplican atributos de campo de Serde a un argumento, la macro test_fuzz no realiza otras conversiones en el argumento.

La macro test_fuzz_impl

Siempre que la macro test_fuzz se use en un bloque impl, el impl debe estar precedido por la macro test_fuzz_impl. Ejemplo:```rust #[test_fuzz_impl] impl Foo { #[test_fuzz] fn bar(&self, x: &str) { ... } }

root@kitploit:~
La razón de este requisito es la siguiente. La expansión de la macro [`test_fuzz`] añade una definición de módulo al ámbito circundante. Sin embargo, una definición de módulo no puede aparecer dentro de un bloque `impl`. Anteponer la macro `test_fuzz_impl` al `impl` hace que el módulo se añada fuera del bloque `impl`.

Si ves un error como el siguiente, probablemente significa que falta un uso de la macro `test_fuzz_impl`:```
error: module is not supported in `trait`s or `impl`s

test_fuzz_impl actualmente no tiene opciones.

Comando cargo test-fuzz

El comando cargo test-fuzz se utiliza para interactuar con los objetivos de fuzz, y para manipular sus corpus, crashes, hangs y colas de trabajo. Ejemplos de invocación incluyen:

  1. Listar objetivos de fuzz ``` cargo test-fuzz --list
    root@kitploit:~
  2. Muestra el corpus del objetivo foo ``` cargo test-fuzz foo --display corpus
    root@kitploit:~
  3. Objetivo de fuzzing foo ``` cargo test-fuzz foo
    root@kitploit:~
  4. Reproduce los fallos encontrados para el objetivo 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

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

root@kitploit:~
Al usar la opción `--display`, se muestra cualquier salida escrita en stderr por el objetivo. Esto incluye la salida de las sentencias `eprintln!`, así como de macros de depuración como `dbg!`. Esto puede ser útil para entender qué está sucediendo en tu código al procesar entradas específicas.

Las opciones `--display` y `--replay` se pueden pasar juntas, lo que te permite ver y reproducir entradas del corpus en un solo comando, por ejemplo:```
cargo test-fuzz foo --display corpus --replay corpus

Funciones y macros de conveniencia

Advertencia: Estas utilidades están excluidas del versionado semántico y pueden eliminarse en futuras versiones de test-fuzz.

dont_care!

La macro dont_care! se puede utilizar para implementar serde::Serialize/serde::Deserialize para tipos que son fáciles de construir y cuyos valores no te interesa registrar. Intuitivamente, dont_care!($ty, $expr) indica:

  • Omite los valores de tipo $ty al serializar.
  • Inicializa los valores de tipo $ty con $expr al deserializar.

Más específicamente, dont_care!($ty, $expr) se expande a lo siguiente:```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:~
Si `$ty` es una estructura unitaria, entonces `$expr` puede omitirse. Es decir, `dont_care!($ty)` es equivalente a `dont_care!($ty, $ty)`.

#### `leak!`

La macro `leak!` puede ayudar a serializar argumentos de destino que son referencias y cuyos tipos implementan el trait [`ToOwned`]. Está pensada para usarse con la opción [`convert`].

Específicamente, una invocación de la siguiente forma declara un tipo `LeakedX` e implementa los traits `From` y `test_fuzz::Into` para él:```rust
leak!(X, LeakedX);

Entonces se puede usar LeakedX con la opción convert de la siguiente manera:```rust #[test_fuzz::test_fuzz(convert = "&X, LeakedX")

root@kitploit:~
Un ejemplo donde `X` es [`Path`] aparece en [conversion.rs] en este repositorio.

Más generalmente, una invocación de la forma `leak!($ty, $ident)` se expande a lo siguiente:```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))
    }
}

/

Descargar herramienta
serialize_ref
deserialize_ref

serialize_ref y deserialize_ref funcionan de manera similar a leak!, pero están pensados para usarse con los atributos de campo serialize_with y deserialize_with de 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` y `deserialize_ref_mut` son similares a `serialize_ref` y `deserialize_ref` (respectivamente), excepto que operan sobre referencias mutables en lugar de inmutables.

## Características del paquete `test-fuzz`

Las características de esta sección se aplican al paquete `test-fuzz` en su conjunto. Actívalas en la especificación de dependencia de `test-fuzz` como se describe en [The Cargo Book]. Por ejemplo, para habilitar la característica `cast_checks`, usa:```toml
test-fuzz = { version = "*", features = ["cast_checks"] }

El paquete test-fuzz actualmente soporta las siguientes features:

cast_checks

Usa cast_checks para comprobar automáticamente las funciones objetivo en busca de casts no válidos.

Ten en cuenta que esta feature habilita cast_checks solo para las funciones anotadas con el test_fuzz macro, no para las funciones a las que llaman.

Formatos de Serde

test-fuzz puede serializar los argumentos objetivo en múltiples formatos de Serde. Las siguientes son las features utilizadas para seleccionar un formato.

  • serde_postcard - Postcard (por defecto)

  • serde_bincode - Bincode

Archivos de corpus generados automáticamente

cargo-test-fuzz puede auto-generar valores para los tipos que implementan ciertos traits. Si todos los tipos de argumentos de un objetivo implementan dichos traits, cargo-test-fuzz puede auto-generar archivos de corpus para el objetivo.

Los traits que cargo-test-fuzz soporta actualmente y los valores generados para ellos son los siguientes:

Trait(s)Value(s)
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()

Clave

  • 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
  • Two - test_fuzz::runtime::traits::Two (esencialmente Add + One)

Variables de entorno

TEST_FUZZ_LOG

Durante la expansión de macros:

  • Si TEST_FUZZ_LOG está establecida en 1, escribe todos los fuzz targets instrumentados y las definiciones de módulos en la salida estándar.
  • Si TEST_FUZZ_LOG está establecida en el nombre de una crate, escribe los fuzz targets instrumentados y las definiciones de módulos de esa crate en la salida estándar.

Esto puede ser útil para depurar.

TEST_FUZZ_MANIFEST_PATH

Al ejecutar un objetivo desde fuera del directorio de su paquete, busca el archivo Cargo.toml del paquete en esta ubicación. Puede ser necesario definir esta variable de entorno cuando se utiliza enable_in_production.

TEST_FUZZ_WRITE

Genera archivos de corpus cuando no se están ejecutando pruebas para aquellos objetivos para los que está definido enable_in_production.

Limitaciones

Argumentos clonables

Los argumentos de un objetivo deben implementar el trait Clone. La razón de este requisito es que los argumentos se necesitan en dos lugares: en una función interna de test-fuzz que escribe archivos de corpus, y en el cuerpo de la función objetivo. Para resolver este conflicto, los argumentos se clonan antes de pasarse a la primera.

Argumentos serializables / deserializables

En general, los argumentos de un objetivo deben implementar los traits serde::Serialize y serde::Deserialize, por ejemplo, derivándolos. Decimos "en general" porque test-fuzz sabe cómo manejar ciertos casos especiales que normalmente no serían serializables/deserializables. Por ejemplo, un argumento de tipo &str se convierte a String al serializar, y de vuelta a &str al deserializar. Véase también generic_args y impl_generic_args más arriba.

Variables globales

Los harnesses de fuzzing que implementa test-fuzz no inicializan variables globales. Si bien execute_with proporciona algún remedio, no es una solución completa. En general, hacer fuzzing de una función que depende de variables globales requiere métodos ad hoc.

convert y generic_args / impl_generic_args

Estas opciones son incompatibles en el siguiente sentido. Si el tipo de argumento de un fuzz target es un parámetro de tipo, convert intentará hacer coincidir el parámetro de tipo, no el tipo al que se establece el parámetro. Soportar esto último parecería requerir simular la sustitución de tipos tal como la realizaría el compilador. Sin embargo, esto no está implementado actualmente.

Consejos y trucos

  • #[cfg(test)] no está habilitado para pruebas de integración. Si tu objetivo se prueba solo con pruebas de integración, considera usar enable_in_production y TEST_FUZZ_WRITE para generar un corpus. (Sin embargo, ten en cuenta la advertencia que acompaña a enable_in_production.)

  • Si conoces el paquete en el que reside tu objetivo, pasar -p <package> a cargo test/cargo test-fuzz puede reducir significativamente los tiempos de compilación. Del mismo modo, si sabes que tu objetivo se llama desde una sola prueba de integración, pasar --test <name> puede reducir los tiempos de compilación.

  • Rust no te permite implementar serde::Serialize para los tipos de otros repositorios. Pero quizás puedas parchear otros repositorios para hacer que sus tipos sean serializables. Además, cargo-clone puede ser útil para obtener los repositorios de las dependencias.

  • Los Atributos de Serde pueden ser útiles para implementar serde::Serialize/serde::Deserialize en tipos difíciles.

Política de versionado semántico

Nos reservamos el derecho de cambiar el formato de los corpus, los crashes, los cuelgues y las colas de trabajo, y de considerar que dichos cambios no rompen la compatibilidad.

Licencia

test-fuzz está licenciado y distribuido bajo la licencia AGPLv3 con la Macros and Inline Functions Exception. En lenguaje llano, usar el test_fuzz macro, el test_fuzz_impl macro, o las funciones y macros de conveniencia de test-fuzz en tu software no requiere que este esté cubierto por la licencia AGPLv3.