Suture es una herramienta de análisis de Taint de puntos a alto y de alto orden (para C) basada en LLVM IR y basada en el Dr. Checker (¡muchas gracias a sus creadores!), Dos aspectos destacados son:
Lo nombramos sutura con la esperanza de que pueda ser quirúrgicamente preciso, mientras que puede coser múltiples syscalls/funciones de entrada para construir flujos de datos de alto orden. Para obtener más detalles, consulte nuestro documento: descubrir vulnerabilidades de estilo de mancha de alto orden en los núcleos del sistema operativo en ACM CCS'21 .
Primer clon el repositorio:
~$ git clone https://github.com/seclab-ucr/SUTURE.git suture
Luego configure el entorno LLVM para sutura:
~$ cd suture
~/suture$ python setup_suture.py -o ../suture_deps
Opciones (para setup_suture.py ):
-b especifica el nombre de la rama de LLVM que se configurará para Suture, es predeterminado a "Release_90" (es decir, LLVM 9.0) en este repositorio de espejo LLVM.-o especifica la carpeta para alojar todas las cosas requeridas por Suture (por ejemplo, LLVM), use cualquier carpeta que prefiera. Dependiendo de su hardware, la configuración de LLVM puede llevar bastante tiempo. Después de que termine, se generará un archivo SRCIPT llamado env.sh en la carpeta Root de sutura, contiene comandos para establecer las variables de entorno utilizadas por Suture.
Importante : Asegúrese de activar este env.sh
~/suture$ source env.sh
A continuación, vamos a construir sutura:
~/suture$ cd llvm_analysis
~/suture/llvm_analysis$ ./build.sh
En una construcción exitosa, Suture está lista para usar.
La sutura se puede utilizar para descubrir vulnerabilidades de mancha de alto orden fuera de la caja, en esta sección caminamos a través de este proceso con un ejemplo (por ejemplo, el ejemplo motivador como se muestra en la Sección 2.1 en nuestro documento).
Para descubrir vulnerabilidades, la sutura requiere dos tipos de entrada: (1) un módulo de programa compilado a LLVM BitCode (por ejemplo, un archivo .bc ) y (2) un archivo de configuración para el módulo que manifiesta sus funciones de entrada y argumentos controlados por el usuario.
Primero preparemos el código de bits LLVM para nuestro ejemplo motivador:
~/suture$ cd benchmark
~/suture/benchmark$ ./gen.sh motivating_example
Nota : Por conveniencia, proporcionamos gen.sh para compilar A .C a .BC y .LL (Código de bits LLVM legible humano), con nivel de optimización -O1 .
Ahora deberíamos tener el motivating_example.bc en la misma carpeta de referencia , ese es el módulo de programa de entrada para Suture.
Luego se produce el archivo de configuración, ya hemos preparado uno para el ejemplo motivador:
~/suture/benchmark$ cat conf_motivating_example
entry0 MY_IOCTL 1
entry1 NULL_ARG
entry2 NULL_ARG
Explicación : Cada línea en el archivo de configuración describe la información de una función de entrada (por ejemplo, la función de nivel superior sin personas que llaman y generalmente sirve como interfaz externa) en el módulo de programa, contiene hasta 3 tokens separados por el espacio:
entry_x MY_IOCTL 0_2 .Entonces, como se muestra arriba, conf_motivating_example especifica que hay tres funciones de entrada en motivating_example.c : Entry0 (el parámetro 1 está controlado por el usuario), Entry1 y Entry2 (sin parámetros controlados por el usuario). Esto coincide con el código del ejemplo motivador.
Una vez que el código de bits del programa y el archivo de configuración de entrada están listos, podemos ejecutar Suture para descubrir vulnerabilidades de contaminación:
~/suture$ ./run_nohup.sh benchmark/motivating_example.bc benchmark/conf_motivating_example
Explicación : Run_nohup.sh es un script simple que invoca los pases de análisis LLVM compilados de sutura:
~/suture$ ./run_nohup.sh [path/to/program.bc] [path/to/entry_func_config]
Una vez iniciado, dependiendo del hardware real y la complejidad del programa objetivo, el tiempo requerido para que Suture termine el análisis y el descubrimiento de vulnerabilidad varíe mucho. Sin embargo, un programa simple como nuestro ejemplo motivador generalmente termina al instante.
Decida si el análisis finaliza : durante la ejecución, Suture mantiene iniciando sesión en un archivo en el mismo directorio del archivo de configuración de entrada , suponga que la ruta del archivo de configuración es /rath/to/conf_program , el archivo de registro será /path/to/conf_program.log . Podemos decidir si el análisis termina monitoreando el registro:
~/suture$ grep "Bug Detection Phase finished" benchmark/conf_motivating_example.log
El archivo de registro antes mencionado también es la salida de Suture, Suture incrustará sus vulnerabilidades potenciales descubiertas en el archivo de registro, que puede extraerse y organizarse en un informe de advertencia final después de que termine el análisis:
~/suture$ ./ext_warns.sh benchmark/conf_motivating_example.log
Explicación : Ext_warns.sh extraerá todas las advertencias (en JSON) incrustadas en el archivo de registro dado, reorganizarlas e imprimirlas bastante en los informes finales de advertencia. Los informes de advertencia se colocarán en una carpeta en la misma ruta del archivo de registro, suponga que el archivo de registro es /path/to/conf_program.log , la carpeta de informe de advertencia será /rath/to/warns-conf_program-yyyyy-mm-dd .
~/suture$ ls benchmark/warns-conf_motivating_example-2021-11-10/
all int_overflow taint_data_use taint_loopbound taint_ptr_def
En la carpeta hay 5 informes de advertencia, todos contienen todos los tipos de advertencias agrupadas de acuerdo con su relación de flujo de datos, mientras que otros informes solo contienen tipos específicos de advertencias (por ejemplo, desbordamiento de enteros, dreferencia de puntero contaminado, etc.), se pueden encontrar más detalles sobre cómo las advertencias de agrupación se pueden encontrar en nuestro documento (Sección 4). Por lo general, solo usamos el informe unificado todo .
~/suture$ cat -n benchmark/warns-conf_motivating_example-2021-11-10/all
1 =========================GROUP 0 (2 - 2)=========================
2 #####Warned Insts#####
3 (u'motivating_example.c', 30, [u'IntegerOverflowDetector'])
4 ######################
5
6 ++++++++++++++++WARN 0 (2 - 2)++++++++++++++++
7 IntegerOverflowDetector says: motivating_example.c@30 (bar : %add = add i8 %0, -16, !dbg !31)
8 ********Trace 0(2)********
9 #####CTX##### entry0
10 entry0 (motivating_example.c@18)
11 #####INSTS#####
12 >>>>>>>>>>>>>>>>>>tag: 0x55b206570420 tf: 0x55b20695b1b0 (2)>>>>>>>>>>>>>>>>>>
13 motivating_example.c@18 ( %cond = icmp eq i32 %cmd, 0, !dbg !31)
14 motivating_example.c@21 ( store i8 %user_input, i8* getelementptr inbounds (%struct.data, %struct.data* @d, i64 0, i32 1, i64 0), align 4, !dbg !32, !tbaa !34)
15 #####CTX##### entry1 -> bar
16 entry1 (motivating_example.c@35)
17 ----> (motivating_example.c@35 : %call = tail call i32 @bar(i8* bitcast (%struct.data* @d to i8*)), !dbg !27)
18 bar (motivating_example.c@30)
19 #####INSTS#####
20 >>>>>>>>>>>>>>>>>>tag: 0x55b2065e7050 tf: 0x55b2068174f0 (1)>>>>>>>>>>>>>>>>>>
21 motivating_example.c@30 ( %0 = load i8, i8* %add.ptr, align 1, !dbg !31, !tbaa !32)
22 motivating_example.c@30 ( %add = add i8 %0, -16, !dbg !31)
23
Explicación : En un alto nivel, el informe de advertencia contiene algunos grupos de advertencia, cada grupo contiene varias advertencias , y cada advertencia contiene varias trazas de mancha que se originan en una entrada de usuario y se hunden a una misma declaración de programa sensible. En otras palabras, se plantea una advertencia para una determinada declaración del programa y de un tipo específico (por ejemplo, desbordamiento de enteros), mientras que un grupo contiene múltiples advertencias estrechamente relacionadas desde la perspectiva del flujo de datos (ver Sección 4 en nuestro documento), por lo tanto, un grupo puede tener múltiples declaraciones de programas advertidos e incluir diferentes tipos de advertencia.
Tome el informe de advertencia anterior como ejemplo:
===GROUP No. (min_order - max_order)=== , donde min_order es el orden mínimo (por ejemplo, en pocas palabras, el orden son los tiempos de las invocaciones de la función de entrada requerida en la max_order de Taint, ver la Sección 3.2 en nuestro documento para una definición más formal).WARN No. es local para su grupo.***Trace No. (order)*** , un rastro de mancha siempre tiene un orden único que se puede calcular (por ejemplo, no un rango).El informe de advertencia identifica una vulnerabilidad válida de desbordamiento entero en el ejemplo motivador, mientras evita posibles falsas alarmas que puede generar una herramienta de análisis estático menos precisa, se pueden encontrar detalles en la Sección 2.1 de nuestro documento.
Este repositorio también contiene algunas otras herramientas/scripts que puede encontrar útiles.
~/suture$ python llvm_analysis/AnalysisHelpers/EntryPointIdentifier/entry_point_identifier.py /path/to/prog_module.bc
~/suture$ cat /path/to/prog_module.bc.all_entries
Explicación : Este script puede identificar algunas funciones de entrada comunes en un módulo de controlador de dispositivo Linux (por ejemplo, ioctl () , read () , write () , etc.), que ayuda a la construcción del archivo de configuración de entrada como la entrada a sutura. Esta utilidad es implementada principalmente por el Dr. Checker original basado en algunos conocimientos de dominio del núcleo (por ejemplo, estructuras de datos específicas como File_Operations utilizadas para definir los puntos de entrada del controlador), luego hicimos algunas mejoras (por ejemplo, hacer que las heurísticas sean más sólidas).
~/suture$ python flt_warns.py /path/to/warn_report [Regex] > /path/to/filtered_warn_report
Explicación : Según nuestra experiencia, muchas falsas alarmas en el informe de advertencia a menudo comparten un mismo problema de subtintería problemática (consulte la Sección 6.3 en nuestro documento). Mientras el revisor de advertencia inspeccione una falsa alarma y reconozca la sub-traza inductora de FP, naturalmente, puede intentar excluir automáticamente todas las otras falsas alarmas similares que contienen la misma sub-s-traza, reduciendo la tasa de falsa alarma percibida por el revisor. Para este propósito, proporcionamos este simple flt_warns.py que toma el informe de advertencia original y una expresión regular de Python como entradas, luego coincidirá con el RE con cada traza de contaminación en el informe, una vez coincidente, la traza de contaminación se tratará como una falsa alarma. El script generará un nuevo informe de advertencia filtrado que excluye todas las trazas de mancha coincidentes.
Es posible que esté interesado en usar Suture como un análisis estático de uso general (por ejemplo, obtener el conjunto de puntos a/mancha de una variable en una determinada ubicación del programa), esto es seguro factible, pero a diferencia del descubrimiento de vulnerabilidad, debe ensuciarse las manos y sumergirse en el código de sutura para familiarizarse con las estructuras de datos principales que utiliza y algunas financiaciones importantes. Espero que los siguientes consejos puedan ayudar:
LLVM evoluciona muy rápidamente, sin garantía de compatiabilidad hacia atrás. Es muy probable que encuentre algunos errores de compilación al intentar construir sutura con una nueva versión LLVM (por ejemplo,> 9.0). Pero afortunadamente, tales errores de compilación generalmente son fáciles de resolver (por ejemplo, a menudo el caso de que solo necesitamos reemplazar las API LLVM desactualizadas con las más nuevas). Básicamente, para adaptar la sutura a una versión LLVM más nueva, primero necesitamos configurar un LLVM más nuevo en 0x0 (por ejemplo, debemos modificar la configuración_suture.py para clonar LLVM Repo desde una fuente activa y especificar el nombre de la rama más nuevo), y luego, y luego intentar construir una sutura contra él, solucionando los errores de compilación cubiertos.