Parte: 5 — Explotación de sistemas y binarios · Fuente: Andriesse, Practical Binary Analysis · docs AFL++/LLVM ⏱️ Duración estimada: 140 min · Nivel: Avanzado
Descubrir vulnerabilidades automáticamente mediante fuzzing: alimentar a un programa con miles de entradas mutadas guiadas por cobertura hasta provocar crashes. Aprenderás a compilar objetivos con instrumentación, a lanzar campañas con AFL++ y libFuzzer, a combinar con sanitizers (ASan/UBSan) y a triar y minimizar los crashes encontrados.
⚠️ Ética: fuzzea software propio, open source con permiso o dentro de programas autorizados. Reporta hallazgos de forma responsable.
Al finalizar, el alumno podrá:
afl-cc/-fsanitize=fuzzer.LLVMFuzzerTestOneInput).| # | Tema | Por qué importa |
|---|---|---|
| 1 | Fuzzing guiado por cobertura | Encuentra rutas profundas |
| 2 | Instrumentación (AFL/SanCov) | Retroalimenta al mutador |
| 3 | Corpus semilla | Buen punto de partida |
| 4 | Diccionarios | Tokens del formato objetivo |
| 5 | Sanitizers (ASan/UBSan) | Detectan bugs silenciosos |
| 6 | libFuzzer y harness | Fuzzing in-process rápido |
| 7 | Triage y dedup de crashes | Del ruido al bug real |
| 8 | Minimización (tmin/casr) | Caso mínimo reproducible |
LLVMFuzzerTestOneInput. Clave: muy rápido para
librerías/funciones.afl-tmin,
ASan backtrace, casr para dedup.# AFL++
sudo apt install -y afl++ # o compilar desde github.com/AFLplusplus/AFLplusplus
# libFuzzer viene con clang
clang --version
Entorno propio.
parser.c (tiene un overflow deliberado):c
#include <stdio.h>
#include <string.h>
void parse(const char *s){ char b[16]; if(s[0]=='F' && s[1]=='U') strcpy(b, s); }
int main(int c, char**v){ char buf[256]; FILE*f=fopen(v[1],"rb"); int n=fread(buf,1,255,f); buf[n]=0; parse(buf); }
bash
afl-cc -fsanitize=address -o parser_afl parser.c
mkdir in && printf 'FUabc' > in/seed
afl-fuzz -i in -o out -- ./parser_afl @@
Observa el panel de AFL++: paths, crashes únicos, ejecuciones/seg. En minutos deberían aparecer
crashes en out/default/crashes/.
Reproduce y tría un crash con ASan para ver el stack-buffer-overflow:
bash
./parser_afl out/default/crashes/id:000000* # ASan imprime el backtrace
Minimiza el caso: afl-tmin -i <crash> -o crash_min -- ./parser_afl @@.
Escribe un harness libFuzzer para la función parse:
c
extern void parse(const char*);
int LLVMFuzzerTestOneInput(const unsigned char *d, unsigned long n){
char *s = malloc(n+1); memcpy(s,d,n); s[n]=0; parse(s); free(s); return 0; }
bash
clang -g -fsanitize=fuzzer,address harness.c parser.c -o fz && ./fz -runs=100000
"FU" y compara la velocidad de descubrimiento.afl-tmin y explica qué eliminó.Encuentra y reproduce un crash en un objetivo instrumentado, minimiza el caso y explica la causa raíz con el backtrace de ASan.
Criterio de aceptación: entregas un input mínimo que provoca el crash de forma determinista y el
reporte de ASan que identifica el tipo de bug (p. ej. stack-buffer-overflow).
| Síntoma / mensaje | Causa y cómo arreglar |
|---|---|
| 0 crashes tras horas | Corpus/diccionario pobres; el fuzzer no supera un check |
| "suboptimal, core dumps" | Ajusta core_pattern como indica AFL al arrancar |
| Crashes no reproducibles | Falta ASan o hay no-determinismo (tiempo/aleatorio) |
| Muy pocas ejec/seg | Objetivo lento; usa modo persistente/in-process |
| Todos los crashes iguales | Falta dedup; usa triage por backtrace/casr |
❓ ¿AFL++ o libFuzzer? AFL++ para binarios/CLI y black-box con QEMU; libFuzzer para funciones de librería in-process. A menudo se usan ambos.
❓ ¿Necesito el código fuente? No siempre: AFL++ tiene modo QEMU/frida para binarios sin fuente, aunque más lento.
❓ ¿Y si no hay crashes? Mejora semillas y diccionario, añade sanitizers y aumenta el tiempo; también considera assertions.
Clase 135 — Ofuscación y técnicas anti-reversing