
Fuzzer basado en cobertura para aplicaciones Python
pip3 install frelatage
El fuzzer de Python que el mundo merece
Instalación | Cómo funciona | Características | Usar Frelatage | Configuración
Frelatage es una librería de fuzzing para Python basada en cobertura que puede ser usada para fuzzear código Python. El desarrollo de Frelatage se inspiró en varios otros fuzzers, incluyendo AFL/AFL++, Atheris y PythonFuzz. El propósito principal del proyecto es aprovechar las mejores características de estos fuzzers y reunirlas en una nueva herramienta para fuzzear aplicaciones Python de manera eficiente.
AVISO: Este proyecto está en fase alfa y aún puede causar muchos comportamientos inesperados. Frelatage no debe ser utilizado en un entorno de producción en este momento.
pip3 install frelatage
Recomendado para desarrolladores. Clona automáticamente la rama principal del repositorio de frelatage e instala desde el código fuente.
# Clona automáticamente el repositorio de Frelatage e instala Frelatage desde el código fuente
bash <(wget -q https://raw.githubusercontent.com/Rog3rSm1th/Frelatage/main/scripts/autoinstall.sh -O -)
La idea detrás del diseño de Frelatage es el uso de un algoritmo genético para generar mutaciones que cubran la mayor cantidad de código posible. El funcionamiento de un ciclo de fuzzing se puede resumir aproximadamente con este diagrama:
graph TB
m1(Mutación 1) --> |input| function(Función fuzzeada)
m2(Mutación 2) --> |input| function(Función fuzzeada)
mplus(Mutación ...) --> |input| function(Función fuzzeada)
mn(Mutación n) --> |input| function(Función fuzzeada)
function --> generate_reports(Generar reportes)
generate_reports --> rank_reports(Clasificar reportes)
rank_reports --> select(Seleccionar n mejores reportes)
select --> |mutate| nm1(Mutación 1) & nm2(Mutación 2) & nmplus(Mutación ...) & nmn(Mutación n)
subgraph Mutaciones del ciclo
direction LR
m1
m2
mplus
mn
end
subgraph Mutaciones del siguiente ciclo
direction LR
nm1
nm2
nmplus
nmn
end
style function fill:#5388e8,stroke:white,stroke-width:4px
Frelatage permite fuzzear una función pasando un archivo como entrada.
import frelatage
import my_vulnerable_library
def MyFunctionFuzz(data):
my_vulnerable_library.parse(data)
input = frelatage.Input(value="initial_value")
f = frelatage.Fuzzer(MyFunctionFuzz, [[input]])
f.fuzz()
Frelatage te ofrece la posibilidad de fuzzear parámetros de entrada de tipo archivo. Para inicializar el valor de estos archivos, debes crear archivos en la carpeta de entrada (./in por defecto).
Si queremos inicializar el valor de un archivo usado para fuzzear, podemos hacerlo así:
echo "initial value" > ./in/input.txt
Y luego ejecutar el fuzzer:
import frelatage
import my_vulnerable_library
def MyFunctionFuzz(data):
my_vulnerable_library.load_file(data)
input = frelatage.Input(file=True, value="input.txt")
f = frelatage.Fuzzer(MyFunctionFuzz, [[input]])
f.fuzz()
import frelatage
import my_vulnerable_library
input = frelatage.Input(file=True, value="input.txt")
@frelatage.instrument([[input]])
def MyFunctionFuzz_1(data):
my_vulnerable_library.load_file(data)
@frelatage.instrument([[input]])
def MyFunctionFuzz_1(data):
my_vulnerable_library.load_file_but_different(data)
# Y así sucesivamente
# Fuzzeará los métodos instrumentados uno tras otro
frelatage.Fuzzer.fuzz_all()
Si necesitas cargar varios archivos en un corpus de una sola vez (útil si usas un corpus grande), puedes usar la función incorporada de Frelatage load_corpus. Esta función devuelve una lista de entradas.
load_corpus(directory: str, file_extensions: list) -> list[Input]
./, ./images["jpeg", "gif"], ["pdf"]import frelatage
import my_vulnerable_library
def MyFunctionFuzz(data):
my_vulnerable_library.load_file(data)
my_vulnerable_library.load_file(data2)
# Carga todos los archivos en el directorio ./in
corpus_1 = frelatage.load_corpus(directory="./")
# Carga todos los archivos .gif/.jpeg en el subdirectorio ./in/images
corpus_2 = frelatage.load_corpus(directory="./images", file_extension=["gif", "jpeg"])
f = frelatage.Fuzzer(MyFunctionFuzz, [corpus_1, corpus_2])
f.fuzz()
Puedes copiar uno o más diccionarios ubicados aquí en el directorio dedicado a los diccionarios (./dict por defecto).
El fuzzing diferencial es una técnica popular de prueba de software que intenta detectar errores proporcionando la misma entrada a múltiples librerías/programas y observando diferencias en sus comportamientos. Encontrarás un ejemplo aquí de un uso de fuzzing diferencial con Frelatage con las librerías json y ujson.
Puedes encontrar más ejemplos de fuzzers y corpus en el directorio de ejemplos.
Cada reporte de caída se guarda en la carpeta de salida (./out por defecto), en una carpeta llamada: id:<crash ID>,err:<error type>,err_pos:<error>,err_file:<error file>.
El directorio del reporte tiene la siguiente forma:
├── out
│ ├── id:<crash ID>,err:<error type>,err_file:<error file>,err_pos:<err_pos>
│ ├── message
│ ├── input
│ ├── 0
│ ├── <inputfile1>
│ ├── ...
│ ├── ...
Cada reporte de caída contiene un archivo message que contiene el mensaje de error.
Cada reporte de aumento de cobertura se guarda en la carpeta de cobertura (./cov por defecto), en una carpeta llamada: coverage_<ID>, y el directorio del reporte tiene la misma forma que los directorios de reportes de caída.
Las entradas pasadas a una función se serializan usando el módulo pickle antes de guardarse en el archivo <report_folder>/input. Por lo tanto, es necesario deserializarlo para poder leer el contenido del archivo. Esta acción se puede realizar con el comando frelatage-report.
$ frelatage-report input
Hay dos formas de configurar Frelatage:
Un ejemplo de configuración:
export FRELATAGE_DICTIONARY_ENABLE=1 &&
export FRELATAGE_SAVE_NEW_COVERAGE=1 &&
export FRELATAGE_TIMEOUT_DELAY=2 &&
export FRELATAGE_INPUT_FILE_TMP_DIR="/tmp/frelatage" &&
export FRELATAGE_INPUT_MAX_LEN=4096 &&
export FRELATAGE_MAX_THREADS=8 &&
export FRELATAGE_MAX_STAGES=1000000 &&
export FRELATAGE_MAX_CYCLES_WITHOUT_NEW_PATHS=5000 &&
export FRELATAGE_INPUT_DIR="./in" &&
export FRELATAGE_DICTIONARY_DIR="./dict" &&
python3 fuzzer.py
import frelatage
def myfunction(input1_string, input2_int):
pass
input1 = frelatage.Input(value="initial_value")
input2 = frelatage.Input(value=2)
f = frelatage.Fuzzer(
# El método que quieres fuzzear
method=myfunction,
# Corpus
corpus=[[input1], [input2]],
# Número de hilos
threads_count=8,
# Excepciones que serán tomadas en cuenta
exceptions_whitelist=(OSError),
# Excepciones que no serán tomadas en cuenta
exceptions_blacklist=(),
# Directorio donde se almacenarán los reportes de error
output_directory="./out",
# Directorio donde se almacenarán los reportes de aumento de cobertura
coverage_directory="./cov",
# Habilitar o deshabilitar el modo silencioso
silent=False,
# Habilitar o deshabilitar el fuzzing infinito
infinite_fuzz=False
)
f.fuzz()
Por favor, ten en cuenta que, de manera similar a muchas otras tareas computacionalmente intensivas, el fuzzing puede ejercer presión sobre tu hardware y el sistema operativo. En particular:
Tu CPU se calentará y necesitará refrigeración adecuada. En la mayoría de los casos, si la refrigeración es insuficiente o deja de funcionar correctamente, las velocidades de la CPU se reducirán automáticamente. Dicho esto, especialmente al fuzzear en hardware menos adecuado (portátiles, smartphones, etc.), no es del todo imposible que algo explote.
Los programas objetivo pueden terminar consumiendo erráticamente gigabytes de memoria o llenando el espacio en disco con archivos basura. Frelatage intenta imponer límites básicos de memoria, pero no puede prevenir todos los posibles contratiempos. En resumen, no deberías hacer fuzzing en sistemas donde la posibilidad de pérdida de datos no sea un riesgo aceptable.
El fuzzing implica miles de millones de lecturas y escrituras en el sistema de archivos. En sistemas modernos, esto generalmente estará fuertemente cacheado, resultando en una E/S "física" bastante modesta, pero hay muchos factores que pueden alterar esta ecuación. Es tu responsabilidad monitorear posibles problemas; con E/S muy pesada, la vida útil de muchos HDD y SSD puede reducirse.
Una buena forma de monitorear la E/S del disco en Linux es el comando 'iostat':
$ iostat -d 3 -x -k [...optional disk ID...]
Soy Rog3rSm1th, tengo 21 años y soy un entusiasta francés de la informática y la ciberseguridad. Me gusta desarrollar herramientas (OSINT, Fuzzing...) y jugar CTFs/Wargames. Para saber más sobre mí y mis proyectos, simplemente haz clic aquí.
➜ Si quieres contratarme para uno de tus proyectos (Programación, ciberseguridad...), solo contáctame en [email protected] y evaluaremos tus necesidades juntos.
Para cualquier comentario, sugerencia, reporte de error, o si encontraste un error usando Frelatage, puedes contactarme en [email protected] o en Twitter @Rog3rSm1th
| Variable de Entorno | Descripción | Valores Posibles | Valor por Defecto |
|---|
| FRELATAGE_DICTIONARY_ENABLE | Habilitar el uso de mutaciones basadas en elementos del diccionario | 1 para habilitar, 0 en caso contrario | 1 |
| FRELATAGE_SAVE_NEW_COVERAGE | Guardar nuevas entradas de cobertura para reutilizarlas después | 1 para habilitar, 0 en caso contrario | 1 |
| FRELATAGE_TIMEOUT_DELAY | Retardo en segundos después del cual una función devolverá un TimeoutError | 1 - infinity | 2 |
| FRELATAGE_INPUT_FILE_TMP_DIR | Carpeta temporal donde se almacenan los archivos de entrada | ruta absoluta a una carpeta, p. ej. /tmp/custom_dir | /tmp/frelatage |
| FRELATAGE_INPUT_MAX_LEN | Tamaño máximo de una variable de entrada en bytes | 4 - infinity | 4094 |
| FRELATAGE_MAX_THREADS | Número máximo de hilos simultáneos | 8 - infinity | 8 |
| FRELATAGE_MAX_STAGES | Número máximo de etapas para una función fuzzeada | 1 - infinity | 1000000 |
| FRELATAGE_MAX_CYCLES_WITHOUT_NEW_PATHS | Número de ciclos sin nuevas rutas encontradas después del cual pasamos a la siguiente etapa | 10 - infinity | 5000 |
| FRELATAGE_INPUT_DIR | Directorio que contiene los archivos de entrada iniciales. Debe ser una ruta relativa (a la ruta del archivo de fuzzing) | ruta relativa a una carpeta, p. ej. ./in | ./in |
| FRELATAGE_DICTIONARY_DIR | Directorio por defecto para los diccionarios. Debe ser una ruta relativa (a la ruta del archivo de fuzzing) | ruta relativa a una carpeta, p. ej. ./dict | ./dict |
| FRELATAGE_DEBUG_MODE | Habilitar el modo de depuración (mostrar el error cuando Frelatage falla) | 1 para habilitar, 0 en caso contrario | 1 |