El pasado 29 de marzo se detectó una vulnerabilidad crítica (CVE-2024-3094, con un score de 10 sobre 10 según el NIST) en el componente XZ Utils, presente en muchas distribuciones Unix. Esta vulnerabilidad permite al atacante modificar el flujo de descifrado en el servidor SSH para permitir a ciertos atacantes, con una clave privada específica, ejecutar código malintencionado a través de SSH antes del paso de autenticación.
Tal vez hasta aquí parezca algo común, una vulnerabilidad más detectada en un componente. Nada más lejos de la realidad. Durante las siguientes líneas de este post iremos desgranando como un atacante vulnera el ciclo de vida del software de diferentes proyectos open-source hasta conseguir introducir una vulnerabilidad que le permita controlar cualquier sistema bajo dos condiciones habituales: tener un servicio SSH y tener instalada la librería de compresión XZ.
Camino al éxito
Todo comienza en 2021, cuando se crea el usuario JiaT75 en GitHub. Este será el protagonista de nuestra historia, ya que será el encargado de preparar un plan de dos años para acabar insertando esta vulnerabilidad en el código fuente de la librería XZ Utils.
En febrero de 2022, JiaT75 hace su primera contribución al proyecto open-source de XZ Utils con un cambio menor, y no sería hasta finales de junio del año pasado donde haría una primera contribución con el objetivo de establecer la base del ataque: añadir el soporte para la implementación de ‘ifunc‘ o ‘GNU indirect function support’. Básicamente, ‘ifunc’ permite a un desarrollador crear múltiples implementaciones de una función en GNU.
A inicios de julio de 2023, el usuario JiaT75 abre una Pull Request en otro proyecto, ‘ozz-fuzz’, que era la librería encargada de hacer el fuzz testing para XZ Utils. El cambio es claro, deshabilitar que ‘ozz-fuzz’ detecte el ‘ifunc’ fuzzing para que su modificación maliciosa en XZ Utils no sea detectada.
En este punto, damos un salto hasta febrero de 2024, el momento donde JiaT75 consigue incluir su payload en el componente. El 15 de febrero, JiaT75 consigue incluir en el .gitignore el fichero ‘build-to-host.m4‘, que será el script que se generará en compilación y que contiene las macros maliciosas que instalan la backdoor en la víctima. El 23 de febrero, JiaT75 sube dos archivos aparentemente de pruebas, pero que realmente contienen el código malicioso ofuscado. En este momento, la librería ya incorpora el backdoor. Finalmente, se despliega la versión vulnerable 5.6.0 el 24 de febrero, y el propio usuario JiaT75 mejora los binarios maliciosos y se despliega la versión 5.6.1. JiaT75 puede explotar la vulnerabilidad en los sistemas que tengan SSH y el componente en las versiones 5.6.X.
Salvados por unos milisegundos
Ante este plan tan elaborado y los diferentes errores de seguridad en el ciclo de vida del software que detallaremos a continuación, parece complicado que un usuario se dé cuenta de que está siendo atacado. Pues existe un héroe sin capa que, con suerte, habilidad y curiosidad, encontró un comportamiento extraño cuando hacía una prueba de tiempos con una base de datos PostgreSQL.
Andres Freud, desarrollador de prestigio, estaba tratando de depurar el rendimiento de su PostgreSQL en su Debian cuando notó que su proceso SSH estaba consumiendo demasiados recursos y sus accesos eran demasiado lentos (500 ms más de lo habitual), por lo que decidió tirar del hilo hasta averiguar todo el entramado de JiaT75.
Aunque el descubrimiento es brillante, la consecuencia es que los paquetes de la librería XZ siguen presentes hoy en día y son ampliamente usados en múltiples sistemas operativos Linux (Fedora, Debian, Alpine, Kali, OpenSUSE y ArchLinux). Además, mucho software para Linux (en especial software libre) es comprimido con esta librería.
Para protegerte ante este ataque, recomendamos revisar tus sistemas Linux (en especial, los anteriormente mencionados) y verificar que no usen el paquete xz-utils en la versión 5.6.0 o 5.6.1. En caso de utilizarlo, seguir las recomendaciones de actualización de la distro o, en su defecto, desinstalar o bajar la versión del propio paquete.
En caso de no ser posible seguir las mitigaciones anteriores, el impacto disminuye al no utilizar SSH o no exponerlo públicamente.
Consecuencias de un ciclo de vida del software inseguro
Detrás de un problema de seguridad existen múltiples enseñanzas. En este caso, desde nuestro blog nos gustaría reflexionar acerca de la importancia que tiene implantar un ciclo de vida del software seguro.
Uno de los grandes errores que permitió al atacante introducir código malicioso en el binario pasó por no tener una serie de controles de seguridad correctos en el ciclo de vida del proyecto. La ausencia tanto de controles de seguridad en integración continua como de validaciones de código a través de linters y por parte de los propietarios del proyecto, acompañados de una alta confianza en los contribuidores hacen el caldo de cultivo perfecto para que un atacante pueda generar este tipo de ataques.
Desde Ciberso impulsamos la seguridad en todos los puntos de vida del software, desde las etapas más tempranas hasta su puesta en producción, evitando así este tipo de ataques. A través de nuestro equipo de DevSecOps, automatizamos los controles de seguridad para fortalecer la seguridad del software y detectamos las vulnerabilidades antes de pasar a producción, reduciendo el riesgo y ahorrando costes a la compañía.
Además, acompañados del equipo de auditoría, detectamos las vulnerabilidades en componentes de terceros de forma ágil y rápida, a través de distintos tipos de analizadores y procesos en las distintas etapas de desarrollo. Esto permite que, si un equipo usa un componente vulnerable (por ejemplo, una imagen Docker que contenga la vulnerabilidad ‘“’XZ backdoor’”’), se le avise tanto si se ha descubierto y lo tiene ya en producción como en el momento en que se esté desarrollando, previniendo así la inclusión de la vulnerabilidad en la aplicación.