
Encontrar y ejecutar automáticamente ataques de inyección de fallos
▄▖ ▜ ▗ ▄▖ ▘ ▗ ▘ ▄▖▘ ▌
▙▖▀▌▌▌▐ ▜▘ ▐ ▛▌ ▌█▌▛▘▜▘▌▛▌▛▌ ▙▖▌▛▌▛▌█▌▛▘
▌ █▌▙▌▐▖▐▖ ▟▖▌▌ ▌▙▖▙▖▐▖▌▙▌▌▌ ▌ ▌▌▌▙▌▙▖▌
▙▌
Este repositorio es la parte software de nuestro proyecto de ataque por inyección de fallos. Para la parte hardware, consulta este repositorio.
El objetivo de este proyecto es encontrar instrucciones en la ejecución de un programa que, cuando se omiten/convierten en NOP, causan problemas de seguridad.
targets/tooling.py y una FPGA.Diagrama de flujo del software
Presentación que dimos para este proyecto
Buscamos problemas de seguridad haciendo una o más de las siguientes acciones:
Para inyectar fallos, elegimos hacer glitching tipo crowbar. Esto se logró usando una FPGA con un MOSFET N-canal SI 2302. Aquí hay un enlace a nuestras herramientas FPGA. Más información se puede encontrar en targets.
Para los disparadores, elegimos usar una entrada GPIO a una FPGA. En el código de prueba, alternamos un LED, aunque se podría realizar análisis de potencia para tus disparadores.
Las dependencias están listadas en requirements.txt. Instálalas con pip install -r requirements.txt.
uso: main.py [-h] [-s ÍNDICE] [-i MÁX_ITERACIONES] [-o SALIDA_ESPERADA] [-e SALIDA_ESPERADA] [-d PC_DESEADO] [-v] [-n] [-t TIPOS] [-b DIR_BINARIO]
[-u DIR_SALIDA] [-f DIR_INICIO] [-g DIR_FIN]
ruta_binario ruta_entrada
Encuentra automáticamente vulnerabilidades de seguridad hardware en binarios. Solo soporta ARM.
argumentos posicionales:
ruta_binario El binario a examinar
ruta_entrada La ruta a la entrada del programa
opciones:
-h, --help muestra este mensaje de ayuda y sale
-s, --simulate ÍNDICE Ejecuta una simulación Unicorn con el fallo en la enésima instrucción emitida. Ignora todas las demás banderas excepto
--max_iterations y --verbose.
-i, --max-iterations MÁX_ITERACIONES
El número máximo de instrucciones a ejecutar en el binario antes de terminar antes de tiempo
-o, --expected-output SALIDA_ESPERADA
La salida esperada del programa ante un incidente de seguridad exitoso
-e, --expected-exit SALIDA_ESPERADA
La salida esperada del programa ante un incidente de seguridad exitoso
-d, --desired-pc PC_DESEADO
El contador de programa que deseamos alcanzar si es posible. En hexadecimal o decimal. Ten en cuenta que esta es la
dirección absoluta, no relativa al binario.
-v, --verbose Verborrea: advertencia, información, depuración
-n, --no-thumb Si ejecutar o no en modo thumb
-t, --types TIPOS Qué tipos de instrucciones enfocar. 0) Fuerza bruta: cada emisión. 1) Valores predeterminados recomendados. 2) Solo
saltos condicionales. 3) Solo comparaciones/pruebas. 4) Solo retornos. 5) Solo saltos, llamadas, retornos y comparaciones
-b, --binary-addr DIR_BINARIO
La dirección a la que cargar el binario. Por defecto 0x1000000. Puede ser en hexadecimal o decimal.
-u, --output-dir DIR_SALIDA
El directorio para almacenar los fallos encontrados.
-f, --begin-addr DIR_INICIO
La dirección de inicio de las instrucciones que deben considerarse para omitir. (inclusive.) Si se establece, -g también
debe establecerse.
-g, --end-addr DIR_FIN
La dirección final de las instrucciones que deben considerarse para omitir. (inclusive.) Si se establece, -f también debe
establecerse.
python3 main.py ./binaries/sha256.bin ./inputs/sha256.bin -o ./expecteds/sha256.bin -v
Verifica la salida para ver si logramos nuestros objetivos de ataque.
python3 main.py ./binaries/aes_ecb.bin ./inputs/aes_ecb.bin -d 0x100045c -v -u outputs/aes_ecb
Prueba el binario aes_ecb para saltar a una función "inalcanzable" personalizada y almacena las entradas en un directorio.
python3 main.py ./binaries/aes_ecb.bin ./outputs/aes_ecb/solved_pc_188.bin -s 188
Ejecuta la simulación Unicorn para este ciclo de glitch y entrada específicos. En este caso, la salida del control del contador de programa.
El código incluido en binaries/sources es solo para pruebas. No apunta a ningún hardware real y es estrictamente para probar la herramienta.
Para ejecutar un binario específico dirigido a un dispositivo, necesitas extraer la parte relevante del binario de manera que no haga ninguna llamada a periféricos fuera de E/S simple. Por ejemplo, si tu binario usa UART, puedes parchear el binario reemplazando las llamadas a UART con llamadas a los stubs _read y _write que se encuentran en binaries/stubs. Además, GPIO puede reemplazarse con llamadas a _trigger si se desea. Para ejecutar tu código a través de la herramienta, crearás un símbolo main que contenga tu binario parcheado y luego lo enlazarás con el código binaries/startup.s. De esta manera, la herramienta podrá iniciar y ejecutar tu binario.
Para un ejemplo específico, consulta el directorio targets donde mostramos este proceso en el TIMSPM0L2228.
Debes tener la misma versión del compilador y las mismas banderas/pasos de compilación para crear un binario que refleje el binario que se ejecuta en el objetivo. Si estás creando tus propios programas y probándolos, esto está bien. Pero si solo tienes el código fuente del objetivo que estás atacando, es poco probable que puedas compilar hasta el binario exacto que se está ejecutando. Por lo tanto, se recomienda usar el binario exacto que se ejecuta en tu objetivo siempre que sea posible.