Puertas traseras de microcódigo ensambladas con procesador X86

No confiamos en el software durante mucho tiempo y, por lo tanto, llevamos a cabo su auditoría, realizamos ingeniería inversa, lo ejecutamos en un modo paso a paso, lo ejecutamos en el sandbox. ¿Qué pasa con el procesador en el que se ejecuta nuestro software? - Confiamos ciega y sinceramente en esta pequeña pieza de silicio. Sin embargo, el hardware moderno tiene los mismos problemas que el software: secreto, funcionalidad indocumentada, errores, vulnerabilidades, malware, troyanos, rootkits, puertas traseras.



ISA (Arquitectura de conjunto de instrucciones) x86 es una de las "arquitecturas de conjunto de instrucciones" más cambiantes de la historia. Comenzando con el diseño 8086 desarrollado en 1976, ISA está experimentando continuos cambios y actualizaciones; manteniendo la compatibilidad con versiones anteriores y el soporte para la especificación original. Más de 40 años de crecimiento, la arquitectura ISA ha crecido y continúa creciendo con muchos modos y conjuntos de instrucciones nuevos, cada uno de los cuales agrega una nueva capa al diseño anterior, que ya está sobrecargado. Debido a la política de compatibilidad con versiones anteriores, los procesadores x86 modernos incluso contienen instrucciones y modos que ya están completamente olvidados. Como resultado, tenemos una arquitectura de procesador, que es un complejo laberinto de tecnologías nuevas y antiguas. Un entorno tan extremadamente complejo: causa muchos problemas con la ciberseguridad del procesador. Por lo tanto, los procesadores x86 no pueden afirmar ser la raíz confiable de la infraestructura cibernética crítica.


¿Todavía confías en tu procesador?


Seguridad de programas y sistema operativo: se basa en la seguridad del hardware en el que se implementan. Como regla general, los desarrolladores de software no tienen en cuenta el hecho de que el hardware en el que se implementa su software puede ser no confiable, malicioso. Cuando el hardware se comporta erróneamente (ya sea intencionalmente o no), los mecanismos de seguridad del software se vuelven completamente inútiles. Durante muchos años, se han ofrecido varios modelos de procesadores seguros: Intel SGX, AMD Pacifica, etc. Sin embargo, la regularidad envidiable con la que se publica la información sobre fallas críticas (de las recientes, por ejemplo Meltdown y Spectre) y las funciones de "depuración" no documentadas detectadas - conduce a La idea de que nuestra confianza incondicional en los procesadores no tiene fundamento.


Los procesadores x86 modernos son un entretejido muy complejo e intrincado de las tecnologías más recientes y antiguas. 8086 tenía 29 mil transistores, Pentium 3 millones, Broadwell 3.2 mil millones, Cannonlake más de 10 mil millones.



Con tantos transistores, no es sorprendente que los procesadores x86 modernos estén plagados de instrucciones indocumentadas y vulnerabilidades de hardware. Entre los indocumentados, que fueron descubiertos casi por accidente, las instrucciones: ICEBP (0xF1), LOADALL (0x0F07), apicall (0x0FFFF0) [1], que permiten desbloquear el procesador para el acceso no autorizado a áreas de memoria protegidas.


En cuanto a las numerosas vulnerabilidades de hardware de los procesadores (ver dos figuras a continuación), permiten a un ciber-atacante escalar privilegios [3], extraer claves criptográficas [4], crear nuevas instrucciones de ensamblador [2], cambiar la funcionalidad de las instrucciones de ensamblador ya existentes [2] , instale ganchos en las instrucciones del ensamblador [2], tome el control de la "virtualización acelerada por hardware" [7], intervenga en "cálculos criptográficos atómicos" [7] y, finalmente, dulce, ingrese al "modo dios": ive usted mismo un hardware de Intel ME legítima de puerta trasera (que le permite recibir acceso remoto incluso el ordenador apagado). [8] Y todo esto, sin dejar rastros digitales.




Los procesadores modernos son más software que hardware


Estrictamente hablando, los procesadores modernos ni siquiera pueden llamarse "hardware" en el sentido completo de la palabra. Porque su funcionalidad más crítica (incluida ISA) es proporcionada por microcódigo intermitente. Inicialmente, el microcódigo era el principal responsable de controlar la decodificación y la ejecución de instrucciones complejas de ensamblador: operaciones de punto flotante, primitivas MMX, manejadores de línea con el prefijo REP, etc. Sin embargo, con el tiempo, se asigna más y más responsabilidad al microcódigo para procesar las operaciones dentro del procesador. Por ejemplo, las extensiones modernas de procesadores Intel, como AVX (Advanced Vector Extensions) y VT-d (virtualización de hardware) se implementan en microcódigo.


Además, hoy el microcódigo es responsable, entre otras cosas, de mantener el estado del procesador, de administrar el caché y también de administrar el ahorro de energía. Para ahorrar energía, el microcódigo implementa un mecanismo de interrupción que procesa estados de energía: estado C (grado de suspensión de ahorro de energía: desde el estado activo hasta la suspensión profunda) y estado P (diferentes combinaciones de voltaje y frecuencia). Entonces, por ejemplo, el microcódigo es responsable de restablecer el caché L2 al ingresar al estado C4, así como al salir de él.


¿Por qué los fabricantes de procesadores usan microcódigo?


Los fabricantes de procesadores x86 usan microcódigo para descomponer instrucciones de ensamblaje complejas (que pueden tener hasta 15 bytes de largo) en una cadena de microinstrucciones simples, para simplificar la arquitectura del hardware y facilitar el diagnóstico. De hecho, el microcódigo es un intérprete entre la arquitectura CISC externa (visible para el usuario) y la arquitectura RISC interna (hardware).


Si se detectan errores en la arquitectura CISC (principalmente en ISA), los fabricantes publican una actualización de microcódigo que se puede descargar al procesador a través del BIOS / UEFI de la placa base o del sistema operativo (durante el proceso de arranque). Gracias a este sistema de actualización basado en microcódigos, los fabricantes de procesadores se proporcionan flexibilidad y reducción de costos, al tiempo que corrigen errores en sus equipos. El error sensacional con fdiv, que derribó severamente los procesadores Intel Pentium en 1994, hizo que el hecho de que el software de alta tecnología sea propenso a errores como el software lo hace aún más obvio. Este incidente ha dado a los fabricantes aún más interés en la arquitectura de los procesadores basados ​​en microcódigo. Por lo tanto, Intel y AMD comenzaron a construir sus procesadores utilizando la tecnología de microcódigo. Intel: comenzando con el Pentium Pro (P6), lanzado en 1995. AMD: comenzando con el K7, lanzado en 1999.


Todo secreto queda claro


A pesar de que los fabricantes de procesadores están tratando de mantener la arquitectura de los microcódigos y el mecanismo para actualizarlos con la más estricta confidencialidad, el enemigo no está dormido. Los fragmentos de información fragmentada (principalmente de patentes, como AMD RISC86 [5]) y la reversión reflexiva de las actualizaciones oficiales del BIOS (como sucedió con K8 [6]), arrojan luz gradualmente sobre el secreto del microcódigo (ver, por ejemplo, en la figura a continuación " Mecanismo de actualización del microcódigo del procesador AMD "). Y gracias a la constante evolución de las herramientas de inversión (tanto de software como de hardware) [2], las prometedoras técnicas de fuzzing [1] y la aparición de herramientas OpenSource como Microparse [9] y Sandsifter [10]: un ciber-atacante puede aprender todo sobre un microcódigo que es necesario para tener que escribir malware de microcódigo en él.



Entonces, por ejemplo, en [2] como "¡Hola mundo!" (el primer paso para trojanizar el microcódigo) se desarrolló un "micro gancho" (programa de microcódigo que intercepta la instrucción del ensamblador), que cuenta cuántas veces el procesador accedió al comando div. Este microenganche es una inyección en el controlador del div de instrucciones del ensamblador.



Ibid [2] presenta un microenganche más avanzado, que se ubica silenciosamente en las instrucciones del ensamblador del div ebx, sin dar presencia, y se activa solo cuando se cumplen condiciones específicas al acceder al ebx div: el registro ebx contiene el valor B y el registro eax contiene el valor A. Cuando se activa, este micro gancho aumenta el valor del registro eip (puntero de instrucción actual) en uno. Como resultado, la ejecución del programa (que tuvo el coraje de referirse a la instrucción div ebx) continúa con un desplazamiento: no desde el primer byte del comando que sigue al div ebx, sino desde su segundo byte. Si se especifican otros valores en los registros eax y ebx, entonces el div ebx funciona como de costumbre. ¿Cuál es el valor práctico en esto? Por ejemplo, para activar silenciosamente una cadena oculta de instrucciones de ensamblador cuando se utilizan técnicas de ofuscación con "instrucciones superpuestas" [11].



Estos dos ejemplos demuestran cómo las instrucciones legítimas de ensamblador se pueden usar para ocultar código troyano arbitrario en ellas.


Al mismo tiempo, un atacante cibernético puede activar su carga maliciosa, incluso de forma remota. Por ejemplo, cuando se cumple la condición necesaria para la activación en una página web controlada por un atacante. Esto es posible gracias a los compiladores Just-in-Time (JIT) y Ahead-of-Time (AOT) integrados en los navegadores web modernos. Estos compiladores le permiten emitir una secuencia predefinida de instrucciones de ensamblador para el código de máquina, incluso si escribe el programa exclusivamente en JavaScript de alto nivel (consulte la última lista, justo arriba).


Bibliografia
  1. Christopher Domas . Rompiendo el x86 ISA // DEFCON 25.07.2017.
  2. Philipp Koppe . Ingeniería inversa Microcódigo del procesador x86 // Actas del 26º Simposio de seguridad USENIX. 2017. pp. 1163-1180.
  3. Matthew Hicks . ESPECIFICACIONES: un mecanismo de tiempo de ejecución ligero para proteger el software de errores críticos del procesador de seguridad // Actas de la 28ª Conferencia internacional sobre soporte arquitectónico para lenguajes de programación y sistemas operativos (ASPLOS). 2015. pp. 517-529.
  4. Adi Shamir . Bug Attacks // Actas de la 28ª Conferencia anual sobre criptografía: avances en criptología. 2008. pp. 221–240.
  5. John Favor Conjunto de instrucciones RISC86 // Patente de EE. UU. 6336178. 2002.
  6. Opteron expuesto: actualizaciones de microcódigo de ingeniería inversa AMD K8 . 2004
  7. Saming Chen . Análisis de seguridad del microcódigo del procesador x86 .2014.
  8. Catalin Cimpanu . El malware utiliza la característica oculta de la CPU Intel para robar datos y evitar firewalls . 2017
  9. Daming Chen . Microparse: analizador de microcódigo para procesadores AMD, Intel y VIA // GitHub. 2014.
  10. Sandsifter: el procesador fuzzer x86 // GitHib. 2017
  11. Karev V.M. Cómo escribir un programa ensamblador con instrucciones superpuestas (otra técnica para ofuscar el código de bytes) // Habrahabr. 2018. URL: (Fecha de acceso: 25 de octubre de 2018).

Source: https://habr.com/ru/post/es427757/


All Articles