Spectre : un grupo de vulnerabilidades de hardware , un error en la mayoría de los procesadores modernos que tienen ejecución de instrucción especulativay predicción avanzada de bifurcaciones , lo que permite que los datos se lean a través de un canal de terceros en forma de una jerarquía de caché común . Afecta a la mayoría de los microprocesadores modernos, en particular las arquitecturas x86/x86_64 (Intel y AMD) y algunos núcleos de procesadores ARM [1] .
La vulnerabilidad permite potencialmente que las aplicaciones locales (un atacante local, cuando ejecuta un programa especial) accedan al contenido de la memoria virtual de la aplicación actual u otros programas [2] [3] [4] . A la amenaza se le han asignado dos identificadores CVE: CVE -2017-5753 y CVE-2017-5715 .
Spectre fue descubierto de forma independiente por investigadores de la corporación norteamericana Google ( Project Zero ) y un grupo colaborador de Paul Kocher, con la participación de empleados de la Universidad Tecnológica de Graz . La vulnerabilidad se encontró a mediados de 2017 y estuvo bajo discusión cerrada y corrección durante varios meses. La publicación de detalles y correcciones estaba programada para el 9 de enero de 2018, pero los detalles de la vulnerabilidad se hicieron públicos el 4 de enero de 2018 al mismo tiempo que el ataque Meltdown , debido a publicaciones de los periodistas de The Register [5] que se enteraron de las correcciones de KAISER/KPTI para combatir Meltdown desde la lista de correo del kernel de Linux [6] .
El error de Spectre permite que las aplicaciones de usuarios maliciosos que se ejecutan en una computadora determinada obtengan acceso de lectura a ubicaciones arbitrarias en la memoria de la computadora utilizada por el proceso de la víctima, como otras aplicaciones (es decir, romper el aislamiento de memoria entre programas). El ataque de Spectre afecta a la mayoría de los sistemas informáticos que utilizan microprocesadores de alto rendimiento, incluidos ordenadores personales, servidores, portátiles y una serie de dispositivos móviles [7] . En particular, el ataque de Spectre se demostró en procesadores fabricados por las corporaciones Intel y AMD y en chips que utilizan núcleos de procesador ARM .
Existe una variante del ataque Spectre que utiliza programas JavaScript para acceder a la memoria de los navegadores (leyendo datos de otros sitios o datos guardados en el navegador) [8] .
Supongamos que el fragmento de código del proceso víctima
si ( x < array1_size ) y = matriz2 [ matriz1 [ x ] * 256 ];es parte de una función que recibe un entero sin signo x de una fuente que no es de confianza, y el proceso que ejecuta este código tiene acceso a una matriz de enteros de 8 bits sin signo matriz1 de tamaño matriz1_tamaño y una segunda matriz de enteros de 8 bits sin signo matriz2 de tamaño 64 kb.
Este fragmento comienza comprobando que x es un valor válido. Y este control es fundamental desde el punto de vista de la seguridad. En particular, evita leer información más allá de los límites de array1 . En su ausencia, los valores x no válidos pueden generar una excepción al intentar leer datos fuera de la memoria disponible del proceso o leer información confidencial accesible al proceso especificando x = <secret_byte_address> - <array1_address_array1> .
Desafortunadamente, la predicción errónea de una rama condicional en la ejecución especulativa de instrucciones puede conducir a la ejecución de una rama del código del programa que, en condiciones normales, nunca se ejecutaría [9] .
Por ejemplo, el fragmento de código anterior podría ejecutarse bajo las siguientes condiciones:
Tales condiciones pueden surgir espontáneamente, sin embargo, también pueden formarse a propósito, por ejemplo, al leer una gran cantidad de datos extraños para llenar el caché del procesador con estos datos y, en consecuencia, eliminar array1_size y array2 del caché, y luego llame a la función del kernel que usa el byte secreto k , para almacenarlo en caché. Sin embargo, si se conoce la estructura de la memoria caché o si el procesador proporciona voluntariamente una instrucción de restablecimiento de la memoria caché (por ejemplo, la instrucción cflush para los procesadores de la familia x86 ), la tarea de crear las condiciones necesarias para ejecutar un fragmento de código se simplifica enormemente.
El fragmento de código comienza comparando el valor de x con el valor de array1_size . La lectura del valor de array1_size en las condiciones descritas anteriormente dará como resultado una pérdida de memoria caché, lo que a su vez provocará la espera de que el valor de array1_size se obtenga de la RAM. Debido a la presencia de un mecanismo de ejecución de instrucción especulativa en el procesador, durante el tiempo de espera el procesador no estará inactivo, sino que intentará ejecutar una de las ramas del código del programa siguiendo la instrucción de rama.
Dado que los accesos anteriores al fragmento se realizaron con valores válidos de x , el predictor de bifurcación asumirá que esta vez el predicado (x < array1_size) será verdadero, y el procesador intentará ejecutar la secuencia de instrucciones correspondiente. Es decir, leerá el byte en <array1_address> + x , es decir, el byte secreto k , que, gracias a condiciones especialmente formadas, ya está en el caché. Luego, el procesador usa el valor resultante para evaluar la expresión k * 256 y lee el elemento de array2[k * 256] , lo que resultará en una segunda falla de caché, y espera a que el valor de array2[k * 256] sea recuperado de la RAM. En este momento, el valor de array1_size se obtendrá de la RAM , el procesador reconocerá el error del predictor de rama y restaurará el estado arquitectónico al momento anterior al inicio de la ejecución de la rama incorrecta del código del programa.
Sin embargo, en procesadores reales, una lectura especulativa de array2[k * 256] afectará el estado de la memoria caché del procesador, y este estado dependerá de k . Para completar el ataque, solo es necesario detectar este cambio mediante un ataque de canal lateral (el atacante debe tener acceso a la memoria caché del procesador compartido y la fuente de tiempo exacta) y, en base a ello, calcular el byte secreto k . Esto es fácil de hacer, ya que leer los elementos de array2[n * 256] será rápido para n = k y lento para otros valores.
Una sucursal indirecta puede usar más de dos direcciones para ramificar. Por ejemplo, las instrucciones del procesador de la familia x86 pueden saltar usando un valor de dirección en un registro ( jmp eax ), en la memoria ( jmp [eax] o jmp dword ptr [0xdeadc0de] ) o en la pila ( ret ). Las instrucciones de salto indirecto también se encuentran en ARM ( mov pc, r14 ), MIPS ( jr $ra ), SPARC ( jmpl %o7 ), RISC-V ( jarl x0,x1,0 ) y muchos otros.
Si la determinación de una dirección de rama indirecta se retrasa debido a una falta de memoria caché, y el predictor de rama indirecta se "entrena" con direcciones especialmente elegidas, puede ocurrir la ejecución especulativa de instrucciones en la dirección proporcionada por el atacante. Comandos que de otro modo nunca se habrían ejecutado. Si tal actuación deja efectos secundarios medibles , entonces su uso se convierte en una poderosa herramienta en manos del atacante.
Actualmente, no existen tecnologías de software preparadas para proteger contra el ataque de Spectre, aunque se está trabajando [10] . Según un sitio web dedicado a promover el ataque, "no es tan fácil de solucionar y (el error) nos perseguirá durante mucho tiempo".
Una solución de software puede incluir volver a compilar el software utilizando nuevos compiladores para reemplazar las secuencias de código de máquina vulnerables (el llamado mecanismo "retpoline", implementado en GCC y Clang / LLVM ) [11] .
Los fabricantes de procesadores han propuesto varias correcciones, algunas que requieren actualizaciones del microcódigo del procesador, otras que requieren que se agreguen nuevas instrucciones a los procesadores futuros. Las correcciones deben combinarse con la recompilación del software [11] .
En las primeras versiones del aviso de Spectre CVE, el CERT sugirió reemplazar los procesadores como respuesta a la vulnerabilidad: “La vulnerabilidad es causada por elecciones en el diseño del microprocesador. La eliminación completa de la vulnerabilidad requiere el reemplazo de los microprocesadores afectados”. Sin embargo, en textos posteriores ya no se menciona esta versión de la corrección [11] .