✨︎ Resumen (TL;DR):
- StepSecurity registró 435 paquetes únicos y 1,557 versiones maliciosas al corte de las 16:20 UTC del 4 de agosto de 2026.
- Las librerías afectadas sumaban más de 2,000 millones de instalaciones mensuales, pero esa cifra no equivale a infecciones, descargas del malware ni usuarios únicos.
- ChainDrop robó credenciales, contaminó otros paquetes y consiguió publicaciones con atestaciones SLSA válidas mediante flujos legítimos de GitHub Actions.
ChainDrop, un gusano de cadena de suministro derivado de Shai-Hulud, contaminó 435 paquetes únicos y 1,557 versiones de npm durante poco más de dos horas el 4 de agosto de 2026. StepSecurity registró ese alcance a las 16:20 UTC. El malware robó credenciales de desarrollo y nube, las usó para publicar más paquetes infectados y dejó mecanismos de persistencia en Claude Code y Visual Studio Code.
ChainDrop es un gusano de cadena de suministro que busca credenciales en computadoras de desarrollo y runners de integración continua, y usa los tokens de npm recuperados para publicar más paquetes contaminados.

El conteo real separa paquetes, versiones y repositorios
El dato de 1,300 paquetes que circuló durante las primeras horas mezcló tres métricas distintas. Aikido informó a las 13:37 CEST que había identificado 434 paquetes distribuidos en 1,381 versiones, con más de 2,000 millones de instalaciones mensuales combinadas. Unas horas después, StepSecurity elevó su corte a 435 paquetes y 1,557 versiones.
El incremento se explica porque el gusano republicó numerosas versiones históricas de un mismo paquete. El segundo número creció por la cantidad de versiones contaminadas, no porque cada publicación correspondiera a un nombre nuevo en npm.
Los aproximadamente 1,300 que aparecen en otro conteo corresponden a repositorios públicos de GitHub que contenían el indicador Shai-Hulud: Here We Go Again. El malware usaba esos repositorios como puntos de entrega para credenciales robadas y cifradas. Son repositorios de exfiltración, no paquetes de npm.
StepSecurity separó el alcance comprobado en estos grupos:
- 11 paquetes portadores completos dentro de proyectos mantenidos por Jared Wray, con el cargador y el gusano de 727 KB.
- 424 paquetes propagados por el propio malware mediante credenciales obtenidas de nuevas víctimas.
- 1,557 combinaciones de paquete y versión contaminadas entre las 09:35 y las 11:44 UTC.
Las más de 2,000 millones de instalaciones mensuales tampoco equivalen a 2,000 millones de infecciones. El cálculo suma el volumen habitual de descarga de las librerías afectadas. Una aplicación puede instalar varias de ellas, los sistemas automatizados generan descargas repetidas y muchas copias nunca resolvieron una versión maliciosa.
Un commit malicioso atravesó una cadena de confianza válida
Los investigadores rastrearon el inicio del ataque hasta la cuenta de GitHub del responsable de Keyv, Cacheable y otras utilidades. El atacante introdujo archivos maliciosos directamente en la rama principal, creó etiquetas de lanzamiento y dejó que los flujos legítimos de GitHub Actions publicaran las nuevas versiones en npm mediante OIDC Trusted Publishing.
Por esa razón, varias publicaciones recibieron atestaciones SLSA válidas. La infraestructura confirmó que cada paquete se había construido desde el commit indicado y mediante el flujo esperado. El problema estaba antes: el commit ya contenía el malware. La procedencia verificó el recorrido de la publicación, pero no pudo determinar si el mantenedor había autorizado ese cambio.
Una atestación SLSA es una prueba de procedencia que vincula un paquete con el commit y el flujo de construcción que lo generaron. No determina por sí misma si ese commit fue autorizado por el mantenedor.
El ataque comenzó con keyv@6.0.0 y alcanzó librerías que también aparecían como dependencias indirectas en proyectos JavaScript. Entre las primeras publicaciones contaminadas estuvieron:
keyv@6.0.0, con aproximadamente 153.7 millones de descargas semanales según el conteo de StepSecurity.flat-cache@6.1.24, con unas 149.9 millones de descargas semanales.file-entry-cache@11.1.6, con unas 147.6 millones de descargas semanales.cacheable-request@13.0.20.cacheable@2.5.1.cache-manager@7.2.10.ecto@5.0.1.
La propagación también alcanzó paquetes asociados con ServiceTitan, OneReach, Ornikar, Deliveroo, Picsart y Qlik. Como varios nombres afectados eran dependencias transitivas, un desarrollador podía ejecutar el código sin haber agregado Keyv o flat-cache directamente a su proyecto.
El preinstall robó credenciales y propagó el ataque
Cada versión portadora añadía un script preinstall que ejecutaba setup.mjs antes de completar npm install. Ese archivo descargaba la distribución oficial de Bun 1.3.13 desde GitHub y la usaba para iniciar Math_Symbol.js, una segunda etapa ofuscada de aproximadamente 728 KB.
Una vez activo, el payload buscaba secretos en la computadora del desarrollador o en el runner de integración continua. Los objetivos documentados incluían:
- Tokens de npm y GitHub, incluidos los tokens OIDC de GitHub Actions.
- Credenciales de AWS, Google Cloud y Microsoft Azure.
- Tokens de HashiCorp Vault y cuentas de servicio de Kubernetes.
- Llaves SSH, archivos
.env, estados de Terraform y credenciales de registros Docker. - Claves de Stripe, tokens de Slack y credenciales guardadas por herramientas de AI para programación.
El gusano cifraba los datos y los enviaba principalmente a repositorios de GitHub creados desde las cuentas comprometidas. StepSecurity también observó un canal de mando que resolvía su destino mediante un contrato de Ethereum y usaba npm-cache.com para comunicarse.
Después, ChainDrop revisaba qué paquetes podía publicar cada token de npm robado. Los reempaquetaba con el mismo gancho de instalación y repetía el ciclo con nuevas víctimas.
La infección no termina al borrar node_modules
El robo de credenciales no era el único mecanismo. Los análisis de StepSecurity, Socket y Datadog encontraron cambios en .claude/settings.json y .vscode/tasks.json, además de configuraciones de CI capaces de volver a ejecutar el cargador al abrir o confiar en un repositorio.
Borrar node_modules o actualizar el paquete no elimina por sí solo las credenciales que ya fueron robadas ni los mecanismos de persistencia colocados fuera del paquete.
Qué revisar si un proyecto instaló una versión afectada
StepSecurity advirtió que una sola ejecución de npm install durante la ventana de exposición bastaba para activar el cargador. La revisión debe cubrir proyectos, runners de CI y computadoras de desarrollo, incluso cuando Keyv o Cacheable solo aparezcan como dependencias indirectas.
- Busca todos los nombres y versiones afectados en
package-lock.json,pnpm-lock.yamlyyarn.lock. El listado completo puede cambiar mientras npm y los mantenedores sanean el registro, así que conviene usar el inventario vivo de los investigadores. - Revisa la presencia inesperada de
setup.mjs,Math_Symbol.jsomath_init.jsdentro denode_modules, además de cambios en las carpetas.claude,.vscodey los flujos de GitHub. - Si una versión afectada llegó a ejecutarse, trata la computadora o el runner como comprometido. Aísla el entorno, conserva los registros y revisa actividad anómala en npm, GitHub y los proveedores de nube.
- Retira primero la persistencia y el monitor de tokens descrito por StepSecurity y Socket. Ese componente puede reaccionar cuando se revoca un token de GitHub.
- Después, desde una computadora limpia, revoca y rota todos los secretos accesibles para el proceso: tokens de npm y GitHub, llaves SSH, credenciales de nube, secretos de CI, accesos de Kubernetes y sesiones sensibles.
- Fija versiones limpias exactas, reconstruye los archivos de bloqueo y considera reinstalar con los scripts desactivados mientras termina la investigación.
Advertencia: Si una versión afectada llegó a ejecutarse, actualizar el paquete no limpia la computadora. Primero hay que retirar la persistencia y el monitor de tokens; después deben revocarse y rotarse las credenciales desde un equipo limpio.
La firma válida no garantiza un cambio autorizado
ChainDrop atravesó controles que habrían detenido publicaciones sin firma porque las versiones iniciales salieron de flujos legítimos de GitHub Actions. El episodio expone el límite concreto de la procedencia criptográfica: puede vincular un artefacto con su fuente y su proceso de construcción, pero no decidir si el cambio dentro de ese repositorio confiable fue autorizado por el mantenedor.
Los investigadores propusieron varias defensas para reducir ese riesgo:
- Imponer una antigüedad mínima antes de adoptar lanzamientos nuevos.
- Restringir los scripts de instalación.
- Fijar las dependencias por versión e integridad.
- Vigilar cambios directos en ramas protegidas.
- Mantener un inventario de lo que realmente ejecutan los runners.
En este caso, un periodo de espera de varios días habría dejado fuera casi todas las versiones contaminadas, que se publicaron y detectaron en cuestión de horas.
El conteo puede seguir moviéndose mientras npm y los mantenedores terminan la limpieza. Al corte de las 16:20 UTC, la medida comprobada era de 435 paquetes y 1,557 versiones. Para un proyecto que ejecutó una versión afectada, la prioridad no es simplemente reinstalar el paquete: primero debe retirar la persistencia y el monitor de tokens, y después revocar y rotar las credenciales desde una computadora limpia. La atestación válida describe cómo se construyó la publicación, no si el commit que la originó estaba autorizado.
