Microcódigo, la frontera final

Si bien el título de este escrito recuerda un poco al usado en la presentación inicial de la Space Opera Star Trek, debo decir que no estoy escribiendo sobre ciencia ficción.

Para empezar, creo que puedo afirmar que la seguridad informática es un quebradero de cabeza. Es una batalla que se libra actualmente en diferentes frentes, todos con mayor o menor vulnerabilidad, y podemos estar seguros de que todos estos frentes poseen alguna puerta trasera a la espera de ser encontrada y aprovechada.

Por otro lado, en informática, el concepto de seguridad se soporta sobre cuatro pilares base: integridad, confidencialidad, disponibilidad y autenticación.

Integridad: Es la cualidad de la información para ser correcta, manteniendo sus datos exactamente tal cual fueron generados, sin manipulaciones ni alteraciones por parte de terceros.

Confidencialidad: Es la cualidad de la información para no ser divulgada a personas o sistemas no autorizados.

Disponibilidad: Es la cualidad de la información para poder ser accedida cuando sea requerido mediante el uso de los canales adecuados.

Autentificación: Es la cualidad de la información que permite identificar al generador de esta. Garantiza la originalidad e integridad de los datos.

Todo lo anterior funciona muy bien gracias a los diferentes recursos de encriptación disponibles (certificados o firmas digitales, llaves asimétricas, etc.) y hasta cierto punto la confiabilidad se soporta sobre complejos algoritmos matemáticos y la seguridad que ofrece el hardware subyacente, que es el que finalmente proporciona toda la infraestructura física necesaria (cálculo matemático, almacenamiento, canales de comunicaciones, etc.).

Y escribo hasta cierto punto porque se asume que el elemento activo final, el procesador, es inmune e incólume a ataques e intrusiones que alteren y/o modifiquen su funcionamiento interno. Después de todo, está compuesto de millones de transistores y lógica cableada (hardwired), y todo eso ha sido  fijado con mucho proceso químico y fotolitográfico, y es inmodificable… hummm… ¿inmodificable? bueno… si… pero… no…

¿No? ¿Cómo qué no?

Recientemente, explorando las recientes ponencias en el 34th Chaos Communication Congress (34C3) me llamo especialmente la atención una denominada “Everything you want to know about x86 microcode, but might have been afraid to ask”, que entre otras cosas expone como es factible la modificación del comportamiento en algunas de las instrucciones de alto nivel en un microprocesador (Intel o AMD para más señas) modificando el microcódigo.

Y… ¿Qué es el microcódigo?

Por definición, microcódigo es una tabla de búsqueda para ciertos tipos de instrucciones altamente especializadas, la cual es traducida en un conjunto de micro instrucciones. Puede interpretarse como una instrucción macro traducida a una secuencia de instrucciones más simples.

La razón de la existencia del microcódigo fue inicialmente la necesidad de bajar los costos del desarrollo del silicio y reducir en lo posible el área hardwired que contiene las instrucciones fijas del procesador. Con el avance y mejoras en rendimiento en los nuevos diseños se hacía necesario usar instrucciones más complejas, pero en ese entonces la capacidad de integración era limitada (espacio disponible en el silicio). La solución fue que las instrucciones más complejas fueran ejecutadas usando un micro programa compuesto por una secuencia de instrucciones más simples, ya existentes en el secuenciador de instrucciones del procesador.

La arquitectura CISC, muy usada por Intel y AMD, soporta sus instrucciones más complejas usando este esquema. Es fácil ver que en el fondo, el secuenciador de instrucciones de estos procesadores es realmente arquitectura RISC.

Esta metodología de trabajo no suponía en la práctica ningún problema, porque la tabla de microcódigo también era grabada en forma fija en el núcleo del procesador, en un área especial para ello. Todo esto iba bien hasta que ocurrió el nefasto error FDIV en los procesadores Pentium en 1994. Esto supuso reconsiderar la forma de tratar el manejo de esta tabla para superar nuevos errores en el futuro, y se decidió crear en el procesador una área de memoria volátil, en la cual al iniciar el procesador se copia la tabla de microcódigo hardcoded, pero deja abierta la posibilidad de que esta área de memoria pueda ser sobrescrita mediante una actualización por parte de la BIOS o del KERNEL del sistema operativo.

El formato de microcódigo no solo es muy específico para el modelo de procesador al que se aplica, sino que también es muy diferente como es aplicado por cada fabricante. También es lógico pensar que hacer ese cambio en dicha tabla no es fácil, esta encriptado, y es tecnología propietaria.

Intel ha publicado el método mediante el cual un sistema operativo o un BIOS de la placa base puede actualizar el microcódigo (los contenidos del microcódigo no están documentados y el parche de actualización esta encriptado), el cual debe realizarse después de cada arranque del equipo ya que la actualización se mantienen en RAM. Todo este proceso de actualización está especificado en El Manual del desarrollador de software Intel® 64 e IA-32 Architectures (volumen 3a) que describe el procedimiento de actualización en la sección 9.11.

Regresando a la ponencia en el 34C3, en la charla se expone en vivo métodos de ingeniería inversa cuyo objetivo era descubrir la semántica y comportamiento del microcódigo x86, proporcionando programas de ejemplo y los medios necesarios para llevar a cabo esta tarea. Para este cometido usaron ejemplos de microcódigo de procesadores más viejos (2013) los cuales no poseen una fuerte protección criptográfica y facilitaban la creación de actualizaciones personalizadas.

Del resumen de esta ponencia, y desde mi punto de vista, dos aspectos fueron claves:

“… comenzamos a analizar el comportamiento de la CPU con nuestras propias actualizaciones y usamos estas observaciones para inferir la semántica de microcódigo. Después de un tiempo, teníamos suficiente conocimiento de ingeniería inversa a la semántica para escribir nuestros propios programas de microcódigo. Estos programas van desde pruebas muy simples de conceptos hasta puertas traseras furtivas y primitivas defensivas.”

“… también realizamos análisis de hardware. Al hacer decapado de la CPU y visualizarla con un microscopio óptico y un microscopio electrónico, pudimos ubicar y leer la ROM que contiene el microcódigo. Después de procesar y reordenar las conexiones físicas, recuperamos el microcódigo de la CPU. Esto nos dio más información sobre lo que se puede hacer con microcódigo y permitió obtener más información sobre el comportamiento previsto que nuestro enfoque de ingeniería inversa.”

La ingeniería inversa en microcódigo no es nueva. Este tema fue abordado recientemente en el 26th USENIX Security Symposium (USENIX Security ’17), y sus riesgos de seguridad fueron discutidos en el artículo Security Analysis of x86 Processor Microcode en 2013.

Debo reconocer que todas estas actividades no son de alcance fácil, difíciles de ejecutar, y adicionalmente, empresas como Intel o AMD no ayudaran a que esto sea accesible, pero el precedente de que se puede hacer existe, hay un método aplicable, y es de esperarse que en el futuro surjan nuevos métodos que puedan aprovechar errores u omisiones de seguridad.

 

Referencias:

https://media.ccc.de/v/34c3-9058-everything_you_want_to_know_about_x86_microcode_but_might_have_been_afraid_to_ask

https://en.wikipedia.org/wiki/Pentium_FDIV_bug

https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.html

https://www.usenix.org/conference/usenixsecurity17/technical-sessions/presentation/koppe

https://repository.asu.edu/items/22342

 

Tambien le puede interesar...