
AFL/QEMU fuzzing con emulación de sistema completo.
Nuevo: Para aquellos que quieran jugar con TriforceAFL y TLSF, Richard Johnson creó un Dockerfile que instala ambos (e incluso compila un kernel de Linux por ti). Está disponible aquí https://hub.docker.com/r/moflow/afl-triforce/tags/.
También nuevo: ¡afl-tmin ahora funciona con el forkserver!
https://github.com/nccgroup/TriforceAFL Jesse Hertz [email protected] Tim Newsham [email protected]
Esta es una versión parcheada de AFL que soporta fuzzing de sistema completo usando QEMU. El QEMU incluido ha sido actualizado para permitir el trazado de ramas cuando se ejecuta un emulador de sistema para x86_64. Se han añadido instrucciones adicionales para iniciar el forkserver de AFL, configurar los ajustes de fuzzing, y marcar el inicio y el fin de los casos de prueba.
Nota: no todas las herramientas de AFL han sido probadas con los nuevos cambios. Estas herramientas han recibido algunas pruebas:
Para compilar:
make
Para obtener un mapa de cobertura:
echo hello > /tmp/hello
./afl-showmap -o coverage.txt -QQ --
./afl-qemu-system-trace -kernel ../bzImage
-initrd ../initramfs.cpio.gz -m 1G -nographic
-append "console=ttyS0" -aflFile /tmp/hello
cat coverage.txt
Para hacer fuzzing:
egrep ' (panic|log_store)$' ../mykern/kallsyms ffffffff8108e570 t log_store ffffffff8181064b T panic
mkdir inputs
echo hello > inputs/hello
./afl-fuzz -i inputs -o outputs -QQ --
afl-qemu-system-trace -kernel bzImage -initrd root.cpio.gz
-m 1G -nographic -append "console=ttyS0"
-aflPanicAddr ffffffff8181064b -aflDmesgAddr ffffffff8108e570
-aflFile @@
(Nota: a diferencia de cuando se usa la opción "-Q", debes especificar la línea de comandos completa para afl-qemu-system-trace cuando uses la opción "-QQ").
Para más detalles sobre cómo usar esta versión modificada de AFL, consulta nuestro fuzzer de syscalls de Linux en https://github.com/nccgroup/TriforceLinuxSyscallFuzzer.
Nuevas opciones de AFL: -QQ - usa qemu en emulación de sistema completo en lugar de modo usuario (-Q)
Nuevas opciones de QEMU: -aflFile - El nombre del archivo que contiene las entradas del fuzzer -aflPanicAddr - Una dirección de kernel panic para la detección de pánicos -aflDmesgAddr - Dirección en el kernel de Linux de la función de registro dmesg para detectar el registro e interceptar los mensajes de log
Nuevas instrucciones de QEMU: 0f 24 - aflCall edi=1 startForkserver(esi=enableTicks) Inicia el fork server de AFL. Después de este punto, cada prueba se ejecutará en un proceso hijo fork separado. Si enableTicks es distinto de cero, QEMU volverá a habilitar el temporizador de la CPU después de hacer fork de un hijo; de lo contrario, no se habilitará. edi=2 getWork(esi=ptr, edx=sz) Llena ptr[0..sz] con el siguiente caso de prueba de entrada. Devuelve el tamaño real llenado (<= sz). edi=3 startWork(esi=ptr) Indica a AFL que comience el trazado. El argumento apunta a un buffer con dos quadwords que indican las direcciones de inicio y fin del código a trazar. Las instrucciones fuera de este rango no se trazan. edi=4 doneWork(esi=exitCode) Indica a AFL que el caso de prueba ha terminado. Si se detecta un pánico, AFL detendrá el caso de prueba inmediatamente. De lo contrario, se ejecutará hasta que se llame a doneWork. El exitCode especificado se devuelve a AFL. (El código puede, pero actualmente no lo hace, aplicar un OR con el valor 64 a todos los códigos de salida si se detectaron logs de dmesg durante el caso de prueba).
Nuevo controlador de bloque de QEMU: -drive filename=privmem: Este controlador de bloque mantiene la imagen de la unidad en memoria copy-on-write para que los cambios nunca se persistan en disco. Los cambios realizados por un caso de prueba están aislados de otros casos de prueba.
Escrito y mantenido por Michal Zalewski [email protected]
Copyright 2013, 2014, 2015, 2016 Google Inc. Todos los derechos reservados. Publicado bajo los términos y condiciones de la Licencia Apache, Versión 2.0.
Para nuevas versiones e información adicional, consulta: http://lcamtuf.coredump.cx/afl/
Para comparar notas con otros usuarios o recibir notificaciones sobre nuevas funciones importantes, envía un correo a [email protected].
** Consulta QuickStartGuide.txt si no tienes tiempo de leer este archivo. **
El fuzzing es una de las estrategias más potentes y probadas para identificar problemas de seguridad en software del mundo real; es responsable de la gran mayoría de los bugs de ejecución remota de código y escalada de privilegios encontrados hasta la fecha en software crítico para la seguridad.
Desafortunadamente, el fuzzing también es relativamente superficial; las mutaciones ciegas y aleatorias hacen muy poco probable alcanzar ciertas rutas de código en el software probado, dejando algunas vulnerabilidades firmemente fuera del alcance de esta técnica.
Ha habido numerosos intentos de resolver este problema. Uno de los primeros enfoques - pionero de Tavis Ormandy - es la destilación de corpus. El método se basa en señales de cobertura para seleccionar un subconjunto de semillas interesantes de un corpus masivo y de alta calidad de archivos candidatos, y luego fuzzearlos por medios tradicionales. El enfoque funciona excepcionalmente bien, pero requiere que dicho corpus esté fácilmente disponible. Además, las mediciones de cobertura de bloques proporcionan solo una comprensión muy simplista del estado del programa, y son menos útiles para guiar el esfuerzo de fuzzing a largo plazo.
Otras investigaciones más sofisticadas se han centrado en técnicas como el análisis de flujo de programa ("ejecución concolic"), ejecución simbólica o análisis estático. Todos estos métodos son extremadamente prometedores en entornos experimentales, pero tienden a sufrir problemas de fiabilidad y rendimiento en usos prácticos - y actualmente no ofrecen una alternativa viable a las técnicas de fuzzing "tonto".
American Fuzzy Lop es un fuzzer de fuerza bruta combinado con un algoritmo genético extremadamente simple pero sólido como una roca, guiado por instrumentación. Utiliza una forma modificada de cobertura de aristas para detectar sin esfuerzo cambios sutiles a escala local en el flujo de control del programa.
Simplificando un poco, el algoritmo general se puede resumir como:
Cargar los casos de prueba iniciales proporcionados por el usuario en la cola,
Tomar el siguiente archivo de entrada de la cola,
Intentar recortar el caso de prueba al tamaño más pequeño que no altere el comportamiento medido del programa,
Mutar el archivo repetidamente usando una variedad equilibrada y bien investigada de estrategias tradicionales de fuzzing,
Si alguna de las mutaciones generadas resultó en una nueva transición de estado registrada por la instrumentación, añadir la salida mutada como una nueva entrada en la cola.
Ir al paso 2.