Desafíos de seguridad de los procesadores RISC-V: un breve análisis de las vulnerabilidades de la arquitectura y los riesgos de ataque

fondo

En el verano de 2010, nació en la Universidad de California, Berkeley, un nuevo conjunto de instrucciones de código abierto, RISC-V, que encendió una chispa para el campo de los chips.

En los siguientes diez años, el diseño arquitectónico RISC-V floreció en todos los círculos académicos con el impulso de iniciar un incendio en la pradera.

Por ejemplo, varios diseños de chips CPU y DSA basados ​​en la arquitectura RISC-V: procesadores de la serie Xuantie de Alibaba, procesadores SonicBoom/Rocket-chip de Berkeley, procesadores de la serie riscy-OOO del MIT, etc.

Se puede decir que la arquitectura RISC-V ha roto el monopolio de x86, Arm, Power y otras arquitecturas, y está alimentando la próxima generación de oportunidades de desarrollo de tecnología de la información con necesidades conceptuales más avanzadas. "Se estima que hay 10 mil millones de núcleos RISC-V en el mercado", dijo Calista Redmond, directora ejecutiva de la Fundación RISC-V, en la reciente feria Embedded World.

El árbol está quieto pero el viento no para. Cuando RISC-V eche raíces en muchos campos tecnológicos de vanguardia, como big data, 5G, Internet de las cosas, realidad virtual y computación de vanguardia, la integridad del conjunto de instrucciones y el diseño de virtualización , extensión RAS, arquitectura de seguridad, etc. Comienzan a surgir problemas en diferentes implementaciones de aplicaciones. Especialmente en el desarrollo del campo de la seguridad, cómo garantizar la seguridad de un SoC basado en la arquitectura RISC-V se ha convertido en el foco de los desarrolladores de arquitectura RISC-V.

Este artículo analiza brevemente la estructura de seguridad de los procesadores RISC-V, presenta las vulnerabilidades actuales de la estructura del sistema debido a ataques de canal lateral de caché, ataques de memoria, etc. , realiza pruebas POC integradas en SonicBoomel núcleo

Ataque de canal lateral de caché

El canal lateral es un medio muy utilizado para el robo de información. El atacante no ataca directamente los posibles fallos teóricos del algoritmo, sino que obtiene datos confidenciales de los canales físicos del sistema mediante la medición del tiempo, la medición del consumo de energía o incluso la escucha de señales acústicas y eléctricas. señales. , La gente ha estudiado los esquemas de defensa de software y hardware de los ataques de canal lateral tradicionales durante más de 20 años.

Sin embargo, con el rápido desarrollo de los procesadores superescalares multinúcleo, siguen surgiendo nuevas variantes de ataques de canal lateral dirigidos a sistemas de procesadores .

Desde 2017, varios equipos de investigación han informado de forma independiente sobre las vulnerabilidades "Spectre" y "Meltdown" y sus variantes derivadas. Cada uno de ellos explota vulnerabilidades de alto rendimiento, como la predicción de bifurcaciones y la ejecución fuera de orden en el nivel de microarquitectura del procesador . utiliza la ejecución de flujos de instrucciones transitorios para eludir los controles de seguridad de software y hardware, robar información del usuario o acceder a datos con altos privilegios en un nivel superior , ha atraído una atención generalizada por parte del mundo académico y la industria. Estos ataques demuestran las fallas en las ideas de diseño de microarquitectura de procesadores modernos, exponiendo así algunas vulnerabilidades de seguridad fundamentales.

Análisis de seguridad de ataques de canal lateral de caché basado en flujo de instrucciones transitorio

El flujo de instrucciones transitorias (Transient Instrcution) se refiere a instrucciones que se encuentran en un estado de ejecución especulativa y aún no es seguro si finalmente se pueden retirar . Por ejemplo, la tecnología de predicción de bifurcación permite al procesador seleccionar un determinado segmento de código para la ejecución especulativa antes de la resolución de la bifurcación, y las instrucciones en el estado de ejecución especulativa pueden denominarse flujo de instrucciones transitorio.

Si los resultados de resolución de bifurcación posteriores indican que la predicción fue correcta, el flujo de instrucciones transitorias puede eventualmente retirarse normalmente. Pero si se encuentra una predicción errónea, la canalización debe revertirse al estado anterior a la ejecución de estas instrucciones transitorias. En teoría, las instrucciones transitorias revertidas no deberían tener ningún impacto observable en la microarquitectura; de lo contrario, los rastros que dejan en el procesador estas instrucciones que no deberían haberse ejecutado pueden provocar una fuga de información.

Desafortunadamente, es difícil para el procesador eliminar por completo los rastros de ejecución del flujo de instrucciones transitorias . Un ejemplo típico es: si la instrucción de carga de memoria ejecutada en el flujo de instrucciones transitorias trae un dato al caché, incluso si la tubería regresa Durante la ejecución, el procesador descarta el resultado del acceso a la memoria devuelto por esta instrucción, pero el estado de la caché que se ha modificado no se puede deshacer . A partir de esto, el atacante puede inferir la dirección de acceso a la memoria del programa víctima monitoreando los cambios en el caché. Si la dirección en sí contiene información confidencial, provocará una fuga de información. El proceso anterior constituye un tipo de ataque de canal lateral de caché basado en un flujo de instrucciones transitorio.

A diferencia de los ataques de canal lateral tradicionales, los ataques de canal lateral basados ​​en flujos de instrucciones transitorios explotan fallas de seguridad comunes en el nivel de microarquitectura del hardware para robar información confidencial. Este tipo de ataque no se puede defender completamente mediante medios de software . Este tipo de ataque generalmente tiene las siguientes tres características:

  • Impacto de amplio alcance: la mayoría de los procesadores modernos tienen estructuras de caché y admiten tecnologías de alto rendimiento como superescalares fuera de servicio , por lo que los diseños arquitectónicos de los principales fabricantes se ven afectados por este ataque.
  • Alto costo de defensa: este tipo de ataque introducido actualmente por los métodos de protección tendrá un mayor impacto en el rendimiento general del sistema .
  • Dependencia del hardware: la implementación de este tipo de ataque depende del entorno de ejecución del programa y del diseño de hardware específico . Se pueden derivar diferentes planes de ataque para diferentes microarquitecturas.

Métodos de defensa contra ataques de canal lateral de caché

Los ataques "fantasma" y "fusión" han atraído una amplia atención e investigación en el mundo académico y la industria. A continuación se presenta una breve introducción a algunas de las ideas de defensa utilizadas actualmente por el mundo académico para los ataques de canal lateral de caché como referencia:

1. Prevenir la propagación de valores especulativos en el oleoducto

Un ataque de canal lateral basado en un flujo de instrucciones transitorio consiste esencialmente en que el valor ubicado en la ruta de especulación incorrecta en el procesador se utiliza para la ejecución de instrucciones posteriores y, finalmente, el atacante lo obtiene a través de medios de canal lateral.

Por lo tanto, una solución sencilla es evitar que los valores especulativos que contienen datos privados se propaguen en el oleoducto . De hecho, la solución inicial de la industria de usar instrucciones de barrera de tipo barrera también se basó en esta idea, pero simplemente usar instrucciones de barrera para bloquear todas las instrucciones posteriores causaría una gran pérdida en el rendimiento de la ejecución especulativa.

Por lo tanto, se ha convertido en una importante dirección de investigación dividir y marcar dinámicamente instrucciones inseguras en el hardware de una manera más detallada, reduciendo así el rango de instrucciones cuya ejecución está restringida y reduciendo la pérdida de rendimiento de la solución. Los trabajos representativos incluyen STT (MICRO'19), NDA (MICRO' 19), SpectreGuard (DAC'19) y métodos de diseño de otros artículos.

2. Aplazar las modificaciones del estado de la caché mediante instrucciones especulativas de acceso a la memoria.

Este tipo de solución introduce un búfer adicional en la arquitectura de caché, y las instrucciones de acceso a la memoria que aún no han escapado de la ejecución especulativa solo pueden ingresar al búfer en lugar de al caché . Si la suposición es correcta, el valor del búfer se ingresará en la caché; de lo contrario, si la suposición es incorrecta, el valor del búfer se descartará, evitando así que el estado de la caché sea modificado por el resultado del acceso a la memoria en el ruta de conjetura incorrecta. Los trabajos representativos incluyen métodos de diseño en InvisiSpec (MICRO'18) y SafeSpec (DAC'19).

3. Proporcionar aislamiento de datos entre diferentes dominios de seguridad a nivel de caché.

Esta dirección también modifica la arquitectura de la caché para evitar que los atacantes midan eficazmente la información de la caché, pero la esencia es proporcionar aislamiento de datos entre los atacantes y las partes atacadas. Las instrucciones básicas incluyen partición de caché y asignación aleatoria de direcciones de caché .

Entre ellos, la tecnología CAT (Cache Allocation Technology) de Intel también proporciona un mecanismo de partición de caché en LLC (CAT se introdujo originalmente para resolver el problema de los vecinos ruidosos de los sistemas multinúcleo), y algunas investigaciones han tomado prestada o utilizada esta estructura.

4. Identificar dinámicamente a los atacantes del canal lateral

Los atacantes del canal lateral de caché generalmente compiten con el atacante para acceder a la misma ubicación física de caché, por lo que mostrarán un patrón de acceso a la memoria específico (patrón). Si el hardware puede identificar posibles patrones de acceso a la memoria de ataques de canal lateral y dar una advertencia oportuna, entonces el El atacante puede tomar medidas para evitar la fuga de datos. Por ejemplo, el método de diseño en Cyclone (MICRO '19).

Reaparece el ataque al canal lateral de caché

A continuación se muestra un fragmento del programa de ataque Spectre-v1 (Bounds Check Bypass) como ejemplo:

if (idx < array1_sz){
        secret = array1[idx]
        dummy = array2[secret * L1_BLOCK_SZ_BYTES];
}

Este ataque tiene como objetivo instrucciones de rama condicionales (como las instrucciones bge en RISC-V). El proceso de ataque consta de las siguientes etapas:

  • Durante la fase de entrenamiento, el segmento de código anterior se ejecuta repetidamente con el índice de datos idx que no ha cruzado el límite, de modo que la dirección del salto de rama registrada en la PHT (Tabla de historial de patrones) es el segmento de código en if. Dado que el valor idx nunca excede el rango de índice de la matriz matriz1 en esta etapa, no causa problemas de seguridad.

  • En la fase de ataque, el segmento de código anterior se ejecuta con un índice de matriz idx fuera de límites. Desde la perspectiva de un escritor de software, dado que el juicio condicional if estipula que el valor idx no puede exceder el rango del índice array1, el acceso a datos dentro del segmento de código no debe ejecutarse en este momento y el programa debe ser seguro. Sin embargo, dado que el predictor de rama ya ha sido entrenado, el hardware aún ingresará de manera especulativa en la ejecución del segmento de código y accederá al secreto de datos privados en algún otro lugar del espacio de direcciones de memoria con el índice de matriz fuera de límites idx. Dentro de la ventana de ejecución especulativa, el atacante luego construye una dirección de acceso a la memoria a través del valor secreto en el flujo de instrucciones transitorias para acceder a una matriz auxiliar array2.

  • Durante la fase de recuperación del procesador, se descubre un error de predicción de rama y el procesador revierte la canalización y descarta los resultados del acceso a la memoria de las instrucciones anteriores. Sin embargo, algunos datos de la matriz auxiliar array2 se incorporaron a la caché durante la etapa de ataque y la dirección de la caché está relacionada con el secreto de datos privados . El contenido que se haya introducido en la caché no será revocado debido a la reversión de la canalización.

  • En la fase de reconstrucción de datos, el atacante solo necesita medir los accesos a los datos en diferentes posiciones en la matriz array2 en el caché , y luego puede inferir en qué parte de la matriz2 se accedió al segmento de código anterior durante la fase de ataque, y luego inferir el valor específico. de secreto, completando así el robo de datos.

Se puede ver que el proceso de ataque anterior pasa por alto la verificación de límites establecida por el software y obtiene datos privados en otra parte del espacio de direcciones de la memoria sin generar ninguna excepción del programa.

El ataque se reproduce a continuación en un procesador real. El entorno SoC utilizado es Chipyard, el núcleo del procesador es SonicBoom y la FPGA usa VCU118 o VC707. Para conocer el código fuente del ataque, consulte el código POC proporcionado por SonicBOOM https://github.com/riscv-boom/boom-attacks.

La dirección atacada almacena: !"ThisIsTheBabyBoomerTest

El atacante real obtuvo: !"ThisIsTheBabyBoomerTest

Se puede ver que el programa de ataque Spectre-v1 se reprodujo con éxito en SonicBoom.

Seguridad de los datos de la memoria

Según las últimas estadísticas de MITRE, el 40% de las vulnerabilidades en las 25 debilidades de software más peligrosas de CWE de 2022 (2022 CWE Top 25 Most Dangerous Software Weaknesses) están relacionadas con la modificación/control de la memoria. En los últimos años, el lenguaje C/C++ se ha utilizado ampliamente en la programación de sistemas modernos. Debido a las características de lenguajes como C/C++ que pueden operar el sistema de memoria, ha mejorado enormemente la velocidad de ejecución y la eficiencia del programa. pero también ha introducido muchos problemas de seguridad de la memoria. Actualmente, los compiladores y tiempos de ejecución convencionales no realizan comprobaciones de seguridad estáticas o dinámicas en los punteros de los programas C/C++. El resultado final es que los programas programados en C/C++ son muy vulnerables a los atacantes.

Según el tipo de acceso ilegal al puntero, la seguridad de la memoria se divide principalmente en dos categorías: seguridad de la memoria espacial y seguridad de la memoria temporal (seguridad espacial y seguridad temporal).

  • Violación de la seguridad del espacio: cuando el puntero accede a un objeto fuera del alcance del programa, se violará la seguridad del espacio de memoria. El ejemplo más común es utilizar el desbordamiento del búfer en la pila para sobrescribir la dirección de retorno de la función con un valor diseñado por el atacante para guiar la dirección del flujo de ejecución del programa, o utilizar directamente el desbordamiento para reescribir variables importantes o información importante.

  • Violación de seguridad temporal: la seguridad temporal de la memoria se viola cuando se usa una referencia a un objeto fuera de un rango de tiempo específico, generalmente después de que la memoria del objeto instanciado se haya reasignado sin una inicialización de memoria estricta . Por ejemplo, una infracción de seguridad horaria (Uso después de la liberación) provocada por la desreferenciación de un puntero a una memoria no válida (normalmente no asignada o liberada).

La seguridad de la memoria se puede subdividir en

  • Desbordamiento del búfer,
  • Doble gratis,
  • Desreferenciación de puntero nulo,
  • Gratis no válido,
  • Acceder a la memoria no inicializada (Leer la memoria no inicializada),
  • Usar después de gratis (Usar después de gratis) y varias otras subcategorías.

Los ataques de vulnerabilidad de la memoria se dividen principalmente en tres etapas:

  • Implantación de código: implantar el código de ataque (shellcode) en el programa de destino;

  • Ataque de desbordamiento: el desbordamiento del búfer se logra ingresando una cadena especial como parámetro, y la dirección de retorno después del desbordamiento apunta a la dirección inicial del código de ataque;

  • Secuestro del sistema: secuestra y controla el sistema ejecutando el código de ataque;

Tecnología de protección del software de seguridad de la memoria

Después de comprender los ataques de vulnerabilidad de la memoria y varias vulnerabilidades de la memoria, presentemos las tecnologías de protección de software existentes:

El más típico es el mecanismo de protección de ejecución de datos (DEP) . Su principio básico es marcar la página de memoria donde se encuentran los datos como no ejecutable . Cuando el programa se desborda y se transfiere con éxito al shellcode , el programa intentará ejecutar instrucciones en la página de datos y la CPU lanzará una excepción en lugar de ejecutar instrucciones maliciosas. Al habilitar DEP, puede evitar de manera efectiva que las páginas de datos (como la página de montón predeterminada, varias páginas de pila y páginas de grupo de memoria) ejecuten código.

La tecnología ASLR (Address Space Layout Randomization) es un mecanismo de protección que interfiere con el posicionamiento del shellcode al no utilizar una dirección base fija al cargar un programa. Incluyendo aleatorización de imágenes, aleatorización de pila y aleatorización de PEB (bloque de entorno de proceso, bloque de entorno de proceso) y TEB (bloque de entorno de subprocesos, bloque de entorno de subprocesos)

Otro mecanismo de defensa eficaz es el mecanismo Stack Canary. Su principio es insertar información de cookies en la parte inferior de la pila cuando se ejecuta la función. Cuando la función regresa, verificará si la información de las cookies es legal. Si no es legal, el programa dejará de ejecutarse.

El mecanismo ASLR, DEP (NX/XD) o mecanismo Stack Canary implementado a través del sistema de memoria virtual evita que los atacantes inyecten y ejecuten código de ataque arbitrario a voluntad, por lo que estos mecanismos de prevención protegen el funcionamiento seguro del programa hasta cierto punto.

Sin embargo, frente a códigos y métodos de ataque más complejos, estos mecanismos de protección enfrentarán las consecuencias del fracaso. Por ejemplo, en la programación orientada al retorno (ROP), la ejecución de código arbitrario se logra destruyendo punteros de código (como direcciones de retorno) y encadenando la ejecución de múltiples dispositivos. Las secuencias de estos binarios sin formato se combinan para implementar el código de ataque malicioso previsto por el atacante.

Además de agregar algunas tecnologías de protección de software a los lenguajes de programación originales, en los últimos años han surgido muchos lenguajes de programación centrados en la seguridad. Por ejemplo, el lenguaje Rust ha recibido mucha atención por su capacidad para lograr un rendimiento comparable al de C/C++ al mismo tiempo que garantiza una alta seguridad, por lo que este artículo presenta brevemente el lenguaje de seguridad Rust.

Seguridad

Rust valora primero la seguridad y la velocidad. No tiene un mecanismo de recolección de basura, pero logra con éxito la seguridad de la memoria. Lo que diferencia a Rust de C y C++ son sus sólidas garantías de seguridad.

Rust es completamente seguro para la memoria a menos que se seleccione explícitamente usando la palabra clave "inseguro".

El diseño central de Rust es un conjunto estricto de reglas de seguridad que se aplican mediante comprobaciones en tiempo de compilación. Para admitir más controles de bajo nivel, Rust permite a los programadores omitir estas comprobaciones del compilador para escribir código inseguro. Aproximadamente el 70% de los problemas de seguridad en CVE proporcionados por MSRC (Centro de respuesta de seguridad de Microsoft) son problemas de seguridad de la memoria. Creen que si los programas que actualmente causan problemas de seguridad estuvieran escritos en Rust, entonces el 70% de los problemas de seguridad de la memoria ya no existirían.

Un artículo de investigación en la Conferencia Internacional ACM SIGPLAN (PLDI'20) señaló que "el mecanismo de código seguro del lenguaje Rust puede evitar de manera muy efectiva los problemas de seguridad de la memoria. Todos los problemas de seguridad de la memoria encontrados en versiones estables están relacionados con código inseguro".

actuación

Si desea utilizar Rust para reemplazar C o C++ como lenguaje de programación principal, debe considerar las capacidades de rendimiento y control del lenguaje de programación.

Rust también tiene un tiempo de ejecución configurable, y la biblioteca estándar de Rust se basa en libc para soportar su plataforma, al igual que los lenguajes C y C++, y la biblioteca estándar de Rust también es opcional. Los programas compilados por Rust se ejecutan en plataformas que no tienen un sistema operativo. También se puede ejecutar.

Tecnología de protección de hardware de seguridad de memoria

A continuación se presenta una breve introducción a algunas ideas técnicas utilizadas actualmente por la comunidad académica para proteger el hardware de seguridad de la memoria, como referencia:

1.Mitigación basada en Tripwire

Este tipo de solución coloca principalmente la zona roja entre los bloques de memoria asignados. Cuando el puntero accede al área de la zona roja, se producirá el error correspondiente. Ejemplos típicos son AddressSanitizer de Google (ATC'12) y el comprobador de memoria de Valgrind (PLDI'07).

La innovación de Tripwire es que se activan inmediatamente cuando se produce una intrusión en la memoria, confiando en la memoria oculta para realizar comprobaciones, a diferencia de medidas de software como Canary (Stack Canary) que dependen del software para realizar comprobaciones explícitas.

Aunque Tripwire proporciona una seguridad sólida, conlleva una sobrecarga prohibitiva de alto rendimiento y, por lo tanto, no es adecuado para su implementación en dispositivos sensibles al rendimiento. La siguiente figura es el diagrama de flujo de comportamiento de REST (ISCA'18). REST es simplemente un valor aleatorio muy grande integrado en el programa. Es una detección de zona roja basada en hardware: reemplaza la zona roja con un token para eliminar la dependencia de la memoria oculta. Todas las instrucciones de carga/almacenamiento detectarán si el área de direcciones está aislada.

2. Punteros gordos

Además del mecanismo Tripwire, los punteros gruesos también son una dirección popular en la industria. Mantienen principalmente los metadatos del objeto (generalmente referidos a la base del objeto, límites, etc.), insertados en el puntero e indexados junto con la dirección del puntero. Generalmente, se comprobará durante la compilación estática y la instrumentación dinámica durante el tiempo de ejecución realiza la protección de seguridad de la memoria.

Hardbound (SOSP'08) utiliza espacio de sombra para almacenar los permisos del puntero. La dirección a la que apunta el puntero es base más tamaño como todo el intervalo enlazado. Softbound (PLDI'09) y Watchdog (ISCA'12) para cada uno Generar un único identificador para cada asignación de memoria, asociar estos identificadores con punteros y verificar para garantizar que el identificador esté presente en cada acceso a la memoria. Para proporcionar una detección integral, Watchdog utiliza identificadores basados ​​en identificadores casi exclusivamente en la detección de hardware. La siguiente imagen es una descripción general del diseño de HardBound. Consulte el documento para conocer el significado específico. Este artículo no lo describirá.

3. Capacidad

El puntero de capacidad y el puntero Fat son similares hasta cierto punto. Están protegidos agregando metadatos al puntero. Sin embargo, la diferencia entre el puntero de capacidad y el puntero Fat es que es un puntero Fat que no se puede modificar. Cada capacidad solo se puede crear y transferir. (referencia del módulo secundario) y se elimina. Los permisos de lectura, escritura, ejecución y otros de cada puntero también se adjuntan al puntero. El puntero ya no sólo funciona como un índice, sino más bien como un "objeto" con permiso de recuperación.

También hay algunas implementaciones de hardware basadas en la capacidad, y el diseño representativo es CHERI (ISCA'14, Instrucciones RISC mejoradas de hardware de capacidad). Los metadatos (límites y permisos) se mantienen en línea con las direcciones, eliminando así costosas búsquedas en tablas ocultas mediante el intercambio de regiones, almacenamiento y ancho de banda de memoria. A continuación se explica brevemente algunas ventajas del diseño CHERI:

  • Se propuso e implementó un modelo de protección abstracto de CHERI en MIPS de 64 bits, RISC-V de 32/64 bits y ARMv8-A de 64 bits, respectivamente.
  • Tiene un modelo ISA formal, admite simulación Qemu-CHERI y múltiples prototipos de FPGA
  • Verificar que el modelo de seguridad ISA esté establecido mediante software
  • Numerosos sistemas de soporte de software: CHERI Clang/LLVM/LLD, CHERI BSD, C/C++ APP
  • Morello: La placa de demostración de nivel industrial de la arquitectura CHERI ISA es actualmente la única implementación física de la arquitectura prototipo CHERI.

controlar la integridad del flujo

Los ataques de secuestro de flujo de control logran el propósito del ataque construyendo vectores de ataque específicos, explotando vulnerabilidades del software como el desbordamiento de memoria y alterando ilegalmente los datos de control en el proceso, cambiando así el flujo de control del proceso y ejecutando programas de ataque preconstruidos. Un ataque de desbordamiento de búfer exitoso se puede utilizar como un requisito previo eficaz para modificar el flujo de control en ejecución del sistema informático. Por ejemplo, puede modificar la dirección de retorno del programa, el EBP de la función que llama o modificar el puntero de función y GOT. tabla, etc., llevando así el programa a la configuración preestablecida del atacante. Los programas de ataque se utilizan para ejecutar código malicioso, provocando el colapso de todo el programa o permitiendo a los atacantes obtener el control de parte del sistema, provocando una crisis de seguridad para todo el sistema informático. . Por lo tanto, la defensa contra ataques de desbordamiento de buffer tiene un significado práctico extremadamente importante.

W ⊕, este es un ataque de reutilización de código. El ataque inicial de reutilización de código es un ataque de retorno a libc. Dado que la biblioteca libc del sistema a menudo contiene subrutinas para ejecutar llamadas al sistema y otras funciones que pueden ser útiles para el atacante, lo más probable es que se busque código para ensamblar el ataque. . .

En un ataque Return-to-libc, el atacante secuestra el flujo de control del programa explotando una vulnerabilidad de desbordamiento del búfer, selecciona una función de biblioteca disponible y sobrescribe la dirección del remitente con su ubicación de entrada, y luego sobrescribe los parámetros diseñados con una sobrescritura detallada de la ubicación de la pila. Pasado a la función para completar el ataque.

Ataque ROP

El ataque de retorno a libc pasa por alto el método de defensa W⊕X, pero sus deficiencias también son obvias: los programas de ataque que puede ejecutar no son lo suficientemente flexibles y son fáciles de atacar para la defensa, por lo que surgió ROP.

ROP (Programación orientada al retorno) permite a los atacantes ejecutar código en presencia de defensas de seguridad, como la protección del espacio ejecutable y la firma de código. En esta técnica, un atacante puede controlar la pila de llamadas para secuestrar el flujo de control del programa y luego ejecutar una secuencia cuidadosamente seleccionada de instrucciones de la máquina que ya está presente en la memoria de la máquina, llamada "gadget".

Cada dispositivo normalmente termina con una instrucción de retorno (ret) y está ubicado en una subrutina dentro del programa existente y/o código de biblioteca compartida. Estos dispositivos están encadenados para permitir que un atacante realice acciones arbitrarias en una máquina defendida.

ROP aún sobrescribe la pila con la dirección de retorno y los parámetros, pero la dirección de retorno proporcionada por el atacante puede apuntar a cualquier punto en la base del código, específicamente cualquier fragmento de código (gadgets) que termine con la instrucción ret.


Incluso si las funciones de biblioteca disponibles existentes son completamente inexplotables, ROP todavía puede usar un enfoque de mosaico para ensamblar un programa de ataque completo, especialmente porque el conjunto de instrucciones de la arquitectura x86 es muy denso y casi cualquier secuencia de bytes aleatoria puede interpretarse como algo válido. Conjunto de instrucciones x86.

El punto clave del ataque ROP es buscar un conjunto de dispositivos que cumplan con los siguientes requisitos:

  • Debe terminar con ret
  • No cambia explícitamente el puntero de la pila.
  • Tiene funciones básicas como funciones aritméticas y lógicas.
  • Con funciones complejas como acceso a memoria y llamadas al sistema.
  • Con funciones de bucle y bifurcación condicional (modificar el valor de esp a través de la lógica)
  • Siempre que se pueda encontrar un conjunto de dispositivos que cumplan las condiciones anteriores en el programa ejecutable, se generará disfrazada una máquina orientada al retorno completo de Turing.

Muchas defensas contra la ROP:

  • DynIMA (medición y certificación de integridad dinámica, medición de integridad dinámica): detecta cada secuencia de instrucciones ejecutada consecutivamente que termina en ret
  • DROP (Detección de código malicioso ROP, detección de código malicioso de programación orientada al retorno): detecta la dirección de retorno que aparece en la pila y que siempre apunta a un espacio de memoria específico.
  • Kernels "sin retorno": una optimización a nivel de compilador que elimina todas las instrucciones ret en el programa, lo que hace imposible que los atacantes utilicen ataques ROP.
  • ……

Ataque JOP

Los ataques ROP son poderosos, pero depender de las instrucciones de pila y retirada para secuestrar el flujo de control también es su desventaja. Sobre esta base, los atacantes propusieron el método de ataque JOP/COP.

JOP/COP (Programación orientada a salto/llamada, programación orientada a salto/llamada) abandona toda dependencia del flujo de control de la pila y el descubrimiento y vinculación de dispositivos, y solo utiliza una serie de instrucciones de salto indirectas, que evitan por completo todas las defensas. contra ROP.

En ROP, un programa orientado a saltos consta de un conjunto de direcciones de dispositivos y valores de datos cargados en la memoria. Las direcciones de dispositivos son análogas a los códigos de operación en una nueva máquina orientada a saltos. En ROP, estos datos se almacenan en la pila, por lo que el puntero de la pila actúa como un "contador de programa" en programas orientados al retorno.


JOP no se limita a utilizar esp para hacer referencia a la dirección de su dispositivo, ni el flujo de control está controlado por instrucciones ret. En cambio, JOP utiliza una tabla de despacho para contener direcciones y datos de widgets.


Un "contador de programa" es cualquier registro que apunte a la tabla de despacho. El flujo de control está impulsado por dispositivos programadores especiales que ejecutan secuencias de dispositivos. En cada llamada, el planificador avanza el contador del programa virtual e inicia el widget asociado. Cuando finaliza la ejecución del gadget, se devuelve el despachador.


Lo anterior es un ejemplo de un programa orientado a saltos, y el orden de los saltos está representado por el número 1->6. Aquí, edx se usa como pc, y el despachador va a la siguiente dirección en una tabla consecutiva de direcciones de dispositivos simplemente sumando 4 (por lo tanto, f(pc) = pc + 4). La función "ataque"

  • desreferenciación eax
  • Agregue el valor de la dirección ebx a eax
  • El resultado se almacena en la dirección ecx.
    Los registros esi y edi se utilizan para devolver el control al planificador.

medios defensivos

Control-flow Enforcement Technology (CET) es una extensión del conjunto de instrucciones propuesta por Intel específicamente para desbordamientos de pila avanzados como ROP/COP/JOP. Se divide principalmente en los dos aspectos siguientes:

  • a.Shadow Stack (SS, Shadow Stack) es un método de defensa principalmente contra ROP. El objetivo es proteger la dirección de retorno de la pila. Esta defensa crea una segunda pila separada de la pila de datos del programa y, cuando la pila oculta está habilitada, la instrucción CALL inserta la dirección de retorno en las pilas de datos y ocultas.
  • b. El seguimiento indirecto de sucursales (IBT, indirect Branch Tracking) es un método de defensa principalmente contra COP/JOP. El método consiste en agregar una nueva instrucción ENDBRANCH, que se utiliza para identificar la dirección de destino efectiva de llamadas indirectas y saltos en el programa. Su código de operación se expresa como NOP para arquitecturas más antiguas que no son compatibles con esta instrucción y no impedirán que el programa se ejecute. Para los procesadores que admiten CET, el código de operación sigue siendo NOP y no supone una carga de hardware adicional.

    Para ataques de integridad del flujo de control, ARM proporciona dos soluciones de defensa: BTI y PAC:
    a. Código de autenticación de puntero (PAC, código de autenticación de puntero): el conjunto de instrucciones ARMV8.3-A agrega un mecanismo de autenticación de puntero (PA). un registro como puntero para verificar su contenido antes de acceder a datos o código está destinado a combatir ataques ROP. El principio básico del mecanismo es utilizar instrucciones especiales para obtener un texto cifrado de 64 bits mediante un algoritmo de cifrado basado en un contexto de 64 bits, el valor original del puntero y una clave de 128 bits, truncarlo y utilizarlo como PAC. Utilice instrucciones especiales para verificar el valor del puntero antes de usarlo. Después de pasar la verificación, el bit alto del puntero se restaurará a su estado original. De lo contrario, se completará un código de error en el bit alto del puntero. y se activará una excepción durante la fase de direccionamiento.
  • b.Identificación de objetivo de sucursal (BTI, identificación de objetivo de sucursal): BTI es una característica de seguridad agregada por Armv8.5 para limitar los ataques y se utiliza principalmente para defenderse contra ataques JOP (programación orientada a saltos). El principio de funcionamiento es que el procesador configurará una instrucción BTI para la instrucción BR o BLR durante la compilación, que consiste en establecer el objetivo de salto especificado para la instrucción de salto indirecto (se usa una palabra muy apropiada en inglés, plataformas de aterrizaje, punto de aterrizaje) . Si el objetivo de salto indirecto no es el punto de aterrizaje especificado, se generará una excepción de objetivo de salto. El uso de sitios de aterrizaje limita el número de objetivos posibles para saltos indirectos, lo que dificulta encadenar dispositivos para formar nuevos programas.

El ataque de memoria reaparece

A continuación se muestra un fragmento del programa de ataque Buffer-Overflow como ejemplo:

void shellcode() {
  .......
}
int main(int argc, char *argv[]) {
  char buf[8];
  if(argc < 2) {
    printf("Pass an argument, champ!\n");
  }
  strcpy(buf,argv[1]);
  printf(buf);
}
  • La primera etapa de la implantación del código: implantar el código de ataque (shellcode) en el programa de destino;

  • El ataque de desbordamiento de la segunda etapa: logra el desbordamiento del búfer ingresando una cadena especial como parámetro, y la dirección de retorno después del desbordamiento apunta a la dirección inicial del código de ataque (dirección inicial del código shell);

  • La tercera etapa del secuestro del sistema: secuestro y control del sistema mediante la ejecución de código de ataque;

El entorno de SoC es Chipyard, el núcleo del procesador es SonicBoom y la FPGA usa VCU118/VC707.

Este es un ejemplo del resultado de salida de un programa de ataque de desbordamiento de pila. La depuración de GDB encuentra la dirección RA (dirección de retorno) del programa, la sobrescribe a través del desbordamiento de pila y hace que el programa salte al programa de ataque establecido. El resultado final es la salida ¡Tú ganas! Además de cambiar el shell del programa.

Marco de seguridad Arm: un sistema de referencia importante para el diseño de seguridad RISC-V

En respuesta a las amenazas de seguridad anteriores, muchos desarrolladores en el campo RISC-V han creado sus propias soluciones, pero estas soluciones de seguridad aún no se han estandarizado. La arquitectura Arm tiene contramedidas de seguridad altamente estandarizadas, que pueden proporcionar ideas para futuras direcciones de expansión de seguridad RISC-V aprendiendo de la arquitectura de seguridad Arm. A continuación se presentan cuatro tipos de contramedidas de seguridad:

  • Tecnologías de ejecución defensiva: esta es una clase de tecnologías de protección contra ataques de flujo de control, ataques de acceso a datos y ataques de canal lateral. El software rara vez es perfecto y algunas vulnerabilidades del programa se pueden reparar artificialmente mediante programación defensiva, pero es posible que este enfoque no sea aplicable a todos los programas de código. En la actualidad, la solución a este tipo de problemas de seguridad se realiza principalmente a través de tres medios técnicos: modificar el hardware, el compilador y el tiempo de ejecución para protegerlo. Este tipo de contenido se explicará en detalle más adelante y no se explicará aquí.
  • Tecnologías de aislamiento: los límites de seguridad bien definidos son uno de los principios más básicos de la ingeniería de seguridad. Esta defensa de seguridad es principalmente un tipo de tecnología de aislamiento de defensa diseñada para TEE. Los productos representativos incluyen Arm TrustZone, Intel SGX, Apple Secure Enclave, Penglai Enclave, Keystone Enclave, etc.
  • Servicios de seguridad de plataforma común: por motivos de seguridad, todos los dispositivos requieren una raíz de confianza (RoT) y se verifican con una extensa lista de verificación de requisitos de seguridad. Sin el uso de marcos disponibles públicamente, el mercado no puede determinar si un producto es adecuadamente resistente a los ataques.
  • API de seguridad estándar: con el rápido desarrollo de la informática y la tecnología, cada vez más clientes utilizan interfaces API personalizadas por desarrolladores para desarrollar de forma iterativa varios microservicios. Por esta razón, la interfaz API no solo puede conectar las funciones del servicio, sino también transmitir datos. La protección de seguridad de la API se ha vuelto cada vez más importante. En la actualidad, ha habido muchos accidentes de fuga de datos debido a ataques o lagunas API. Por ejemplo, el incidente de fuga de información de Huazhu en China en 2018 provocó la filtración de una gran cantidad de información personal del usuario, e Instagram en 2019 fue causado por la interfaz API. vulnerabilidades Se filtraron datos y fotografías del usuario.
    Cómo establecer una arquitectura de seguridad informática completa y eficaz en la cadena ecológica RISC-V y reducir la fragmentación del diseño técnico es una demanda muy desafiante y urgente en el desarrollo ecológico actual de RISC-V.

Oportunidades y desafíos

Con la popularización de la tecnología 5G, llegará la era del verdadero Internet de Todo, y un sinfín de vulnerabilidades tendrán un impacto cada vez más generalizado. Las deficiencias existentes en materia de protección de la seguridad se agravarán aún más. En un entorno donde todo puede ser atacado, varios eslabones débiles se convertirán en los primeros objetivos de los atacantes. Si observamos el desarrollo de X86, Arm, MIPS y otras arquitecturas, la seguridad de los chips siempre está "fuera de contacto" durante el diseño inicial de la arquitectura, y se agregan "parches" en las últimas etapas del ciclo de diseño. ¿Se implementará antes la arquitectura RISC-V? ¿Implementar y construir un "foso" seguro? Los círculos industriales y académicos han demostrado la eficiencia y conveniencia del conjunto de instrucciones de código abierto RISC-V a través de varios ejemplos (procesadores de alto rendimiento, aceleradores eficientes, etc.), pero actualmente todavía se están promoviendo activamente las extensiones de seguridad en el campo RISC-V. . A medida que se utilizan cada vez más chips RISC-V en sistemas de misión crítica, cómo inyectar más pensamiento y estrategias en los métodos de seguridad y el ciclo de vida del desarrollo de la seguridad, y cómo expandirse desde la protección de un solo punto a la protección global sistemática. es un nuevo desafío para los desarrolladores de arquitectura.

Supongo que te gusta

Origin blog.csdn.net/weixin_45264425/article/details/132698911
Recomendado
Clasificación