Xzbot: notas, honeypot y demo de exploit para el backdoor de xz
(github.com/amlweems)- xzbot es un repositorio para explorar el backdoor de xz (CVE-2024-3094), que incluye un honeypot, un parche de clave pública ED448, el formato del payload del backdoor y una demo de ejecución remota de código
- El honeypot aplica un parche simple a OpenSSH para registrar en los logs de sshd los intentos de conexión entrantes con un valor N de clave pública que coincide con el formato del backdoor
- El parche ED448 reemplaza la clave pública hardcodeada que el backdoor usa para verificar firmas y descifrar payloads por una clave generada por el usuario, lo que permite activar el backdoor
- El payload del backdoor se coloca en el valor N de la clave de firma de CA del certificado SSH; crea el tipo de solicitud con
a * b + c, y el Type 2 ejecuta un comando terminado en null mediantesystem() - La CLI de demo muestra el flujo para conectarse a un servidor SSH vulnerable y ejecutar un comando; una explotación exitosa no genera logs de nivel INFO o superior
Alcance y componentes de xzbot
- xzbot es un proyecto para explorar CVE-2024-3094, el backdoor de xz
- Incluye cuatro elementos
- honeypot: servidor vulnerable falso para detectar intentos de exploit
- ed448 patch: parchea
liblzma.sopara usar una clave pública ED448 propia - backdoor format: documentación del formato del payload del backdoor
- backdoor demo: CLI que activa RCE bajo el supuesto de que se conoce la clave privada ED448
Honeypot: detección de intentos con un parche de OpenSSH
- openssh.patch es un parche simple que se aplica a OpenSSH para registrar intentos de conexión entrantes con un valor N de clave pública que coincide con el formato del backdoor
- El flujo consiste en clonar
openssh-portable, aplicar el parche y ejecutarautoreconf,configureymake - Los intentos de conexión aparecen en los logs de
sshdcomoxzbot: magic 1 [preauth]y como una secuencia de bytes del payload
Parche de clave pública ED448
- El backdoor usa una clave pública ED448 hardcodeada para verificar firmas y descifrar payloads
- Si se reemplaza esa clave pública por una clave que el usuario posee, se puede activar el backdoor
- El repositorio presenta tanto la clave pública ED448 del atacante como un valor de clave pública alternativo generado con
seed=0 - El paso inicial es preparar el objeto compartido
liblzma.so.5.6.1que contiene el backdoor- Se menciona como fuente de ejemplo el snapshot de Debian de
xz-utils 5.6.1-1 - Tras instalar
pwntools, al ejecutarpatch.py liblzma.so.5.6.1se generaliblzma.so.5.6.1.patch - Luego se ejecuta
sshdusando el objeto compartido modificadoliblzma.so.5.6.1.patch
- Se menciona como fuente de ejemplo el snapshot de Debian de
- En assets hay ejemplos
Formato del payload del backdoor
- El backdoor puede activarse al conectarse con un certificado SSH colocando el payload dentro del valor N de la clave de firma de CA
- Este payload debe estar cifrado y firmado con la clave ED448 del atacante
- La estructura externa está compuesta por los siguientes campos
a: 32 bitsb: 32 bitsc: 64 bitsciphertext: 240 bytes
- El tipo de solicitud se deriva como
a * b + c- Si el valor es mayor que 3, el backdoor omite el procesamiento
- Type 1: uso desconocido, espera 0 bytes
- Type 2: ejecuta un payload terminado en null mediante
system() - Type 3: uso desconocido, espera 48 bytes de datos de firma
ciphertextse cifra con chacha20, usando los primeros 32 bytes de la clave pública ED448 como clave simétrica- Por eso, los intentos de exploit pueden descifrarse con la clave de 32 bytes indicada
- Dentro del
ciphertextdescifrado hay una firma de 114 bytes, campos de 1 bitxey, longitud, cadena de comando, etc.- Si se configura
xoy, se toma una ruta de código ligeramente distinta
- Si se configura
- La firma es una firma ED448 según RFC-8032
- La entrada incluye un valor magic de 32 bits, un campo de 5 bytes previo al comando, opcionalmente los bytes
lengthdel comando y los primeros 32 bytes del hash SHA-256 de la hostkey del servidor
- La entrada incluye un valor magic de 32 bits, un campo de 5 bytes previo al comando, opcionalmente los bytes
CLI de demo del exploit
- La CLI se instala con Go
go install github.com/amlweems/xzbot@latest
xzbot -hmuestra tres opciones principales-addr: dirección del servidor SSH, valor predeterminado127.0.0.1:2222-seed: seed ED448 que debe coincidir con la clave del backdoor de xz, valor predeterminado0-cmd: comando que se ejecutará consystem(), valor predeterminadoid > /tmp/.xz
- El ejemplo se conecta al servidor SSH vulnerable en
127.0.0.1:2222y ejecuta el comandoid > /tmp/.xz - Si se configura un watchpoint en el punto de llamada a
system()en el servidor vulnerable, se puede ver cómo el procesosshdejecutaid > /tmp/.xz - Se presenta un ejemplo en el que, después de la ejecución,
/tmp/.xzcontiene la salidauid=0(root) gid=0(root) groups=0(root)
Árbol de procesos y características de los logs
- El árbol de procesos de una conexión SSH normal continúa hacia la sesión de usuario de
sshdy el shell - En el ejemplo de ejecución del backdoor, después de
xzbot -cmd 'sleep 60'aparecensshd: root [priv],sshd: root [net],sh -c sleep 60ysleep 60 - Una explotación exitosa no genera entradas de log de nivel INFO o superior
1 comentarios
Opiniones de Hacker News
Es bastante interesante que no lo hayan convertido en ejecución remota de código explotable por cualquiera, sino que requiera la clave privada del atacante.
Irónicamente, parece una vulnerabilidad muy consciente de la seguridad.
Si hubieran abierto un agujero enorme para que cualquiera pudiera entrar, lo habrían descubierto y cerrado rápido; y si no hubiera habido degradación de rendimiento, posiblemente habría sobrevivido en silencio durante mucho tiempo con el argumento de que no era ampliamente explotable.
Toda la contribución a xz parece haber sido, al final, trabajo para insertar este backdoor; por ejemplo, incluso crearon un framework de pruebas donde se podía ocultar el payload malicioso.
Antes de trabajar en xz, también contribuyeron a libarchive de BSD, donde apareció una vulnerabilidad.
El diseño y la ejecución fueron extremadamente sofisticados, y que el problema de rendimiento haya sido lo que llevó a descubrirlo se siente casi como pura suerte.
Si es así, no se puede rociar indiscriminadamente sobre servidores, y enviarlo a un host en particular ya implica un costo de cómputo considerable.
Este incidente no me salió de la cabeza en todo el fin de semana.
El mecanismo es fascinante y una gran colección de ofuscación, y la parte de ingeniería social es una historia vergonzosamente familiar para los mantenedores de open source.
Lo más interesante es que hayan elegido datos de prueba malos como vector de ataque: si preparas un buen archivo, lo manipulas estructuralmente y luego lo usas como datos defectuosos para fuzz testing, el resto de los pasos se vuelve muchísimo más fácil.
Para más adelante, diría que este tipo de manipulación debería aparecer en grafos de patrones binarios.
El resto de las técnicas, una vez definido el payload, se parecían en general a ofuscación bastante común, pero la jugada brillante fue que, con el mismo patrón, podían agregar “parches” o incluso backdoors nuevos a otros archivos de prueba sin que nadie se diera cuenta.
Que GitHub haya ocultado y eliminado el repositorio no ayudó en absoluto y dificulta el análisis de este incidente.
Descubrí que esa dependencia en particular prefiere xz de manera llamativa y, si no está instalado, incluso instala xz en la máquina host como si fuera una función de conveniencia.
No hace eso con otras dependencias, así que resulta algo raro.
Estas estrategias de largo plazo dan miedo, y hasta que el “mal” se ejecuta de verdad, no se puede saber qué es malicioso y qué simplemente es extraño.
En especial si son archivos de “basura binaria” para demostrar fallas en pruebas, son un lugar demasiado bueno para ocultar contenido malicioso.
Es muy impresionante que la comunidad, en especial amlweems, haya implementado y documentado tan rápido un código de prueba de concepto.
Si no hay vulnerabilidades adicionales en las funciones criptográficas o de carga del payload, puede que ni siquiera haya abierto la falla de seguridad a todos los demás atacantes hasta que se rompa la clave.
El siguiente paso es encontrar una forma de detectar distribuciones vulnerables, lo cual no parece fácil; y tal vez upstream podría incorporar una forma de monitorear si alguien está escaneando activamente servidores SSH con la clave hardcodeada.
Una prueba de concepto de la versión original requiere la clave privada no publicada del atacante.
Lo mejor que se puede hacer sería un benchmarking más fino, pero no se puede saber si un host cualquiera de Internet responde lento porque es vulnerable, porque está lejos o porque la computadora en sí es lenta.
Tampoco se puede acceder a cuánto tardaron intentos de conexión anteriores a ese host, y además hay variaciones de ruteo.
Me pregunto si alguien probó esta prueba de concepto con herramientas de detección de comportamiento anómalo de procesos.
Productos como Carbon Black, AWS GuardDuty y Sysdig entran en esa categoría, y este parece un caso perfecto para esa familia de productos si se quiere probar si alguien lo habría notado relativamente rápido cuando se desplegó de verdad.
GuardDuty no inspecciona procesos como un EDR tipo CrowdStrike o Carbon Black, así que creo que sería difícil que lo detectara; y Sysdig observa contenedores e infraestructura cloud, por lo que parece difícil que detecte el exploit en sí.
Sin embargo, después de una escalada de privilegios, podría detectar anomalías en las acciones posteriores del actor de amenazas.
En última instancia, lo más probable para detectar el exploit en sí sería un EDR que monitoree procesos de endpoint, o una evaluación de la cadena de suministro de software que monitoree problemas de seguridad en software libre y open source upstream.
Curiosamente, esto lleva a un tema de seguridad más amplio.
A los equipos de desarrollo puede no gustarles instalar EDR en servidores por la degradación de rendimiento y los problemas de experiencia de usuario al aplicar aislamiento, y también pueden no gustarles las políticas que restringen el uso de software libre y open source.
Este exploit golpea justo en el centro de una “vulnerabilidad” organizacional, y según la posición puede haber argumentos tanto para mantener la exposición como para corregirla.
Decía: “Una forma de detección en runtime es monitorear si SSHD carga una biblioteca maliciosa. Estas bibliotecas compartidas suelen incluir la versión en el nombre de archivo”.
El blog también incluye contenido de reglas de detección reales que no vi en otros proveedores de seguridad.
https://sysdig.com/blog/cve-2024-3094-detecting-the-sshd-bac...
Leí mal el enlace de abajo, y dejo el contenido original a modo de registro
Al ver más abajo en el mismo hilo de correo, se decía que la persona que hizo commit del backdoor parecía haber hecho recientemente contribuciones al kernel, pero el análisis original en sí es realmente excelente, así que vale la pena leer ese tipo de textos
https://www.openwall.com/lists/oss-security/2024/03/29/10
El propio Lasse también dijo que no era nada urgente y que no entraría en esta ventana de merge; ninguna persona razonable está acusando a Lasse de ser un actor malicioso
Este incidente tiene una cantidad absurda de similitudes con el caso de Audacity de hace unos años
Cookie guy afirmó que lo habían apuñalado y que la policía federal estaba involucrada en el caso, lo que sugiere la posibilidad de que el incidente estuviera conectado con un actor mucho más grande que 4chan
En ese momento mucha gente creía que solo Muse Group estaba involucrado, pero quizá pudo haber sido un actor estatal ruso
Antes de eso, afirmó que Audacity tenía mucha telemetría y backdoors, y que los había eliminado en el primer commit después de hacer el fork
Tal vez Audacity también tenga realmente un backdoor, así que habría que revisar el código fuente
Últimamente está fusionando operaciones con APT29, y yo no despertaría a cozy bear
Me pregunto cómo hacía esto el exploit real en runtime sin el openssh.patch del honeypot en tiempo de compilación https://github.com/amlweems/xzbot/blob/main/openssh.patch
La cadena era
opensshd -> notificación de systemd -> xz incluido como dependencia transitoria, y quiero saber cómo, después de queliblzma.so.5.6.1se cargaba en memoria, lograba remontarse hastaopenssh_RSA_verifypara engancharlo o parchearloSi se carga antes que libcrypto, registra un manejador de auditoría de símbolos, que probablemente sea una función específica de glibc, y puede recibir una notificación cuando se resuelven los símbolos de libcrypto para demorar el parcheo de la GOT
Me pregunto si se sabe si este exploit solo se activaba cuando entraba una conexión SSH
La lista de cadenas en GitHub incluye
DISPLAYyWAYLAND_DISPLAYhttps://gist.github.com/q3k/af3d93b6a1f399de28fe194add452d01
Estas no tienen una relación clara con SSH, así que existe la posibilidad de que hiciera algo incluso sin una conexión
Podría ser importante para quienes ejecutaron el código pero asumen que están a salvo porque no expusieron el servidor SSH a Internet
Es decir, un mecanismo para evitar situaciones en las que alguien intente detectarlo, reproducirlo o depurarlo
Si no lo desactivas al conectarte a una máquina no confiable, se convierte en un agujero de seguridad común del lado de quien se conecta
Me pregunto si “una explotación exitosa no genera entradas de log” significa que, si este exploit no hubiera sido descubierto, el atacante podría haber ejecutado comandos arbitrarios como root en el host comprometido sin ni siquiera un log de sshd para esa “conexión”
La ejecución remota de código ocurre en la fase de conexión, antes de que se escriban logs
Me pregunto qué tipo de detección de anomalías habría podido captarlo
Me pregunto si este ataque se habría podido dificultar más separando los archivos de prueba en un repositorio aparte para que no pudieran usarse en tiempo de build
La lógica es que cualquier cosa que pueda intervenir en el build debe ser legible por humanos
Hay que tratar este ataque como un accidente aéreo e introducir nuevas reglas que reduzcan la probabilidad de que vuelva a tener éxito
Aunque no podamos verificar a cada colaborador individualmente, los datos de prueba ruidosos deberían poder separarse fácilmente