Ken Thompson Hack (1984)
Ken Thompson describió un método para corromper un compilador binario (y otro software compilado, como un script de inicio de sesión en un sistema * nix) en 1984. Tenía curiosidad por saber si la compilación moderna ha abordado esta falla de seguridad o no.
Breve descripción:
Vuelva a escribir el código del compilador para contener 2 fallas:
- Al compilar su propio binario, el compilador debe compilar estos defectos
- Al compilar algún otro código preseleccionado (función de inicio de sesión) debe compilar alguna puerta trasera arbitraria
Por lo tanto, el compilador funciona normalmente: cuando compila un script de inicio de sesión o similar, puede crear una puerta trasera de seguridad, y cuando compila versiones más nuevas de sí mismo en el futuro, conserva las fallas anteriores, y las fallas solo existirán en el compilador binario, por lo que son extremadamente difíciles de detectar.
Preguntas:
No pude encontrar ninguna respuesta a estas en la web:
- ¿Cómo se relaciona esto con la compilación justo a tiempo?
- ¿Se compilan funciones como el programa que maneja los inicios de sesión en un sistema * nix cuando se ejecutan?
- ¿Sigue siendo una amenaza válida o ha habido avances en la seguridad de la compilación desde 1984 que evitan que esto sea un problema importante?
- ¿Esto afecta a todos los idiomas?
¿Por qué quiero saberlo?
Me encontré con esto mientras hacía algunos deberes, y me pareció interesante, pero me falta el fondo para comprender de manera concreta si se trata de un problema actual o un problema resuelto.
Respuestas:
Este truco tiene que ser entendido en contexto. Fue publicado en un momento y en una cultura en la que Unix se ejecutaba en todo tipo de hardware diferente era el sistema dominante.
Lo que hizo que el ataque fuera tan aterrador fue que el compilador de C era la pieza central de software para estos sistemas. Casi todo en el sistema pasó por el compilador cuando se instaló por primera vez (las distribuciones binarias eran raras debido al hardware heterogéneo). Todos compilamos cosas todo el tiempo. La gente inspeccionaba regularmente el código fuente (a menudo tenían que hacer ajustes para que compilara), por lo que hacer que el compilador inyectara puertas traseras parecía ser una especie de escenario de "crimen perfecto" en el que no podía ser atrapado.
Hoy en día, el hardware es mucho más compatible y, por lo tanto, los compiladores tienen un papel mucho más pequeño en el funcionamiento diario de un sistema. Un compilador comprometido ya no es el escenario más aterrador: los rootkits y un BIOS comprometido son aún más difíciles de detectar y eliminar.
fuente
El propósito de ese discurso no era resaltar una vulnerabilidad que necesita ser abordada, o incluso proponer una vulnerabilidad teórica de la que debemos ser conscientes.
El propósito era que, cuando se trata de seguridad, nos gustaría no tener que confiar en nadie, pero desafortunadamente eso es imposible. Siempre debes confiar en alguien (de ahí el título: "Reflexiones sobre la confianza en la confianza")
Incluso si eres del tipo paranoico que encripta su disco duro de escritorio y se niega a ejecutar cualquier software que no hayas compilado, aún debes confiar en tu sistema operativo. E incluso si compila el sistema operativo usted mismo, aún necesita confiar en el compilador que utilizó. E incluso si compila su propio compilador, ¡ aún necesita confiar en ese compilador! ¡Y eso sin mencionar a los fabricantes de hardware!
Simplemente no puede salirse con la suya confiando en nadie . Ese era el punto que estaba tratando de cruzar.
fuente
No
El ataque, como se describió originalmente, nunca fue una amenaza. Si bien un compilador teóricamente podría hacer esto, en realidad llevar a cabo el ataque requeriría programar el compilador para
Esto implica descubrir cómo funciona el compilador a partir de su código fuente, para poder modificarlo sin romperse.
Por ejemplo, imagine que el formato de enlace almacena las longitudes de datos o el desplazamiento del código de máquina compilado en algún lugar del ejecutable. El compilador tendría que averiguar por sí mismo cuáles de estos deben actualizarse y dónde, al insertar la carga útil de explotación. Las versiones posteriores del compilador (versión inocuo) pueden cambiar arbitrariamente este formato, por lo que el código de explotación necesitaría comprender estos conceptos.
Esta es una programación autodirigida de alto nivel, un problema de IA difícil (la última vez que lo revisé, el estado de la técnica estaba generando código que está prácticamente determinado por sus tipos). Mira: pocos humanos pueden hacer esto; Tendría que aprender el lenguaje de programación y comprender primero el código base.
Incluso si se resuelve el problema de IA, las personas notarían si compilar su pequeño compilador da como resultado un binario con una enorme biblioteca de IA vinculada a él.
Ataque análogo: confianza de arranque
Sin embargo, una generalización del ataque es relevante. El problema básico es que su cadena de confianza tiene que comenzar en alguna parte, y en muchos dominios su origen podría subvertir toda la cadena de una manera difícil de detectar.
Un ejemplo que se podría sacar fácilmente en la vida real.
Su sistema operativo, por ejemplo, Ubuntu Linux, garantiza la seguridad (integridad) de las actualizaciones al comparar los paquetes de actualización descargados con la clave de firma del repositorio (utilizando criptografía de clave pública). Pero esto solo garantiza la autenticidad de las actualizaciones si puede probar que la clave de firma es propiedad de una fuente legítima.
¿De dónde sacaste la clave de firma? Cuando descargó por primera vez la distribución del sistema operativo.
Debe confiar en que la fuente de su cadena de confianza, esta clave de firma, no es mala.
Cualquiera que pueda MITM la conexión a Internet entre usted y el servidor de descarga de Ubuntu, este podría ser su ISP, un gobierno que controla el acceso a Internet (por ejemplo, China) o el proveedor de alojamiento de Ubuntu, podría haber secuestrado este proceso:
A partir de entonces, obtendrá sus actualizaciones de forma segura desde el servidor del atacante. Las actualizaciones se ejecutan como root, por lo que el atacante tiene control total.
Puede evitar el ataque asegurándose de que el original sea auténtico. Pero esto requiere que valide la imagen de CD descargada utilizando un hash ( pocas personas realmente hacen esto ), y el hash debe descargarse de forma segura, por ejemplo, a través de HTTPS. Y si su atacante puede agregar un certificado en su computadora (común en un entorno corporativo) o controla una autoridad de certificación (por ejemplo, China), incluso HTTPS no proporciona protección.
fuente
Primero, mi escrito favorito de este truco se llama Strange Loops .
Este truco en particular ciertamente podría (*) hacerse hoy en cualquiera de los principales proyectos de SO de código abierto, particularmente Linux, * BSD y similares. Esperaría que funcionara casi de manera idéntica. Por ejemplo, descarga una copia de FreeBSD que tiene un compilador explotado para modificar openssh. A partir de ese momento, cada vez que actualice openssh o el compilador por fuente, continuará con el problema. Suponiendo que el atacante ha explotado el sistema utilizado para empaquetar FreeBSD en primer lugar (probablemente, dado que la imagen en sí está dañada o el atacante es el empacador), cada vez que el sistema reconstruya los binarios de FreeBSD, reinyectará el problema. Hay muchas maneras de que este ataque falle, pero no son fundamentalmente diferentes de cómo el ataque de Ken podría haber fallado (**). El mundo realmente no ha cambiado tanto.
Por supuesto, sus propietarios podrían inyectar ataques similares (o más fácilmente) en sistemas como Java, el SDK de iOS, Windows o cualquier otro sistema. Ciertos tipos de fallas de seguridad incluso se pueden diseñar en el hardware (particularmente debilitando la generación de números aleatorios).
(*) Pero por "ciertamente" quiero decir "en principio". ¿Debería esperar que este tipo de agujero exista en algún sistema en particular? No. Lo consideraría bastante improbable por varias razones prácticas. Con el tiempo, a medida que el código cambia y cambia, aumenta la probabilidad de que este tipo de pirateo cause errores extraños. Y eso aumenta la probabilidad de que se descubra. Las puertas traseras menos ingeniosas requerirían conspiraciones para mantenerse. Por supuesto, sabemos con certeza que se han instalado puertas traseras de "intercepción legal" en varios sistemas de telecomunicaciones y redes, por lo que en muchos casos este tipo de intrusión es innecesario. El truco se instala abiertamente.
Así que siempre, defensa en profundidad.
(**) Suponiendo que el ataque de Ken haya existido realmente. Él solo discutió cómo podría hacerse. No dijo que realmente lo hizo hasta donde yo sé.
fuente
¿Esto afecta a todos los idiomas?
Este ataque afecta principalmente a los idiomas que se autohospedan. Es en los idiomas donde el compilador está escrito en el idioma mismo. C, Squeak Smalltalk y el intérprete PyPy Python se verían afectados por esto. Perl, JavaScript y el intérprete CPython Python no lo harían.
¿Cómo se relaciona esto con la compilación justo a tiempo?
No mucho. Es la naturaleza de alojamiento propio del compilador lo que permite ocultar el hack. No conozco ningún compilador JIT de alojamiento propio. (¿Quizás LLVM?)
¿Se compilan funciones como el programa que maneja los inicios de sesión en un sistema * nix cuando se ejecutan?
No Usualmente. Pero la pregunta no es cuándo se compila, sino por qué compilador . Si el programa de inicio de sesión es compilado por un compilador contaminado, será contaminado. Si es compilado por un compilador limpio, estará limpio.
¿Sigue siendo una amenaza válida o ha habido avances en la seguridad de la compilación desde 1984 que evitan que esto sea un problema importante?
Esto sigue siendo una amenaza teórica, pero no es muy probable.
Una cosa que podría hacer para mitigarlo es usar múltiples compiladores. Por ejemplo, un compilador LLVM que es, en sí, compilado por GCC no pasará por una puerta trasera. Del mismo modo, un GCC compilado por LLVM no pasará por una puerta trasera. Entonces, si le preocupa este tipo de ataque, entonces podría compilar su compilador con otra generación de compiladores. Eso significa que el hacker maligno (¿en el proveedor de su sistema operativo?) Tendrá que contaminar a ambos compiladores para reconocerse entre sí; Un problema mucho más difícil.
fuente
Hay una posibilidad teórica de que esto suceda. Sin embargo, hay una forma de verificar si un compilador específico (con el código fuente disponible) se ha visto comprometido, a través de la doble compilación Diverse de David A. Wheeler .
Básicamente, use tanto el compilador sospechoso como otro compilador desarrollado independientemente para compilar la fuente del compilador sospechoso. Esto le da SC SC y SC T . Ahora, compile la fuente sospechosa utilizando ambos binarios. Si los binarios resultantes son idénticos (con excepción de una variedad de cosas que pueden variar legítimamente, como varias marcas de tiempo), el compilador sospechoso no estaba abusando de la confianza.
fuente
Como ataque específico, es una amenaza tan grande como siempre, que prácticamente no es una amenaza.
No estoy seguro de lo que quieres decir con eso. ¿Un JITter es inmune a esto? No. ¿Es más vulnerable? Realmente no. Como desarrollador, SU aplicación es más vulnerable simplemente porque no puede validar que no se haya hecho. Tenga en cuenta que su aplicación aún no desarrollada es básicamente inmune a esta y a todas las variaciones prácticas, solo tiene que preocuparse por un compilador que sea más nuevo que su código.
Eso no es realmente relevante.
No existe una seguridad real de compilación, y no puede existir. Ese fue realmente el punto de su charla, que en algún momento tienes que confiar en alguien.
Si. Fundamentalmente, en algún momento u otro, sus instrucciones tienen que convertirse en algo que la computadora ejecute, y esa traducción puede hacerse incorrectamente.
fuente
David Wheeler tiene un buen artículo: http://www.dwheeler.com/trusting-trust/
Yo estoy más preocupado por los ataques de hardware. Creo que necesitamos una cadena de herramientas de diseño totalmente VLSI con código fuente FLOSS, que podamos modificar y compilar nosotros mismos, que nos permita construir un microprocesador que no tenga puertas traseras insertadas por las herramientas. Las herramientas también deberían permitirnos comprender el propósito de cualquier transistor en el chip. Luego podríamos abrir una muestra de los chips terminados e inspeccionarlos con un microscopio, asegurándonos de que tuvieran el mismo circuito que las herramientas dijeron que se suponía que debían tener.
fuente
Los sistemas en los que los usuarios finales tienen acceso al código fuente son aquellos en los que tendrías que ocultar este tipo de ataque. Esos serían sistemas de código abierto en el mundo de hoy. El problema es que, aunque existe una dependencia de un único compilador para todos los sistemas Linux, el ataque tendría que llegar a los servidores de compilación para todas las principales distribuciones de Linux. Como esos no descargan los binarios del compilador directamente para cada versión del compilador, la fuente del ataque debería haber estado en sus servidores de compilación en al menos una versión anterior del compilador. O eso o la primera versión del compilador que descargaron como binario tendría que haber sido comprometida.
fuente
Si uno tiene código fuente para un compilador / sistema de compilación cuya salida no debe depender de otra cosa que no sea el contenido de los archivos fuente suministrados, y si tiene varios otros compiladores y sabe que no todos contienen el mismo truco del compilador, uno puede asegúrese de obtener un archivo ejecutable que dependa únicamente del código fuente.
Supongamos que uno tiene un código fuente para un paquete compilador / enlazador (digamos Groucho Suite) escrito de tal manera que su salida no dependerá de comportamientos no especificados, ni de nada más que el contenido de los archivos fuente de entrada, y uno compila / vincula ese código en una variedad de compiladores / paquetes vinculadores producidos de forma independiente (por ejemplo, Harpo Suite, Chico suite y Zeppo Suite), produciendo un conjunto diferente de ejecutables para cada uno (llámelos G-Harpo, G-Chico y G-Zeppo). No sería inesperado que estos ejecutables contengan diferentes secuencias de instrucciones, pero deberían ser funcionalmente idénticos. Sin embargo, probar que son funcionalmente idénticos en todos los casos probablemente sería un problema insoluble.
Afortunadamente, tal prueba no será necesaria si uno solo usa los ejecutables resultantes para un solo propósito: compilar nuevamente la suite Groucho. Si uno compila la suite Groucho usando G-Harpo (produciendo GG-Harpo), G-Chico (GG-Chico) y G-Zeppo (GG-Zeppo), entonces los tres archivos resultantes, GG-Harpo, GG-Chico y GG-Zeppo, todos deberían ser idénticos byte por byte. Si los archivos coinciden, eso implicaría que cualquier "virus compilador" que exista en cualquiera de ellos debe existir de manera idéntica en todos ellos (dado que los tres archivos son idénticos byte por byte, no hay forma de que sus comportamientos puedan diferir de alguna manera camino).
Dependiendo de la edad y el linaje de los otros compiladores, puede ser posible asegurar que tal virus no pueda existir en ellos. Por ejemplo, si se usa un Macintosh antiguo para alimentar un compilador que se escribió desde cero en 2007 a través de una versión de MPW que se escribió en la década de 1980, los compiladores de la década de 1980 no sabrían dónde insertar un virus en el compilador de 2007. Es posible que un compilador de hoy haga un análisis de código lo suficientemente sofisticado como para descubrirlo, pero el nivel de cómputo requerido para dicho análisis excedería con creces el nivel de cómputo requerido para simplemente compilar el código, y no podría pasar desapercibido. en un mercado donde la velocidad de compilación era un importante punto de venta.
Yo diría que si uno está trabajando con herramientas de compilación donde los bytes en un archivo ejecutable a ser producido no deberían depender de otra manera que no sea el contenido de los archivos fuente enviados, es posible lograr una inmunidad razonablemente buena de un Thompson virus de estilo. Desafortunadamente, por alguna razón, el no determinismo en la compilación parece considerarse normal en algunos entornos. Reconozco que en un sistema con múltiples CPU puede ser posible que un compilador se ejecute más rápido si se permite que ciertos aspectos de la generación de código varíen dependiendo de cuál de los dos subprocesos termine primero un trabajo.
Por otro lado, no estoy seguro de ver alguna razón por la cual los compiladores / enlazadores no deberían proporcionar un modo de "salida canónica" donde la salida depende solo de los archivos de origen y una "fecha de compilación" que el usuario puede anular. . Incluso si la compilación de código en tal modo tomara el doble de tiempo que la compilación normal, sugeriría que sería de gran valor poder recrear cualquier "compilación de lanzamiento", byte por byte, completamente a partir de materiales fuente, incluso si eso significara que las versiones de lanzamiento tardarían más que las "versiones normales".
fuente