- Andres Freund, de Microsoft, estaba investigando anomalías de rendimiento en SSH en un sistema Debian cuando descubrió un backdoor insertado en xz Utils, usado en casi todos los entornos tipo Linux/Unix
- El código malicioso fue incluido en xz Utils 5.6.0 y 5.6.1, y estaba diseñado para permitir que un atacante con una clave privada específica ocultara código en un certificado de inicio de sesión SSH y lo ejecutara en dispositivos con el backdoor
- El atacante intentó usar tarballs de release en lugar del código fuente de GitHub, archivos de prueba, scripts de build y IFUNC de glibc para hookear la rutina de autenticación de sshd a través de liblzma
- El backdoor verificaba condiciones de build como amd64/x86_64, glibc y paquetes de Debian o derivados de Red Hat; estuvo incluido en Fedora Rawhide, Fedora 41, Debian testing/unstable/experimental, openSUSE Tumbleweed/MicroOS y releases de Kali Linux durante ciertos períodos
- Una persona de desarrollador llamada Jia Tan participó durante años en el mantenimiento de xz Utils; el incidente se rastrea como CVE-2024-3094, y aparecieron herramientas de detección y análisis como la página de verificación de Binarly y xzbot
Descubrimiento y alcance del impacto
- Andres Freund es un desarrollador e ingeniero que trabajaba en temas relacionados con PostgreSQL en Microsoft; detectó señales anómalas mientras investigaba en un sistema Debian por qué los inicios de sesión SSH consumían demasiada CPU y generaban errores de valgrind
- La causa era una actualización de xz Utils, y Freund reveló en la Open Source Security List que esa actualización era un backdoor insertado deliberadamente en el software de compresión
- xz Utils ofrece compresión y descompresión de datos sin pérdida en casi todos los sistemas operativos tipo Unix, incluido Linux, y también soporta el formato legacy .lzma
- Filippo Valsorda lo describió como “posiblemente el caso mejor ejecutado de un ataque a la cadena de suministro descrito públicamente, y un escenario de pesadilla: un upstream malicioso, competente y con privilegios de una biblioteca ampliamente usada”
Qué buscaba hacer el backdoor
- El código malicioso se agregó a xz Utils 5.6.0 y 5.6.1, cambiando la forma en que funciona el software
- El backdoor manipula sshd, el ejecutable usado para conexiones SSH remotas
- Una persona con una clave criptográfica predeterminada podía ocultar el código que quisiera en un certificado de inicio de sesión SSH, subirlo y ejecutarlo en un dispositivo con el backdoor
- No se identificó el código que llegó a subirse realmente, por lo que no se sabe qué código intentaba ejecutar el atacante
- En teoría, podría permitir casi cualquier acción, incluida la extracción de claves criptográficas o la instalación de malware
Cómo una biblioteca de compresión llegó hasta SSH
- Una biblioteca puede manipular el funcionamiento interno de los ejecutables vinculados con ella
- La implementación más representativa de sshd de OpenSSH no se vincula por defecto con liblzma
- Debian y varias distribuciones Linux agregan un parche que conecta sshd con systemd
- systemd es un programa que carga varios servicios durante el arranque
- systemd se vincula con liblzma
- Debido a esta ruta de conexión, xz Utils pudo afectar a sshd
Indicios de preparación durante años
- En 2021, un usuario llamado JiaT75 hizo su primer commit conocido en un proyecto open source
- Fue un cambio en el proyecto libarchive que reemplazó la función safe_fprint por una variante conocida desde hacía tiempo como menos segura, y nadie lo notó en ese momento
- En 2022, JiaT75 envió un parche a la lista de correo de xz Utils
- Poco después, un nuevo participante llamado Jigar Kumar presionó a Lasse Collin diciendo que no actualizaba xz Utils con suficiente frecuencia o rapidez
- Dennis Ens y otros nuevos participantes también presionaron a Collin para que incorporara más maintainers
- En enero de 2023, JiaT75 hizo su primer commit en xz Utils y se involucró más profundamente en el trabajo de xz Utils bajo el nombre Jia Tan
- Cambió el contacto de Collin en oss-fuzz por el suyo
- Solicitó un cambio para desactivar la funcionalidad IFUNC durante las pruebas de oss-fuzz, lo que impediría detectar cambios maliciosos que luego entrarían en xz Utils
- En febrero de 2024, Tan hizo commits de xz Utils 5.6.0 y 5.6.1, y esas actualizaciones implementaron el backdoor
- Después, Tan u otras personas pidieron a desarrolladores de Ubuntu, Red Hat y Debian que integraran esas actualizaciones en sus sistemas operativos
Distribuciones incluidas y condiciones de ejecución
- Según Tenable, las versiones con backdoor o actualizaciones relacionadas entraron en los siguientes releases
- Fedora Rawhide: la distribución de desarrollo de Fedora Linux
- Fedora 41
- Debian testing, unstable, experimental: desde 5.5.1alpha-0.1 hasta 5.6.1-1
- openSUSE Tumbleweed y openSUSE MicroOS: incluyeron versiones de xz con backdoor entre el 7 y el 28 de marzo
- Kali Linux: incluyó xz-utils 5.6.0-0.2 entre el 26 y el 28 de marzo
- Según el análisis de Sam James, el script malicioso verificaba si el objetivo de build era amd64/x86_64, si usaba el nombre linux-gnu, y si usaba GCC y GNU ld
- También verificaba si era un build de paquete Debian o si RPM_ARCH era x86_64
- El ataque parece haber apuntado a entornos que usan glibc en sistemas amd64 y que construyen distribuciones derivadas de Debian o Red Hat
- En ese momento no se sabía si otros sistemas eran vulnerables
Implementación y técnicas de ocultamiento
- Sam James resumió que el backdoor estaba compuesto por varios componentes
- El tarball de release distribuido por upstream no es igual al código de GitHub
- build-to-host.m4 en el tarball de release difiere mucho del upstream de GitHub
- En la carpeta tests/ del repositorio git hay archivos de prueba manipulados
tests/files/bad-3-corrupt_lzma2.xztests/files/good-large_compressed.lzma
- Un script invocado por build-to-host.m4 desempaqueta datos de prueba maliciosos y modifica el proceso de build
- IFUNC de glibc se usa para hookear o redirigir en runtime la rutina de autenticación de OpenSSH
- HD Moore confirmó que la etapa final del backdoor solo se ejecuta al compilar la biblioteca en amd64 y crear paquetes Debian o RPM
- Según los análisis de investigadores, si durante la verificación de una clave pública SSH coincidían una función de fingerprint específica y una clave pública, el contenido de la clave se descifraba con una clave precompartida antes de verificar realmente la clave pública
- El contenido descifrado se pasaba directamente a
systemcuando cumplía un formato específico - Si el fingerprint no coincidía o el contenido descifrado no cumplía el formato, se volvía a la verificación normal de claves, y al usuario le resultaba difícil notar la diferencia
Cadena de ejecución descrita por Akamai
- Los investigadores de Akamai consideran que el backdoor se incluyó solo en los releases de tarball de código fuente, y no en el repositorio GitHub de xz, para intentar evitar su detección
- El backdoor se construyó a lo largo de varios commits
- Durante el proceso de build, usaba IFUNC para permitir que el malware interceptara la función de resolución de símbolos
- Ocultaba un objeto compartido ofuscado dentro de archivos de prueba
- Ejecutaba un script que extraía el objeto compartido durante el build de la biblioteca
- Desactivaba landlocking, una función de seguridad que restringe los privilegios de los procesos
- La cadena de ejecución continúa en varias etapas
build-to-host.m4se ejecuta durante el build de la biblioteca y decodificabad-3-corrupt_lzma2.xzcomo un script bash- Ese script bash decodifica
good-large_compressed.lzmade una forma más compleja y crea otro script - Ese script extrae el objeto compartido
liblzma_la-crc64-fast.oy lo agrega al proceso de compilación de liblzma
- Este objeto compartido se compila dentro de liblzma y reemplaza el proceso normal de resolución de nombres de funciones
- La biblioteca maliciosa puede cambiar el puntero a la función
RSA_public_decryptde OpenSSH por una función maliciosa creada por ella - Según el análisis de Filippo Valsorda, esa función maliciosa extrae comandos del certificado del cliente de autenticación, confirma que se trata del actor de amenaza y luego los pasa a
system()para lograr ejecución remota de código antes de la autenticación
Jia Tan y preguntas pendientes
- Se sabe muy poco sobre Jia Tan
- Esta persona de desarrollador participó durante los últimos años en decenas de otros proyectos de software open source
- No se sabe si detrás del nombre de usuario Jia Tan había una persona real o si era una identidad totalmente fabricada
- Se puede encontrar más análisis técnico en el hilo de Bluesky de Filippo Valsorda, el análisis de Kevin Beaumont y el material divulgado por Freund el viernes
CVE y herramientas de verificación
- El identificador de seguimiento de esta vulnerabilidad es CVE-2024-3094
- La página xz.fail de Binarly detecta implementaciones IFUNC y se basa en análisis de comportamiento
- También puede detectar automáticamente invariantes si se insertan backdoors similares en otros lugares
- xzbot ofrece funciones de análisis y experimentación
- honeypot: servidor vulnerable falso para detectar intentos de explotación
- ed448 patch: parche para
liblzma.soque permite usar una clave pública ED448 propia - backdoor format: formato del payload del backdoor
- backdoor demo: CLI que dispara RCE asumiendo que se conoce la clave privada ED448
1 comentarios
Opiniones de Hacker News
El objetivo era facilitar la escritura de pruebas para XZ usando un framework de pruebas estandarizado
Jia escribió el 2022-06-17 que todavía había muchas funciones sin probar, y que esto ayudaría a aumentar las pruebas para la estabilidad del proyecto a largo plazo
O sea, era un cambio que se venía preparando desde hace mucho
Parece que no se ha hablado mucho del mecanismo de enlazado que permitió que la lib se metiera en
RSA_public_decryptSe habla mucho de separación de procesos y cosas así, pero poco sobre ese redireccionamiento de llamadas a funciones
Quizá se podría crear un modelo donde componentes críticos como el código que entra por SSH se enlacen a bibliotecas con un esquema de confianza por capas, algo como: “confío en esto donde lo llamo, pero no permito que se meta por su cuenta en otros puntos de llamada”
Eso puede provocar la reacción automática de “seguridad por oscuridad”, diciendo que no sirve porque se puede rodear, pero aun así podría verse como una reducción de la superficie de ataque
Pero en un sistema operativo hay varios contextos de seguridad superpuestos. En el caso del backdoor de XZ, se trata sobre todo de seguridad basada en capacidades a nivel de módulo, pero también existen capacidades a nivel de programa, aislamiento a nivel de memoria (paginación), aislamiento a nivel de microarquitectura, etc.
Hacer que todos esos elementos funcionen juntos y además rindan bien es bastante difícil, y que los sistemas tipo Unix migren a ese modelo parece prácticamente imposible porque habría que reemplazar el concepto mismo de proceso
Eso rompió las pruebas de XZ, y de repente aparecieron cuentas presionando para desactivar esas pruebas, lo que terminó haciendo posible el exploit
./configure && make) para parchear el ifunc resolver y hacer que llame al objeto malicioso suministradoEl archivo objeto comprometido se enlaza con la bandera del linker
now, así que el ifunc se resuelve de inmediato al cargar la biblioteca, y en ese momento la tabla de enlazado del proceso todavía está en estado escribible, por lo que se invoca el resolver parcheadoJustamente esa parte es la clave, porque al cargarse la biblioteca se podía simplemente interceptar en memoria la función
RSA_public_decryptUn artículo que analiza muy bien el proceso de inyección del backdoor: https://research.swtch.com/xz-script
dlopencon bibliotecas de compresión: https://github.com/systemd/systemd/pull/31550En ese sentido, es una forma de enlazado más segura
ltracevigile los símbolos que se están llamandoNo entiendo por qué dicen que “casi” infectó a todo el mundo
Al menos 3 distribuciones de Linux bastante populares, Arch, Gentoo y openSUSE Tumbleweed, distribuyeron el backdoor durante varias semanas, y en Tumbleweed definitivamente estaba funcionando
Fueron varias semanas con SSH backdooreado, así que decir “casi” se queda corto
Solo funcionaba en objetivos deb/rpm, así que en la práctica se detuvo antes de llegar a producción
Eso no quiere decir que esté bien; si no lo hubieran detectado hasta que entrara en RHEL o en Debian/Ubuntu stable, bancos y hospitales habrían terminado recibiendo alertas
Si hubiera sido ejecución remota de código antes de la autenticación, salvo en lugares con redes fuertemente restringidas y muy buen registro de flujos, habría sido difícil decir que “no se vieron afectados”
Si comparas
liblzma.so.5.6.1dexz-5.6.1-1yxz-5.6.1-2concmp -l, solo hay diferencias muy pequeñasDa la impresión de que no sabían eso antes de redactar el aviso
https://github.com/QubesOS/qubes-issues/issues/9067#issuecom...
Aunque no sé cómo conseguir datos que respalden eso
Apostaría 101 dólares a que en los próximos 12 meses aparece algo parecido en un entorno real
Porque los mantenedores van a empezar a revisar con sospecha los commits pasados de los demás
Basta con mirar bases de código importantes pero poco conocidas y con muy pocos mantenedores
Si yo hubiera tenido algo así y funcionara bien, probablemente después lo habría “descubierto” con otra cuenta para hacer que lo corrigieran
Saqué varias conclusiones personales de todo esto
Primero, los tarballs de distribución del código fuente que contienen código distinto al del repositorio fuente son una mala idea y habría que alejarse de ellos. Otro gran ataque a la cadena de suministro, event-stream, también aprovechó algo parecido
Como resultado, los artefactos generados automáticamente deberían quedar siempre commiteados
Segundo, los artefactos generados automáticamente que todo el mundo se salta en la revisión de código dándole a Page Down son un problema. Si esos archivos están en el repositorio, también debería haber pruebas automáticas para verificar que nadie los manipuló, y para evitar que queden archivos generados obsoletos abandonados
Tercero, como consecuencia de 1 y 2, autotools es malo y la cultura de autotools también es mala
Cuarto, libsystemd es un problema para el ecosistema. Decir esto hace que te traten como hater de systemd, pero es grande, complejo, tiene muchas dependencias y la mayoría de los programas solo usa una parte muy pequeña. Fomentar que todos los servicios dependan de esto para las notificaciones de inicialización es una locura
Quinto, existe una cultura de que reutilizar código siempre es bueno y que está bien depender de una librería grande por una función pequeña, pero no es cierto. Las dependencias son una carga de mantenimiento y un riesgo de seguridad, y hay que sopesarlas frente a la funcionalidad que aportan
Sexto, también es un problema que los mantenedores de distribuciones apliquen parches grandes a los paquetes. Eso crea forks de facto, muy usados, de librerías y aplicaciones que los mantenedores reales no supervisan
Séptimo, desde la perspectiva de los desarrolladores, el OSS tiene que volverse financieramente sostenible. Liblzma y xz-utils deben tener decenas de millones de instalaciones, pero dependían de un solo mantenedor con problemas de salud mental
Octavo, no me gusta decirlo, pero ahora también hay que tener en cuenta consideraciones geopolíticas en la revisión de código y en la transferencia del mantenimiento
No hay prueba de que Jia Tan sea su nombre real, ni siquiera de que sea una persona real
Si los proyectos empiezan a no aceptar contribuciones de nombres que suenen asiáticos, el siguiente ataque solo tendría que usar un nombre como Richard Jones
Si vienes del mundo BSD, systemd es impactante, monstruoso y tiene tentáculos por todos lados
Los consumidores son ingenuos, pero la propia industria del software también lo es respecto a las amenazas de seguridad
Los exploits sociales son parte de los exploits de código
El principio de FOSS de que “mientras más ojos miren el código, mejor” es correcto, pero solo funciona cuando esos ojos están capacitados. FOSS necesita apoyo material de la industria
Un ingeniero de MSFT detectó este exploit, pero aun así llegó a las versiones públicas generales de Fedora 41, openSUSE y Kali
La cadena de herramientas de desarrollo y los procesos de prueba nunca fueron diseñados para probar seguridad. SolarWinds también es un buen referente: https://www.wired.com/story/the-untold-story-of-solarwinds-t...
Los artefactos de lanzamiento no contienen solo scripts de autoconf
Hay muchas razones para poner blobs binarios en archivos de lanzamiento: recursos de juegos, imágenes de firmware, casos de prueba, etc. También se hablaba de que mpv incluye algunos archivos multimedia generados con un codificador privativo como casos de prueba, y eso no es algo malo sino algo bueno
sqlite, que está bien mantenido, se usa en todas partes y además tiene una excelente suite de pruebas. En cada lanzamiento distribuye no uno sino dos tarballs según la etapa de compilación. Sería fácil dejar de hacerlo, pero solo aumentaría el trabajo de los mantenedores de paquetes y no ayudaría a mejorar la seguridad
La razón por la que Debian compila desde tarballs seleccionados es que están revisados por personas y firmados con claves bien conocidas. Claro, también se podría compilar desde git, pero no está claro que eso mejore la situación. No todos los proyectos firman sus release tags y, aunque lo hagan, es aún más probable que esté automatizado
Queremos que los cambios los revise primero el mantenedor upstream y luego el mantenedor del paquete, y preferimos que esas dos partes no estén relacionadas entre sí
Esta vez un mantenedor upstream corrupto atacó ese proceso, pero eso no significa que haya que eliminar a los mantenedores upstream. En los últimos años esta estructura ha bloqueado varios intentos de backdoor, y no es un proceso que deba descartarse sin una revisión cuidadosa
La dirección de mejora que se viene señalando sigue siendo la misma. Hay que impulsar más los builds reproducibles, reducir en lo posible la superficie de ataque y la complejidad de compilación, y confiar en los mantenedores, pero verificando su trabajo
Más bien, están confundidos precisamente con ese punto y están agregando documentación sobre cómo implementar datagramas simples sin libsystemd
La cantidad de instalaciones de liblzma y xz-utils probablemente sea muchísimo mayor que decenas de millones. Muchos lenguajes como Python, PHP y Ruby dependen de libxml2, y libxml2 usa liblzma. También hay muchas otras dependencias
Las consideraciones geopolíticas no son trabajo de los mantenedores. OSS se ofrece sin garantías
Además, es posible que “Jia Tan” haya sido completamente falso. Si miras los timestamps de los commits, incluso en el mismo día la zona horaria cambia de Europa del Este a Asia. Como mínimo, está claro que estaba jugando un juego de identidad
No sabía que la persona que descubrió este problema era un ingeniero de Microsoft que trabajaba en Azure Postgres
Gracias, Microsoft, ahora Azure me cae mejor
Así fue como se descubrió este problema
Después de eso, habría que revisar la librería implicada y buscar anomalías similares donde una persona falsa tome control de un proyecto
Parece que ignorar este tipo de errores es una práctica común
De verdad no soporto este tipo de presentación
Parece que el mantenedor original de xz cedió la responsabilidad a Jia Tan sin haberlo visto en persona ni, al menos, haber hablado por teléfono con él
Me pregunto si es habitual comunicarse solo por correo electrónico o GitHub
Después de esto, parece que algunos mantenedores de proyectos de código abierto van a ser más cautelosos
Yo también he asumido o entregado el mantenimiento de librerías comunicándome solo por texto, sin saber en absoluto quién era la persona real
Pero creo que la conclusión de que “los mantenedores deben ser más cautelosos” en este caso es totalmente equivocada
La responsabilidad sobre a quién se incorpora a un proyecto no recae en el mantenedor, sino en quienes trabajan con ese proyecto
O confías en el mantenedor o no confías en él, y en el momento en que empiezas a depender de una librería, en cierto modo aceptas implícitamente que irás actualizando continuamente en quién confías
Millones de empresas se aprovechan del trabajo de desarrolladores de código abierto no remunerados
Por eso no sorprende que ellos se vayan y aparezcan problemas
¿Qué confianza adicional habría aportado sobre sus intenciones?
He participado en alrededor de seis proyectos de código abierto de distintos tamaños, desde 100 hasta 30 mil estrellas en GitHub, y nunca he hablado por teléfono con nadie; la comunicación por texto era el estándar
Pero conocer a alguien personalmente no necesariamente resuelve el problema
Hace mucho trabajé de forma anónima en un proyecto de código abierto, cuyo nombre no quiero revelar aquí. Había un co-mantenedor que parecía conocer bastante bien al programador principal
Ese co-mantenedor se dedicó a manipular psicológicamente a mí y luego a otros mantenedores, a menospreciarnos y a culparnos por errores minúsculos hasta hacer que nos fuéramos. Si se le quitan los insultos, era distinto incluso de las reprimendas educativas al estilo de Linus Torvalds
El mantenedor principal lo alentaba porque estaba de acuerdo con el núcleo técnico de sus argumentos
Años después, ese co-mantenedor intentó tomar el control del proyecto de manera hostil, y no salió como esperaba. Poco después se hicieron públicas varias correspondencias privadas, y quedó claro que eso había sido siempre lo que quería y que manipular a los otros mantenedores también formaba parte de ese objetivo
Esto pasó aunque esas dos personas se conocían entre sí
Todos creen que este backdoor fue detectado a tiempo, pero puede que ya haya cumplido su objetivo
Especialmente si el blanco eran desarrolladores que usaban distribuciones rolling release como Kali o Debian
A inicios de semana vi algo de tráfico SSH, pero en ese momento no le presté demasiada atención
Claro, también puede ser una falsa pista: https://www.nubi-network.com/news.php?id=21
El meme de que “Lasse Collin no actualizaba xz Utils con suficiente frecuencia o rapidez” fue un error
No entiendo por qué SSH usa xz
¿Debería hacerlo? ¿De verdad es tan importante?
OpenSSH incorporó
libsystemdpara ofrecer notificaciones de arranque, ylibsystemda su vez incorporóliblzma. Normalmente, el código deliblzmano entra dentro de OpenSSHPero como se compila como dependencia de
libsystemd, el script de compilación se ejecuta en un entorno junto conlibsystemdy OpenSSHLa carga útil del ataque estaba escondida como un blob binario ofuscado, disfrazado de caso de prueba comprimido, dentro del directorio de pruebas de
liblzmaAl compilar
lzmadesde la fuente en git y generar los scripts de compilación con autotools, no había nada sospechoso. Pero en el tarball de código fuente que usan los empaquetadores de distribuciones, autotools ya se había ejecutado, y el atacante sustituyó la salida autogenerada de scripts difíciles de leerEse script verificaba si
liblzmase estaba compilando en un entorno como el de OpenSSH y si además se estaba compilando para entrar en un paquete.debo.rpm; si ambas condiciones se cumplían, insertaba la carga útil en OpenSSHDespués, la carga útil hacía varias comprobaciones, como si OpenSSH había sido iniciado normalmente por un script de init o ejecutado manualmente, si había herramientas de depuración comunes, etc., y solo se acoplaba al proceso en ejecución cuando parecía un arranque “natural”, sin herramientas de depuración
Una vez en ejecución, enganchaba la validación de claves privadas y, cuando la clave privada correcta intentaba iniciar sesión, llamaba al resto del paquete entrante con
system, proporcionando ejecución remota de código con privilegios de root