Cómo poner una puerta trasera en SSH
Víctor Manuel Jáquez Leal¡Qué idea más apetitosa! Poner una puerta trasera en OpenSSH, así, una vez instalado en todos los servidores de mundo, tener acceso ilimitado a todos ellos. Valdría millones, aunque durara sólo un parpadeo. El problema es cómo hacerlo, ya que OpenSSH es uno de los proyectos de software libre más auditados del mundo.
El camino sería exageradamente intrincado y de soslayo. Y alguien lo ha intentado.
Esta es una historia que surgió hace unas horas. Un programador del motor de
base de datos PostgreSQL, actualmente trabajando para Microsoft, descubrió,
mientras hacía mediciones de desempeño del motor de base de datos con Debian
inestable, que el servicio de ssh
tomaba más tiempo de lo usual:
Descubrió, entonces, que la librería de compresión liblzma
, usada por
xz-utils, intervenía, extrañamente, durante el proceso
de autenticación por SSH.
Pero OpenSSH no liga automáticamente liblzma
. Sin embargo, la mayoría de las
grandes distribuciones habilitan en soporte de systemd
para el control del servicio de ssh
, que, a su vez, liga a liblzma
. ¡Menuda
transitividad!
La secuencia que activa la puerta trasera sería más o menos así:
Cuando se ejecuta sshd
, durante su inicialización, se ejecutan las funciones
crc32_resolve()
y crc64_resolve()
. Pero estas funciones son
indirectas, es decir, pueden
existir diferentes implementaciones de las mismas, y el enlazador escogerá la
mejor de acuerdo al resultado de otra función. Pues bien, puesto que los
símbolos de la liberaría liblzma
son resueltos antes que todos, la liblzma
maliciosa ofrecerá otras implementaciones para estas funciones que, al intentar
seleccionarlas, se ejecutarán, aunque sean desechadas inmediatamente después.
Sin embargo, ya habrán logrado su cometido: reemplazar, en el enlazador, la
función RSA_public_decrypt
. Esta función es el punto de entrada, de bajo
nivel, de la librería
OpenSSL,
cuya uso es descifrar mensajes, utilizando la llave pública del emisor. Es usada
por OpenSSH cuando acepta llaves cifradas con el algoritmo
RSA. A este momento no se sabe a ciencia
cierta qué hace, con exactitud, el código binario que reemplaza a
RSA_public_decrypt
, pero se supone que si la llave tiene cierto patrón,
permitirá algo en sistema.
¿Pero cómo demonios metieron ese código binario en liblzma
inadvertidamente?
Toda una película de acción, aunque al final es la misma historia de
log4j: un proyecto de software libre, extremadamente
exitoso y usado, pero mantenido por una sola persona sobre
explotada.
A principios del 2021, cuando las cuarentenas por la pandemia del COVID19 estaban activas en todo el mundo, el autor de la librería estaba pasando por problemas de salud mental, como muchísima gente, y su productividad era baja, como se podía esperar. Justo cuando aparece un tal Jia Tan con parches interesantes para el proyecto; a la vez, un tal Jigar Kumar presiona, por la lista de correos, al mantenedor de XZ Utils para que integre los parches de Jia Tan, a la vez que insiste en que, si no puede con el mantenimiento del proyecto, distribuya la carga de trabajo con otros. En un baile de ingeniería social tan coordinado y sostenido que el mantenedor, en enero del 2023, otorgó a Jia Tan los permisos para integrar parches y de liberar nuevas versiones.
A mediados del 2023, Jia Tan, integró la implementación de una función indirecta para crc64_resolve, supuestamente contribuida por un tal Hans Jansen (quien carecía de ninguna otra contribución en GitHub), para facilitar las ejecución de pruebas de unidad. Sin embargo, estaba ya, en potencia, la posibilidad de ejecutarse en producción.
Sin embargo, revisando el código en el repositorio del proyecto, no se encuentra, por ningún lado, el código malvado que reemplaza la función RSA. ¿Cómo diablos lo hace?
Primero, hay otro cambio realizado por Jia Tan y que después descubrieron su verdadero propósito, que agrega archivos comprimidos, supuestamente corruptos, para pruebas unitarias: Tests: Update two test files. No obstante, ninguno de estos dos archivos son procesados en las pruebas unitarias.
Podemos sospechar que estos archivos contienen el código binario que reemplazará las llamadas RSA, pero ¿cómo lo enganchan al código que se ejecutará?
La respuesta está en los archivos tar
de las versiones liberadas de XZ Utils
5.6.0 y 5.6.1. Ambos construidos y firmados por Jia Tan.
Como muchos proyectos, XZ Utils usa autotools
como mecanismo de compilación y
distribución. Y autotools
esta compuesto por una serie de guiones en BASH,
que, por lo general, no se almacena en el repositorio debido a que son
auto-generados. Pues estas dos versiones afectadas, tienen modificados los
guiones de compilación de autotools
. Los guiones están ofuscados de manera muy
inteligente, pero básicamente extraen ciertas porciones de los archivos
comprimidos de pruebas, que había integrado anteriormente, y reconstruyen un
archivo con código compilado que se enlazaría en la librería. Et voilà! Allí
suponemos que esta la implementación maliciosa tanto de crc64_resolve
como de
RSA_public_decrypt
. ¿Qué hace exactamente? Aún no se sabe, pero se sospecha
que ejecuta código de manera
remota
y contiene mecanismos para evitar ser detectado y
analizado.
Pero allí no para la historia.
Como muchas librerías de software libre, que son muy utilizadas, XZ Utils es analizado continuamente por Google con pruebas de entradas inválidas, y el mismo Jia Tan envió un parche a OSS-Fuzz para deshabilitar las funciones indirectas cuando se hace pruebas de entradas inválidas, que fue integrado.
Posteriormente comenzaron los procedimientos tanto en Debian (por el infame Hans Jansen), como en Ubuntu (por el mismo Jia Tan) y Fedora, para que las versiones maliciosas de XZ Utils fueran integradas en las distribuciones estables.
Muchas lecciones se pueden sacar de estos eventos. Pero para mi, es otra confirmación de que estamos en un mundo en guerra y que el software libre es un campo de batalla más. Otra, como ya apuntó Randall hace años en su cartón, que el ecosistema entero del software libre está sostenido en hombros de personas sin ningún tipo de sostén ni apoyo social. Esta vez nos salvamos por un pelo, pero basta con imaginar qué otros proyectos están siendo infiltrados en este momento, sino es que ya lo están.
Referencias: