Informe del incidente: CVE-2024-YIKES
(nesbitt.io)- CVE-2024-YIKES es un incidente en el que el secuestro de dependencias de JavaScript se propagó a las cadenas de suministro de Rust y Python
- El phishing de
left-justifyfiltró credenciales de.npmrc,.pypirc, Cargo y Gem - El
build.rsmalicioso devulpine-lz4descargó y ejecutó un script de shell en hosts de CI - El malware de
snekpack3.7.0 se propagó a aproximadamente 4.2 millones de equipos y agregó claves SSH y un shell reverso - El gusano cryptobro-9000 accidentalmente actualizó a
snekpack3.7.1, eliminando el malware
Resumen del incidente
- CVE-2024-YIKES es un incidente de seguridad en el que una dependencia comprometida del ecosistema JavaScript llevó al robo de credenciales, que luego se expandió a un ataque a la cadena de suministro de una biblioteca de compresión de Rust y a la distribución de malware en una herramienta de compilación de Python
- El incidente fue reportado a las 03:47 UTC, y su estado cambió a “resuelto por accidente”, mientras que la severidad pasó de “Critical → Catastrophic → Somehow Fine”
- La duración fue de 73 horas, y los sistemas afectados siguieron figurando como “Yes”
- La cadena de paquetes y herramientas comprometidas siguió
left-justify,vulpine-lz4ysnekpack, distribuyendo malware a aproximadamente 4 millones de desarrolladores - Al final, un gusano separado de minería de criptomonedas,
cryptobro-9000, ejecutó actualizaciones en las máquinas infectadas y elevósnekpacka una versión legítima, eliminando el malware por accidente
Desarrollo del incidente
-
Día 1: robo de credenciales desde un paquete de JavaScript
- A las 03:14 UTC, el mantenedor de
left-justify, Marcus Chen, publicó en Twitter que le habían robado su tarjeta de transporte, una laptop vieja y “algo que parecía importante y que Kubernetes vomitó”, pero eso no se vinculó de inmediato con un problema de seguridad del paquete - A las 09:22 UTC, Chen intentó iniciar sesión en el registro de nmp y confirmó que no tenía su llave física de 2FA; el AI Overview en la parte superior de los resultados de Google lo dirigió al sitio de phishing
yubikey-official-store.net, registrado 6 horas antes - A las 09:31 UTC, Chen ingresó sus credenciales de nmp en ese sitio de phishing, que le agradeció la compra y prometió entrega en 3 a 5 días hábiles
- A las 11:00 UTC, se publicó
[email protected]con un changelog que decía “performance improvements” - Ese paquete incluía un script de postinstalación que exfiltraba
.npmrc,.pypirc,~/.cargo/credentialsy~/.gem/credentialshacia un servidor elegido por el atacante - A las 13:15 UTC, se abrió un ticket de soporte en
left-justifycon el mensaje “why is your SDK exfiltrating my .npmrc”, pero fue marcado como “low priority - user environment issue” y luego se cerró automáticamente tras 14 días sin actividad
- A las 03:14 UTC, el mantenedor de
-
Día 1: el ataque a la cadena de suministro se extiende a una biblioteca de Rust
- Entre las credenciales filtradas estaban las del mantenedor de la biblioteca de Rust
vulpine-lz4 vulpine-lz4es una biblioteca para “blazingly fast Firefox-themed LZ4 decompression”; tiene 12 estrellas en GitHub, pero es una dependencia transitiva del propiocargo- A las 22:00 UTC, se publicó
vulpine-lz40.4.1 con el mensaje de commit “fix: resolve edge case in streaming decompression” - El cambio real agregó un script
build.rsque descargaba y ejecutaba un script de shell si el hostname contenía “build”, “ci”, “action”, “jenkins”, “travis” o “karen”
- Entre las credenciales filtradas estaban las del mantenedor de la biblioteca de Rust
-
Día 2: fallan la detección y la respuesta
- A las 08:15 UTC, la investigadora de seguridad Karen Oyelaran descubrió el commit malicioso después de que el payload se ejecutara en su laptop personal
- Karen Oyelaran abrió un issue con “your build script downloads and runs a shell script from the internet?”, pero no recibió respuesta
- El mantenedor legítimo había ganado €2.3 million en EuroMillions y estaba investigando granjas de cabras en Portugal
- A las 10:00 UTC, el VP of Engineering de un cliente Fortune 500 de
snekpackse enteró del incidente por una publicación de LinkedIn titulada “Is YOUR Company Affected by left-justify?”; quiso saber por qué no lo habían incluido antes, pero en realidad ya lo habían incluido antes - A las 10:47 UTC, el canal de Slack
#incident-responsese desvió momentáneamente hacia un hilo de 45 mensajes sobre si “compromised” debía escribirse con ‘z’ al estilo estadounidense
-
Día 2: se infecta la herramienta de compilación de Python
snekpack- A las 12:33 UTC, el script de shell apuntó al pipeline de CI de
snekpackpara enfocarse en una víctima específica snekpackes una herramienta de compilación de Python usada por el 60% de los paquetes de PyPI cuyo nombre contiene “data”snekpackhabía incorporadovulpine-lz4por vendor porque “Rust is memory safe”- A las 18:00 UTC, se lanzó
snekpack3.7.0 y el malware empezó a instalarse en máquinas de desarrolladores de todo el mundo - El malware añadía una clave SSH a
~/.ssh/authorized_keys, instalaba un shell reverso que solo se activaba los martes y cambiaba el shell predeterminado del usuario afish - El cambio del shell predeterminado a
fishfue considerado un bug - A las 19:45 UTC, otro investigador de seguridad publicó una entrada de blog de 14,000 palabras titulada “I found a supply chain attack and reported it to all the wrong people”, que incluía la frase “in this economy?” 7 veces
- A las 12:33 UTC, el script de shell apuntó al pipeline de CI de
-
Día 3: parche accidental y cierre del incidente
- A las 01:17 UTC, un desarrollador junior en Auckland descubrió el malware mientras depuraba un problema distinto y abrió un PR para revertir
vulpine-lz4dentro desnekpack - Ese PR requería 2 aprobaciones, pero ambos revisores estaban dormidos
- A las 02:00 UTC, el mantenedor de
left-justifyrecibió una YubiKey desdeyubikey-official-store.net; resultó ser una memoria USB de 4 dólares con un README que decía “lol” - A las 06:12 UTC, un gusano separado de minería de criptomonedas,
cryptobro-9000, empezó a propagarse mediante una vulnerabilidad dejsonify-extreme jsonify-extremese describe como un paquete que “makes JSON even more JSON, now with nested comment support”- El payload de
cryptobro-9000no tenía nada especial, pero su método de propagación incluía ejecutarnpm updateypip install --upgradeen las máquinas infectadas para ampliar la superficie de ataque futura - A las 06:14 UTC,
cryptobro-9000actualizó por accidentesnekpacka 3.7.1 snekpack3.7.1 fue una versión legítima publicada por un co-mantenedor confundido, que revertía la versión vendor devulpine-lz4a una versión anterior- A las 06:15 UTC, el shell reverso de los martes se activó, pero el servidor de comando y control había sido comprometido por
cryptobro-9000y no pudo responder - A las 09:00 UTC, los mantenedores de
snekpackpublicaron un aviso de seguridad de 4 oraciones que incluía las frases “out of an abundance of caution” y “no evidence of active exploitation” - “no evidence of active exploitation” se consideró técnicamente cierto porque no buscaron evidencia
- A las 11:30 UTC, un desarrollador tuiteó “I updated all my dependencies and now my terminal is in fish???” y obtuvo 47,000 likes
- A las 14:00 UTC, las credenciales comprometidas de
vulpine-lz4fueron reemplazadas - El mantenedor legítimo recibió un correo en su nueva granja de cabras y respondió: “I haven't touched that repository in two years” y “I thought Cargo 2FA was optional”
- A las 15:22 UTC, se declaró resuelto el incidente y se agendó una retrospectiva, que luego fue reprogramada tres veces
- A las 01:17 UTC, un desarrollador junior en Auckland descubrió el malware mientras depuraba un problema distinto y abrió un PR para revertir
Asignación del CVE y alcance del daño
- En la semana 6 se asignó oficialmente CVE-2024-YIKES
- El aviso permaneció bajo embargo mientras MITRE y GitHub Security Advisories discutían la clasificación CWE
- Para cuando el CVE se hizo público, 3 artículos en Medium y una charla en DEF CON ya habían cubierto el incidente en detalle
- El daño total sigue siendo desconocido
- Se estima que el número de máquinas comprometidas fue de 4.2 millones
- También se estima en 4.2 millones el número de máquinas salvadas por el gusano de criptominería
- El cambio neto en la postura de seguridad quedó en “incomodidad”
Causa raíz y factores contribuyentes
-
Causa raíz
- Se consideró como causa raíz que el perro llamado Kubernetes se comió la YubiKey
-
Factores contribuyentes
- El registro de nmp todavía permite autenticación solo con contraseña para paquetes con menos de 10 millones de descargas semanales
- Google AI Overviews enlazó con toda confianza una URL que no debería existir
- La filosofía de “crates pequeños” del ecosistema Rust fue replicada desde el ecosistema npm, permitiendo que paquetes como
is-even-number-rs, con 3 estrellas en GitHub, terminen como dependencia transitiva cuatro niveles abajo en infraestructura crítica - Las herramientas de compilación de Python incorporan bibliotecas de Rust por “performance” y luego no las actualizan
- Dependabot fusionó automáticamente PRs después de que CI pasara, y CI pasó porque el malware instaló
volkswagen - El gusano de criptomonedas tiene mejor higiene de CI/CD que la mayoría de las startups
- No hubo un único responsable, pero el PR de Dependabot fue aprobado por un contratista cuyo último día de trabajo era ese viernes
- El día del incidente era martes
Medidas de mejora y opciones restantes
- Implementar firma de artefactos era un action item del incidente de Q3 2022, pero sigue en el backlog
- La implementación de 2FA obligatorio ya había sido solicitada, pero no ayudó
- La auditoría de dependencias transitivas fue tachada porque había 847 objetivos
- Fijar todas las versiones de dependencias impide recibir parches de seguridad
- No fijar las versiones de dependencias permite ataques a la cadena de suministro
- La opción de reescribirlo en Rust aparece tachada y apunta a
vulpine-lz4 - Las opciones restantes son esperar un gusano benévolo o considerar un cambio de carrera hacia una granja de cabras
Impacto en clientes y respuesta organizacional
- Algunos clientes pueden haber experimentado “resultados de seguridad no óptimos”
- Se contactó de forma proactiva a los stakeholders afectados para dar visibilidad de la situación
- La confianza del cliente se mantuvo como “north star”
- Se creó un grupo de trabajo cross-functional para revisar la postura de seguridad, pero todavía no ha tenido reuniones
- Tras revisión legal, se agregó que el shell
fishno es malware, aunque a veces lo parezca - Este informe del incidente es el tercero del trimestre
- La solicitud de headcount del equipo de seguridad sigue en backlog desde Q1 2023
Agradecimientos
- Karen Oyelaran detectó el problema porque su hostname coincidía con la expresión regular
- El PR del desarrollador junior de Auckland fue aprobado 4 horas después de que el incidente ya se hubiera resuelto
- Algunos investigadores de seguridad encontraron el problema antes, pero lo reportaron a las personas equivocadas
- El autor de
cryptobro-9000no quiso ser identificado, pero pidió que mencionaran su SoundCloud - El perro llamado Kubernetes se negó a comentar
- El equipo de seguridad, pese a todo, cumplió el SLA de este informe
1 comentarios
Opiniones de Hacker News
Para quienes se confundieron: este texto es una ficción bastante bien escrita sobre un incidente de cadena de suministro
Cuando lo leí por encima, pensé que era real y me preocupó bastante, así que terminé leyéndolo con más atención :)
nmpMe dio curiosidad la parte citada de “vulpine-lz4 con 12 estrellas en GitHub como dependencia transitiva del propio cargo”, así que saqué al tanteo algunos crates que podrían meterse en un build de cargo y que ya tienen
build.rs, por lo que llamarían menos la atención:flate2,tar,curl-sys,libgit2-sys,openssl-sys,libsqlite3-sys,blake3,libz-sys,zstd-sys,ccDe paso, si consigues acceso a
xz2, también podrías contaminar rustupAun así, por lo menos sí están siguiendo
Cargo.lock-sysson solo bindings, así que si hicieran otra cosa se vería bastante sospechosoSegún entiendo, los demás pertenecen a maintainers principales de Rust como
alexcrichtono al propiorustlangEs fácil volverse cínico porque, una vez que todo pasa, el problema y la solución parecen demasiado obvios
Pero durante mucho tiempo, y quizá incluso ahora, el credo de la cultura hacker fue move fast and break things
Me parece bien que esté creciendo el impulso por corregir los problemas evidentes de sistemas de cadena de suministro como
npm, pero me preocupa que estemos entrando en una era de nuevos problemas de seguridad provocados en gran parte por el desarrollo agénticoNo solo por las historias de vulnerabilidades en casi todo lo que toca Mythos/Glasswing, sino porque la forma en que construimos software, incorporamos dependencias y vamos perdiendo modelos mentales humanos sobre sistemas complejos probablemente produzca mucho software e infraestructura parchados que nadie entiende de verdad
Ojalá que dentro de unos años, al mirar este momento, no nos arrepintamos de lo ingenuos que fuimos y de no habernos preparado bien para la larga cola del desarrollo con IA, sin intentar resolver el problema rehaciendo sistemas complejos con IA
Eso sí, el texto estuvo chistoso
Como fan de Fish, esta frase me hizo sentir a la vez atacado y comprendido: “por favor, dejen claro que el shell fish no es malware, aunque a veces lo parezca”
Más allá del shell, la parte de “la solicitud para ampliar el equipo de seguridad estaba en backlog desde el primer trimestre de 2023” también se sintió demasiado familiar
figletconapt-getodnfy luego sobrescribir el contenido de/etc/motdcon all your base are belong to us en una enorme tipografía ASCII artMe reí muchísimo con la parte donde el maintainer de left-justify recibió una YubiKey de
yubikey-official-store.net, y resultó ser una memoria USB de 4 dólares con “lol” escrito en elREADMETroleo total
Me gusta que conectar un dispositivo USB enviado desde un sitio de phishing sea en sí mismo otro vector de ataque
No era SCP de verdad, pero fue lo más parecido a SCP que he leído últimamente
Me reí fuerte con la parte de Karen :D ;)
Me recordó un script de build basado en
makeque recibí al revisar el proyecto de un compañero de clase; si el hostname conteníabpavuk, intentaba hacerrm -rfen mi carpeta home¡¡Eso fue en primero de secundaria!!
Los incidentes de cadena de suministro sí son un dolor de cabeza y tenemos que hacerlo mejor
Personalmente, en Rust apoyo que la fundación respalde algunos crates clave para que pasen por el mismo proceso de auditoría que el propio lenguaje Rust y financie proyectos para reducir vulnerabilidades de la cadena de suministro
No creo que la respuesta sea deshacerse de sistemas como
cratesonpm.cratesynpmayudan muchísimo a muchos desarrolladorescratestambién ha hecho esfuerzos por incorporarrustsec, pero aparte de eso, me gustaría que la comunidad dejara de depender tanto de montones de dependencias pequeñas y se moviera hacia menos dependencias grandes, comotokiocrates.ioya son crates de primera parte ofrecidos por la organización de RustEsto se pasa por alto con frecuencia cuando la gente se preocupa por el grafo de crates de Rust
Si miras las 10 más descargadas en la portada de
crates.io, la única que no fue hecha por la organización Rust o por maintainers centrales de Rust es la cratebase64npmcomonmp?“El maintainer oficial ganó 2,3 millones de euros en EuroMillions y ahora está viendo cómo criar cabras en Portugal”, y “causa raíz: un perro llamado Kubernets se comió la YubiKey”
Ah, sí. Qué irresponsable caer en ese ataque famoso de toda la vida
Ya sabes, la técnica de “distraer a alguien con un premio de lotería y hacer que el dongle de otra persona le parezca irresistiblemente sabroso a su mascota”
¿Cuándo irá a aprender la gente?
Menos mal que no uso
npmnipipy solo uso la forma recomendada:curl ... | bashcurl | sudo bashQué amateur