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
efcf-framework — EF/CF - Fuzzing de contratos inteligentes extremadamente rápido | Kitploit
Herramientas/GitHubGitHub/uni-due-syssec/efcf-framework
Análisis de VulnerabilidadesExplotaciónFuzzingAnálisis de Binarios
GitHubuni-due-syssec/efcf-framework

efcf-framework

EF/CF - Fuzzing de contratos inteligentes extremadamente rápido

Ver Repositorio
7013hace 3 añosRevisado 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 →
Compartir

EF/CF - el Fuzzer de Contratos Inteligentes de Ethereum Extremadamente Rápido

EF/CF es un nuevo enfoque para el fuzzing de contratos inteligentes: en lugar de usar un nuevo fuzzer construido a medida, reutiliza la infraestructura de fuzzing existente de C/C++ para contratos inteligentes. Actualmente, AFL++ es el fuzzer soportado principalmente, aunque hay también un soporte muy rudimentario para libfuzzer y honggfuzz.

¿Por qué usar la infraestructura de fuzzing existente?

  • Velocidad. Podemos fuzzear más rápido. Normalmente conseguimos alrededor de 20k ejecuciones/seg/núcleo.
  • Los fuzzers de código nativo están bien diseñados y optimizados.
  • Orientación por cobertura, gestión de colas, reproducción determinista de casos de prueba, etc.

¿Cuáles son algunos de los problemas que encontramos en el camino?

  • Necesitamos enseñar al fuzzer sobre la estructura: es decir, qué es una transacción y cuál es la ABI del contrato inteligente. Usamos un mutador personalizado para esto: ./src/ethmutator/
  • Para aumentar la velocidad y obtener una retroalimentación de cobertura útil, traducimos el bytecode de EVM a C++ usando un transpilador personalizado ./src/evm2cpp/

Este repositorio es el punto de entrada principal para el proyecto EF/CF. Contiene todo el código relevante como subproyectos en ./src/ y varios scripts de conveniencia para la instalación, scripts para lanzar campañas de fuzzing y varios conjuntos de datos para probar el fuzzer (y compararlo con otras herramientas).

  • ./src/ - contiene todo el código fuente necesario para construir y ejecutar EF/CF; para la reproducibilidad, todas las dependencias directas se añaden como submódulos de git.
  • ./data/ - contiene los conjuntos de datos utilizados durante la evaluación
  • ./scripts - contiene scripts para ejecutar experimentos, instalación, etc.
  • ./docker - Dockerfile para el flujo de trabajo basado en contenedores
    • El estándar es Ubuntu, pero también puedes tener un contenedor basado en Fedora o Arch Linux si lo prefieres.
    • ./docker/tools/ contiene dockerfiles para las herramientas con las que evaluamos EF/CF Hicimos todo lo posible por fijar las versiones que evaluamos en nuestro artículo en los dockerfiles.
  • ./EXPERIMENTS.md - contiene una guía para reproducir los experimentos de nuestro artículo.
  • ./examples - contiene ejemplos de salidas producidas por EF/CF

El artículo de EF/CF

Describimos la arquitectura e implementación de EF/CF y resumimos nuestra evaluación resultados en nuestro artículo: preprint de arxiv.org

Cita en Trabajos Académicos

Cuando se refiera a EF/CF en trabajos académicos, utilice la siguiente entrada bibtex para la cita:```bibtex @InProceedings{efcf2023, author = "Michael Rodler and David Paaßen and Wenting Li and Lukas Bernhard and Thorsten Holz and Ghassan Karame and Lucas Davi", title = "EF/CF: High Performance Smart Contract Fuzzing for Exploit Generation", booktitle = "{IEEE} European Symposium on Security and Privacy ({EuroS&P})", publisher = "{IEEE}", year = "2023", }

root@kitploit:~
## Inicio rápido

La forma recomendada es ejecutar EF/CF como un contenedor docker interactivo.

1. Entra en el contenedor con una shell   ```
   docker run --rm -it ghcr.io/uni-due-syssec/efcf-framework

o construye el contenedor desde el repositorio clonado ``` make gitmodules # to fetch the git submodules make container-enter

root@kitploit:~
1. Compilar y luego fuzzear un contrato solidity hasta que se descubra el
primer fallo/error:   ```
efcfuzz --until-crash --out ./baby_bank_results/ --source ./data/examples/baby_bank.sol
  1. Inspeccione el fallo identificado ``` cd /tmp/baby_bank_results/ ./r.sh crashes_min/default_id:000000*
    root@kitploit:~

Instalación / Configuración

Submódulos de Git

¿Sin git? Si usas una versión tarball/docker, ignora esto.

Ejecuta git submodule update --init para obtener los últimos commits de los submódulos en repositorios ya clonados. Asegúrate de ejecutar esto también en ./src/eEVM.``` git submodule update --init; cd src/eEVM/; git submodule update --init; cd ../../

root@kitploit:~
*Advertencia:* Ejecutar `git clone --recursive $repo` o pasar el argumento `--recursive` a `git sumbodule (update|init)` hará que git entre de forma recursiva en los submódulos del repositorio AFL++, que no son necesarios para este proyecto. Así que para ahorrar espacio es mejor evitar los checkouts recursivos de submódulos.


### Contenedor

Proporcionamos los siguientes objetivos make de conveniencia para flujos de trabajo basados en contenedores:```sh
make container-build  # build default efcf container
make container-enter  # enter default efcf container in current working dir

Si quieres asegurar una compilación limpia, puedes usar el siguiente comando```sh make container-build CLEAN_CHECKOUT=1

root@kitploit:~
Alternativamente, el contenedor puede construirse con el siguiente comando de docker:```sh
docker build \
    -f docker/ubuntu.Dockerfile \
    -t efcf:latest \
    .

Tenga en cuenta que también hay un Dockerfile basado en Archlinux y Fedora. Deberían funcionar también, pero no están tan probados.

Para distribuir manualmente una imagen de Docker (p. ej., si se incluyen algunos cambios locales), use:``` make container-release docker load -i ./efcf*.tar

root@kitploit:~
Recomendamos las siguientes opciones de Docker para el lanzamiento:

* `--security-opt seccomp=unconfined` - mejor rendimiento de fuzzing
* `--net=host` - para acceso fácil a un nodo Ethereum local
* `--tmpfs "/tmp/efcf/":exec,size=6g` - poner los archivos temporales de EF/CF en un ramdisk si es posible (menos desgaste del disco)
* `--privileged` - para ejecutar `afl-system-config` o `efcfuzz --configure-system`
* `-v` - para persistir los datos de salida de EF/CF


### VM / Bare-Metal

Para flujos de trabajo basados en VM o bare-metal:```sh
make system-install   # install efcf to current system (requires root or sudo rights)

Ten en cuenta que muchos de los scripts funcionan con el diseño de directorios relativos de todos modos, por lo que esto principalmente instala dependencias y algunas herramientas que son útiles tener en tu PATH. Hemos probado ejecutar EF/CF en las siguientes distribuciones de Linux:

  • Ubuntu Jammy (o posterior)
  • Fedora ($ > 35 $)
  • Archlinux

(La distribución no importa demasiado, probamos LLVM 13 y 14, siendo 14 la opción preferida. LLVM 11 o 12 también podrían seguir funcionando, pero como siempre - cuanto más nuevo, mejor. La parte importante es que haya un LLVM que sea compatible con nuestro fork de AFL++.)

En Mac OS / M1

No hemos probado EF/CF de forma nativa en Mac OS. Es probable que las cosas no funcionen (por ejemplo, afl-clang-lto en Mac OS parece no funcionar). La mejor opción es utilizar docker.```sh

make sure that the submodules are initialized

make gitmodules

pull the linux/amd64 base image

docker pull ubuntu:jammy --platform linux/amd64

build the ef/cf image

docker build -t efcf:latest -f docker/ubuntu.Dockerfile --platform linux/amd64 .

launch the EF/CF container

docker run --tmpfs "/tmp/efcf/":exec,size=8g --platform linux/amd64 --rm -it -v $(pwd):$(pwd) -w $(pwd) efcf:latest

root@kitploit:~
Probamos con docker desktop v4.21.1 y el uso básico de EF/CF funciona. Sin embargo, ten en cuenta lo siguiente:
* Si ves segfaults al compilar: prueba a aumentar el límite de memoria de la VM que docker usa en Mac OS.
* Prueba a habilitar la aceleración usando rosetta en docker - con un poco de suerte será algo más rápido.


### Configuración de desarrollo

Las herramientas generalmente no necesitan instalarse. Instala las dependencias necesarias como en el script `system-install.sh` o como en los Dockerfiles.

Para mayor comodidad, tenemos algunos scripts para actualizar tu `PATH`:```sh
# POSIX-like shells (i.e., bash, ...)
source ./scripts/env.sh

# for the fish shell
source ./scripts/env.fish

Clave de API de Etherscan

Algunos de los scripts requieren una clave de API para obtener metadatos (por ejemplo, el ABI) del servicio Etherscan. Si tienes una clave de API, tienes que establecer la variable de entorno ETHERSCAN_API_KEY para pasarla a los scripts. Para un flujo de trabajo basado en docker, puedes lanzar el contenedor docker con la bandera --env o poner tu clave de API en el archivo .etherscan_api_key, lo que integrará la clave de API en el contenedor docker.

Iniciar EF/CF con el lanzador

Por conveniencia, utilizamos un script contenedor que se encarga de todos los detalles por ti, al iniciar el fuzzer EF/CF: efcfuzz

Puedes establecer muchas opciones de línea de comandos para configurar el comportamiento del fuzzer con respecto al proceso de compilación y fuzzing. Echa un vistazo a efcfuzz --help para ver una lista de opciones.

Ejemplos

Compila el código fuente de solidity a código nativo EF/CF y comienza a hacer fuzzing durante 5 minutos (es decir, 300 segundos).```bash efcfuzz --timeout 300 --source ./data/examples/baby_bank.sol

root@kitploit:~
Alternativamente, inicie con salida de fuzzing reducida (`--quiet` suprime la
salida del fuzzer base, mientras que `--print-progress` imprimirá un breve resumen del
progreso del fuzzing), y ejecute el fuzzer en 4 núcleos.```bash
efcfuzz --quiet --print-progress --cores 4 --timeout 300 --source ./data/examples/baby_bank.sol

Usa el bytecode ya compilado y compila el bytecode a código nativo EF/CF y comienza a fuzzear.```bash

efcfuzz can handle the combined.json output of the solidity compiler

pushd ./data/examples/; make baby_bank.combined.json; popd efcfuzz --timeout 300 --bin-runtime ./data/examples/baby_bank.combined.json

but you can also explicitely pass the runtime and deploy bytecode and the ABI

definition. This is useful if you want to fuzz contracts using other

compilers (e.g., vyper).

pushd ./data/examples/; make baby_bank; popd efcfuzz --timeout 300
--bin-runtime ./data/examples/baby_bank.bin-runtime
--bin-deploy ./data/examples/baby_bank.bin
--abi ./data/examples/baby_bank.abi

root@kitploit:~
El wrapper puede exportar el estado de un contrato desde un nodo go-ethereum/erigon y comenzar
el fuzzing desde allí.```bash
$ efcfuzz --timeout 300 --live-state 0xfffF8D17CB019E0825c478c666B251A7099df3FD

Además, puedes pasar --include-address-deps=y para buscar recursivamente direcciones de otras cuentas en el almacenamiento del contrato exportado e incluir también esas en la exportación de estado. Sin embargo, esto no incluye otros contratos almacenados en tipos mapping de Solidity. Para exportar realmente todo el estado de forma recursiva, pasa también la opción --include-mapping-deps=y.

Pero ten cuidado: esta búsqueda recursiva puede provocar tiempos de compilación largos y un rendimiento de fuzzing deficiente. Especialmente, los contratos de uso frecuente pueden tener una gran cantidad de estado interno y usar su estado exportado puede ralentizar el fuzzing. Comprueba si el fuzzer puede lograr más de 1k execs/sec. Si no es así, es mejor que intentes crear un estado artificial y más pequeño. Prueba a ejecutar un nodo local de go-ethereum en modo --dev y despliega allí tus contratos. Luego exporta el estado en vivo desde ese nodo.

El wrapper guarda en caché las compilaciones, por lo que una segunda ejecución de fuzzing debería arrancar mucho más rápido, porque ya no se necesita el tiempo de compilación inicial. Si solo quieres compilar y colocarlo en la caché, puedes pasar el argumento --build-only.

Ejemplo: Fuzzing con propiedades

EF/CF también admite fuzzing basado en propiedades usando la misma definición de propiedades que el fuzzer echidna. Las propiedades (o invariantes) se expresan como funciones de Solidity que actúan como oráculo de errores para el fuzzer. Por ejemplo, puedes añadir una función de Solidity:```solidity function test_property_balance() public view returns (bool) { return total_balance < 1000; }

root@kitploit:~
Lo cual representa la propiedad de que total_balance siempre debe estar por debajo de
1000. EF/CF entonces reportará un bug si logra violar esta propiedad usando
alguna secuencia de transacciones, es decir, si el oráculo devuelve `false`.

Para indicarle a EF/CF que esto es una propiedad, necesitas especificar una lista de firmas de
funciones en un archivo, que EF/CF recogerá como una lista de propiedades
para comprobar durante el fuzzing.

La forma más sencilla es obtener las firmas relevantes usando la bandera `--hashes`
del compilador de Solidity, p. ej.,```
solc --hashes ./path/to/your.sol | grep test_property > property_list

Ahora puedes lanzar el fuzzer con:``` efcfuzz --source ./path/to/your.sol --properties ./property_list -C

root@kitploit:~
También puedes añadir `--disable-detectors` para desactivar los oráculos de bugs integrados basados en ether.

Puedes probar el siguiente ejemplo para fuzzing basado en propiedades:```
efcfuzz \
    --properties ./data/examples/harvey_baz_properties.signatures
    --disable-detectors \
    --until-crash --timeout 120 \
    --source ./data/examples/harvey_baz.sol \

Ejemplo: Fuzzing para Eventos

EF/CF admite fuzzing para violaciones de aserciones que se expresan mediante eventos. De hecho, también admitimos el uso de eventos personalizados arbitrarios como oráculo de bugs. Por defecto, EF/CF identificará un bug si el contrato objetivo registró uno de los siguientes eventos: AssertionFailed(), AssertionFailed(uint256), AssertionFailed(string), y Panic(uint256).``` efcfuzz --event-assertions
--timeout 120 --until-crash
--source ./data/properties-assertions-tests/verifyfunwithnumbers.sol

root@kitploit:~
También puede especificar temas/hashes de eventos personalizados adicionales a los que prestar atención en un
archivo con `--event-assertions-list ./path/to/eventslist.txt`. Al igual que con la
lista de propiedades anterior, puede obtener el formato usando `solc --hashes` y
copiando los hashes y nombres de eventos al archivo de lista de eventos.

Por defecto, EF/CF ignorará los eventos que no hayan sido emitidos por el contrato
objetivo. Si desea cambiar eso, use `--event-assertions-target-only=n`.

(Nota: puede usar `--assertions` para habilitar tanto la comprobación de eventos como de aserciones de solidity)

**Ejemplo: Fuzzing de aserciones de Solidity ^0.8**

Actualmente, no admitimos el fuzzing de aserciones arbitrarias en código solidity
para versiones de solidity anteriores a 0.8. Anteriormente, las aserciones de solidity simplemente
activaban un opcode `invalid`, lo que resultaba en un revert bastante brusco. La
versión 0.8 de Solidity cambió el comportamiento: en lugar de usar el opcode `invalid` para
revertir transacciones, ahora utilizan el mecanismo `revert` y señalan errores
de vuelta al llamador. Podemos utilizar este tipo de propagación de errores como un oráculo de bugs
en EF/CF. Actualmente, EF/CF admite la comprobación del tipo de error de Solidity
`Panic(uint256)`. [Más información sobre errores de
Solidity.](https://docs.soliditylang.org/en/v0.8.0/control-structures.html?highlight=assert#panic-via-assert-and-error-via-require)```
efcfuzz --sol-assertions \
    --timeout 120 --until-crash \
    --source ./data/assertions-tests/overflow.sol

(Nota: puedes usar --assertions para habilitar tanto la comprobación de aserciones de eventos como de Solidity)

Especificaciones del sistema y configuración

Recomendamos asignar de 4 a 16 núcleos y aproximadamente 1 GB de memoria por núcleo. Puedes utilizar la opción --configure-system para configurar tu sistema para fuzzing de alta velocidad, o configurarlo tú mismo. En contenedores Docker también necesitas configurar el host para obtener el mejor rendimiento. Si es un host no crítico, puedes lanzar el contenedor como --privileged y usar /usr/local/bin/afl-system-config para configurar el sistema para fuzzing de alta velocidad (nota: esto esencialmente ejecuta el contenedor como root).```

configure system

docker run --rm -it --privileged efcf afl-system-config

run fuzzer (somewhat sandboxed and using a tmpfs for less SSD wear)

docker run --rm -it
--security-opt seccomp=unconfined
--tmpfs "/tmp/efcf/":exec,size=6g
efcf

root@kitploit:~
## Ejecutar un Experimento de Fuzzing

Para ejecutar el experimento en el conjunto de datos `data/tests/` puedes usar el siguiente
comando para compilar los contratos y su arnés de fuzzing, y luego ejecutar el
fuzzer con diferentes configuraciones, múltiples repeticiones, etc. Como esto
llevaría bastante tiempo, podemos ejecutar esos experimentos en paralelo. Dividimos los
experimentos de fuzzing en un paso de compilación y un paso de fuzzing. Los pasos de compilación
compilarán todos los contratos inteligentes secuencialmente (aunque la compilación en sí usa múltiples
núcleos). Luego lanzamos 8 instancias del fuzzer en segundo plano, que tomarán
los artefactos de compilación del paso de compilación y comenzarán las ejecuciones de fuzzing. El Makefile
intentará automáticamente lanzar todo en el contenedor adecuado si
está disponible `docker` o `podman`.```bash
make build-tests
make fuzz-tests CONTAINER_BACKGROUND=1 FUZZER_INSTANCES=8

Deshabilitamos seccomp y el sandboxing de red al lanzar los contenedores en segundo plano. Deshabilitar el sandboxing de seccomp mejora el rendimiento del fuzzing. Usar la red del host permite que EF/CF acceda a los nodos de Ethereum en la red local sin configuración adicional.

Usamos el script ./scripts/run-tools-on-dataset.py para ejecutar las otras herramientas dentro de contenedores Docker en estos conjuntos de datos, p. ej., con estos comandos para el conjunto de datos múltiple:```bash python3 ./scripts/run-tools-on-dataset.py ./data/multi/ cd ./results/tools-multi/ python3 ../../scripts/get-tools-on-dataset-stats.py head stats.csv

root@kitploit:~
Debes adaptar el script para configurar las herramientas y el número de ejecuciones.

### Configuración de un experimento de fuzzing

Aquí, usamos el experimento `tests` como ejemplo. Simplemente reemplaza la cadena
`tests` con el nombre del experimento en los siguientes pasos:

1. Reúne tu conjunto de datos en `./data/`, p. ej., el conjunto de datos `./data/tests` con contratos
   de prueba. Para contratos de solidity tenemos un `Makefile` genérico para compilar los
   contratos: `sol.Makefile`. Puedes reutilizarlo si lo deseas, consulta
   `./data/tests/Makefile` para ver un ejemplo.
2. Crea un script para generar los artefactos de compilación, incluyendo cualquier
   paso de preprocesamiento/extracción necesario. Por ejemplo, para el conjunto de datos `tests` tenemos
   el script `./scripts/build-tests.sh`. Los artefactos de compilación deben
   almacenarse en `./builds/tests/${contract}.build.tar.xz`.
3. Crea un script para lanzar la campaña de fuzzing, p. ej., para el conjunto de datos `tests`
   crea un script llamado `./scripts/fuzz-tests.sh`. Normalmente puedes usar la
   función común de campaña de fuzzing de `./scripts/common.sh`. Echa un vistazo a
   `fuzz-tests.sh` como plantilla.
4. Los resultados de `fuzz-tests.sh` se almacenarán en `./results/run-fuzz-tests/`.
5. Para resumir los resultados proporcionamos `./scripts/summarize.py` para los scripts
   lanzadores basados en bash y `./scripts/summarize_l.py` para scripts lanzadores
   de python (la herramienta `efcfuzz`).
   Puede que necesites adaptar estos scripts dependiendo de tu paso 3.



### Experimentos de fuzzing existentes

#### Puntos de referencia (benchmarks)

* <a href="./data/multi/">`./data/multi`</a> contiene el benchmark de escalabilidad
  que usamos para evaluar qué tan bien escala una herramienta de análisis a secuencias de transacciones
  más largas. Consiste en tres tipos de contratos:
    * `multi_gen_*.sol` - contratos sintetizados automáticamente, que hacen un montón
      de `require(input <= MAGIC)` y luego establecen una variable de estado interna. Si
      todas las variables de estado están establecidas, entonces se puede activar el `selfdestruct` (o el oráculo de echidna).
    * `multi_man_complex_*.sol` - variantes creadas manualmente que funcionan de manera similar
      a los contratos del tipo `multi_gen`, pero presentan restricciones un poco más complicadas
      (p. ej., otras cosas además de igualdad y desigualdad con un valor mágico)
    * `justlen_*.sol` - estos están tomados del [ejemplo echidna-parade](https://github.com/crytic/echidna-parade/blob/main/examples/justlen.sol)
    * `multi_simple_*.sol` - comprobaciones de sanidad que verifican que un fuzzer/herramienta
      podría en teoría encontrar errores que requieren 9 o 10 transacciones. Aquí el
      analizador solo necesita llamar a 10 funciones en el orden correcto sin ningún
      argumento. Esto es bastante fácil para la mayoría de las herramientas de análisis.
* <a href="./data/throughput/">`./data/throughput`</a> contiene los contratos que
  usamos para evaluar el rendimiento. Esta es una selección de contratos de
  tamaño variable. Ten en cuenta que parcheamos todas las vulnerabilidades en estos contratos,
  de modo que las vulnerabilidades encontradas no afecten las mediciones de rendimiento.
* <a href="./data/cov-max-testset">`./data/cov-max-testset`</a> contiene los
  contratos que usamos para la comparación de fuzzers basada en la cobertura de código.

#### Detección de errores

* <a href="./data/ethbmc-vuln">`./data/ethbmc-vuln`</a> lista de contratos que
  EthBMC detectó como vulnerables.
* <a href="./data/ethbmc-timeouts">`./data/ethbmc-timeouts`</a> lista de
  contratos, donde EthBMC detuvo el análisis debido a un tiempo de espera agotado.
* <a href="./data/reentrancy">`./data/reentrancy`</a> un conjunto de contratos
  vulnerables a ataques de reentrancia.
* <a href="./data/sailfish-dao-tp">`./data/sailfish-dao-tp`</a> un conjunto de contratos
  que se ha verificado que contienen un error de reentrancia como parte del
  [estudio sailfish](https://github.com/ucsb-seclab/sailfish/tree/master/data/ground-truth).
* <a href="./data/sailfish-dao">`./data/sailfish-dao`</a> lista de todos los contratos, donde
  [sailfish](https://github.com/ucsb-seclab/sailfish/tree/master/data/bugs)
  encontró un error de reentrancia.
* <a href="./data/sereum">`./data/sereum`</a> una lista de contratos
  vulnerables a ataques de reentrancia según [Sereum](https://github.com/uni-due-syssec/sereum-results).
* <a href="./data/smartbugs-curated-accesscontrol">`./data/smartbugs-curated-accesscontrol`</a>
  contratos de los smartbugs seleccionados, clasificados como errores de "control de acceso"
  ([smartbugs github](https://github.com/smartbugs/smartbugs/tree/master/dataset/access_control))
* <a href="./data/smartbugs-curated-reentrancy">`./data/smartbugs-curated-reentrancy`</a>
  contratos de los smartbugs seleccionados, clasificados como errores de "reentrancia"
  ([smartbugs github](https://github.com/smartbugs/smartbugs/tree/master/dataset/reentrancy))

#### Pruebas

Los siguientes conjuntos de datos contienen contratos de prueba sintéticos básicos para probar las capacidades del fuzzer:

* <a href="./data/tests">`./data/tests`</a> pruebas básicas recopiladas de múltiples
  fuentes que verifican las capacidades básicas de un fuzzer. Todas usan un oráculo
  de selfdestruct.
* <a href="./data/tests-not-vuln">`./data/tests-not-vuln`</a> igual que tests, pero no
  deben detectarse como vulnerables.
* <a href="./data/properties-tests">`./data/properties-tests`</a> pruebas para
  fuzzing basado en propiedades
* <a href="./data/assertions-tests">`./data/assertions-tests`</a> pruebas para
  fuzzing de aserciones.


## Fuzzing en más detalle

Utilizamos scripts envoltorio para lanzar el fuzzer real (AFL++ en nuestro caso).
Esto se hace automáticamente cuando se usa el lanzador `efcfuzz`.```bash
$ cd data/tests
$ make SimpleDAO.evm2cpp
$ cd ../../src/eEVM/
$ env AFL_BENCH_UNTIL_CRASH=1 ./fuzz/launch-aflfuzz.sh SimpleDAO

Si tienes tmux y tmuxp instalados, entonces para el desarrollo y la inspección la versión interactiva del script podría ser útil:```bash $ ./fuzz/interactive-aflfuzz.sh -b SimpleDAO

root@kitploit:~
Esto compilará y hará fuzzing durante bastante tiempo. Luego puedes ejecutar
`cd ./fuzz/out/SimpleDAO*` para ver los resultados del fuzzing. Nuestros scripts envoltorio hacen
un trabajo extra además de lanzar el programa `afl-fuzz`, es decir, principalmente
posprocesan los resultados. Además, generan varios scripts de conveniencia
para analizar los casos de prueba generados.

* `./a.sh` - Imprime una forma legible de un caso de prueba, envoltorio de
  `efuzzcaseanalyzer`.
* `./r.sh` - Ejecuta un caso de prueba con la misma configuración que cuando se ejecutó el fuzzer.
* `./m.sh` - Minimiza un caso de prueba con la misma configuración que cuando se ejecutó el
  fuzzer.
* `./c.sh` - Analiza la "cadena" de casos de prueba que conducen al caso de
  prueba dado. Útil para analizar/optimizar el fuzzer. Puedes ver rápidamente qué
  caso de prueba fue producido por qué cadena de mutaciones sobre qué entradas de la cola.
  Requiere `fzf`.

También hay otros informes de conveniencia, como

* `./bugs` y `./bugtypes` que resumen cualquier error que haya sido identificado.
* `./crashes_min`, que contiene crashes minimizados de todas las instancias de
  `afl-fuzz`.

**Ver Cobertura de Código de Bloques Básicos de EVM**```bash
$ cat coverage-percent-all.evmcov
70.73170731707317

El script fuzz/evm-bb-coverage.sh calculará la cobertura de bloques básicos dado un directorio de salida de AFL. El harness puede opcionalmente volcar una traza de bloques básicos, que luego se compara con una lista de bloques básicos generada por evm2cpp (es decir, los archivos .bb_list en eEVM/contracts/).

Por defecto también calculamos la cobertura que producen nuestras semillas genéricas predeterminadas (ver eEVM/fuzz/generic_seeds):```bash $ cat coverage-percent-seeds.evmcov 10.5890

root@kitploit:~
La lista de bloques básicos cubiertos se almacena en el archivo `all.evmcov`.


**Ver Resumen de Casos de Prueba Generados**

`efuzzcaseanalyzer` puede utilizarse para ver/resumir los casos de prueba generados,
p. ej.,```
$ efuzzcaseanalyzer -a ./contract.abi -s ./crashes_min/
Transactions Sequences:
--------------------------------------------------------------
TX [🪙]
    deposit()[🪙];
    withdraw(uint256)[↕️ ↩️ ];
    withdraw(uint256)[];
--------------------------------------------------------------
Number of fuzzcases: 1
Average number of TXs: 3
Number of unique TX sequences: 1
Number of unique TX sequences (consecutive deduplicated): 1

Los resúmenes suelen almacenarse en los archivos crashes_tx_summary y queue_tx_summary, pero este último puede ser un poco verboso.

Analizar un único caso de prueba que provoca un fallo``` $ ./a.sh default/crashes/id:000000,...

roughly equivalent to running

$ efuzzcaseanalyzer -a ./contract.abi default/crashes/id:000000,... Block header: number: 0 difficulty: 0 gas_limit: 0 timestamp: 0 initial_ether: 0

TX with tx_sender: 54 (selector); call_value: 0x0; length: 36; block+=1; #returns=0 func: withdraw(uint256) input: { Uint(80), } TX with tx_sender: 238 (selector); call_value: 0x246ddf979; length: 4; block+=1; #returns=0 func: deposit() input: { } TX with tx_sender: 153 (selector); call_value: 0x3860e6373; length: 4; block+=1; #returns=0 func: deposit() input: { } TX with tx_sender: 166 (selector); call_value: 0x0; length: 36; block+=1; #returns=1 func: withdraw(uint256) input: { Uint(37000000000000000000), } returns: return val: 1; allows reenter: 2; data: 0x0000000000000000000000000000000000000000000000000000000000000001 TX with tx_sender: 166 (selector); call_value: 0x0; length: 36; block+=1; #returns=0 func: withdraw(uint256) input: { Uint(37000000000000000000), }

root@kitploit:~
Y para obtener el resultado real del objetivo de fuzzing, puedes ejecutar:```
$ ./r.sh default/crashes/id:000000,sig:06,src:000000+000010,time:1584,EM-________SAO_______AD
# roughly equivalent to running
$ env EVM_DEBUG_PRINT=1 ./build/fuzz_multitx default/crashes/id:000000,sig:06,src:000000+000010,time:1584,EM-________SAO_______AD

[...]

account 0xc4b803ea8bc30894cc4672a9159ca000d377d9a3 has balance 0x100000000000000000000000000000001bc16d67562e80000( > 0x1000000000000000000000000000000000000000000000000)
Aborted (core dumped)

Esto le proporciona una gran cantidad de salida verbosa, incluidas algunas partes de los rastros de ejecución de los contratos y el resultado de la comprobación de saldo que realiza el harness.

Minimización de entradas que provocan fallos

Las entradas que provocan fallos a menudo contienen transacciones no relacionadas debido al enfoque de pruebas aleatorizadas. Esto se puede mitigar realizando una minimización de la entrada que provoca el fallo (es decir, reducir la entrada siempre que siga provocando un fallo). Si desea minimizar entradas que no provocan fallos, puede usar la bandera -M para habilitar la minimización según la cobertura como criterio de minimización.

El siguiente comando reducirá el caso de prueba y sobrescribirá el archivo:``` $ efuzzcaseminimizer -oa ./contract.abi ./build/fuzz_multitx ./default/crashes/id:000000,sig:06,src:000000+000010,time:1584,EM-________SAO_______AD

[..]

=== Before minimizing: === Block header: number: 0 difficulty: 0 gas_limit: 0 timestamp: 0 initial_ether: 0

TX with tx_sender: 54 (selector); call_value: 0x0; length: 36; block+=1; #returns=0 func: withdraw(uint256) input: { Uint(80), } TX with tx_sender: 238 (selector); call_value: 0x246ddf979; length: 4; block+=1; #returns=0 func: deposit() input: { } TX with tx_sender: 153 (selector); call_value: 0x3860e6373; length: 4; block+=1; #returns=0 func: deposit() input: { } TX with tx_sender: 166 (selector); call_value: 0x0; length: 36; block+=1; #returns=1 func: withdraw(uint256) input: { Uint(37000000000000000000), } returns: return val: 1; allows reenter: 2; data: 0x0000000000000000000000000000000000000000000000000000000000000001 TX with tx_sender: 166 (selector); call_value: 0x0; length: 36; block+=1; #returns=0 func: withdraw(uint256) input: { Uint(37000000000000000000), }

=== After minimizing: === Block header: number: 0 difficulty: 0 gas_limit: 0 timestamp: 0 initial_ether: 15133991795

TX with tx_sender: 4 (selector); call_value: 0x246ddf979; length: 4; block+=0; #returns=0 func: deposit() input: { } TX with tx_sender: 4 (selector); call_value: 0x0; length: 36; block+=1; #returns=1 func: withdraw(uint256) input: { Uint(37000000000000000000), } returns: return val: 1; allows reenter: 2; data: 0x TX with tx_sender: 0 (selector); call_value: 0x0; length: 36; block+=1; #returns=0 func: withdraw(uint256) input: { Uint(37000000000000000000), }

root@kitploit:~
## Lectura del formato de los casos de prueba

El formato de los casos de prueba está orientado al fuzzing y no es tan directo
de leer. Hay varias sutilezas que debes tener en cuenta.

* El formato de los casos de prueba se considera una "cola" de transacciones que
  pueden ejecutarse. En cuanto se encuentra cualquier problema, el procesamiento
  del caso de prueba se detiene. Esto incluye:
    * Cuando una transacción revierte.
    * Cualquier error encontrado por el código de instrumentación (harness).
    * Cualquier bug que se active y se detecte.
  En consecuencia, un caso de prueba impreso no se corresponde necesariamente
  con lo que se ejecuta: puede haber transacciones al final que no se ejecutan.
  ¡Revisa la salida verbosa y usa el minimizador para eliminar esas!
* Del mismo modo, puede haber demasiados `returns` o marcadores `reenter`
  espurios. Usa el minimizador de casos de prueba para eliminarlos.
* Un contrato solo se reingresa cuando hay otra transacción en la lista después
  de la transacción que se supone que realiza el reingreso (es decir, hay otra
  entrada siguiente en la cola).
* Incluso si el marcador `reenter` está establecido en algo, esto no significa
  necesariamente que se reingrese al contrato, solo que el código del harness
  intentará hacerlo si es posible. Por ejemplo, si el contrato no realiza una
  llamada, el marcador `reenter` se ignora porque no hay posibilidad de
  reingresar. Normalmente, el minimizador eliminará cualquier marcador
  `reenter` espurio.

En general, muchos de estos problemas desaparecen cuando usas el minimizador de
casos de prueba, por lo que siempre es una buena idea usarlo antes de analizar
los casos de prueba generados.

## Falsos positivos conocidos

Hemos observado varios tipos de falsos positivos que parecen repetirse al hacer
fuzzing de contratos con EF/CF.

* Contratos que pagan Ether por diseño. El oráculo de bugs de ganancias de
  Ether de EF/CF detectará estos contratos como vulnerables, aunque operan
  según lo diseñado:
    * Contratos de apuestas: muchos contratos de apuestas incluyen alguna forma
      de aleatoriedad, lo que ya es una mala práctica en Ethereum. Sin embargo,
      algunos contratos de apuestas están implementados de forma que te obligan
      a adivinar, por ejemplo, los dos últimos dígitos del hash del siguiente
      bloque o algo similar. Esto se puede habilitar mediante un esquema de
      compromiso, es decir, la primera transacción compromete al usuario a un
      cierto valor y la segunda activa la suposición y el pago si se gana.
      Estos contratos normalmente no son explotables en una blockchain real.
      Sin embargo, en la blockchain simulada de EF/CF, el fuzzer puede adaptar
      el compromiso después de observar el valor en la segunda transacción.
      Este hecho es importante para que EF/CF alcance una mejor cobertura de
      código. Pero también facilita que EF/CF identifique una secuencia de TX
      que permita al fuzzer ganar de forma determinista en el contrato de
      apuestas.
    * Contratos que pagan intereses: hay muchos contratos pequeños que te
      permiten invertir Ether y luego pagan un cierto porcentaje de intereses
      cada `N` bloques. El atacante simulado de EF/CF es capaz de esperar `N`
      bloques y luego recibe el pago de intereses, lo que nuevamente es
      detectado por el oráculo de bugs de ganancias de Ether.
    * Airdrops: algunos contratos de tokens permiten airdrops, es decir,
      reparten tokens a cualquiera que los solicite hasta que se alcanzan
      ciertos límites. Por ejemplo, los airdrops suelen estar habilitados solo
      durante un breve período de tiempo. Si un contrato de este tipo se
      despliega dentro de EF/CF, es muy probable que el límite de tiempo esté
      configurado de modo que los airdrops sigan habilitados. EF/CF entonces
      detecta una ganancia de Ether si los tokens del airdrop pueden venderse
      nuevamente.
* Reporte temprano de `DELEGATECALL` controlable: actualmente reportamos un
  delegatecall controlable en cuanto se invoca. Sin embargo, hay múltiples
  contratos que incluyen funciones que permiten intencionalmente al llamador
  realizar un delegatecall a una dirección arbitraria. No obstante, estas
  funciones revierten incondicionalmente la transacción inmediatamente después
  del delegatecall. Esto evita que cualquier cambio de estado o transferencia
  de Ether persista. Normalmente, estas funciones tienen palabras como
  "simulate" en sus nombres y son fáciles de detectar.
    * Esto podría corregirse en EF/CF retrasando el reporte hasta el final de
      la ejecución. Sin embargo, esto complica bastante el oráculo de bugs.
    * Actualmente no hay planes para corregirlo.
* Initializer invocable: hemos observado que al hacer fuzzing de contratos
  exportados desde la blockchain, EF/CF a veces puede llamar a las funciones
  inicializadoras, aunque el contrato ya haya sido inicializado.
  Normalmente, esto debería provocar una reversión, pero no lo hace en el
  entorno EVM de EF/CF. Llamar al inicializador nuevamente a menudo conduce a
  ganancias triviales de Ether, porque, por ejemplo, el inicializador establece
  una variable de *owner* o algo similar.
    * Aún no estamos seguros de cuál es la causa raíz de este problema. Sin
      embargo, normalmente es fácil de detectar, ya que la función
      inicializadora suele llamarse `initializer`, `init` o algo similar.

## Errores comunes

Hicimos nuestro mejor esfuerzo para que esto sea algo usable, pero sigue siendo
un prototipo de investigación. Espera que algunas cosas fallen. Estos son
algunos problemas comunes que hemos observado:

* *P: Recibo un error de compilación extraño debido a una macro `TOKENPASTE`.*
  R: Esto sucede a menudo cuando `efcfuzz` adivina el nombre incorrecto del
  contrato (es decir, adivina un contrato abstracto); intenta pasar
  `--name YourContract` para especificar el contrato objetivo.
Descargar herramienta