El 4 de agosto de 2026, un sofisticado ataque a la cadena de suministro de software, denominado «ChainDrop», afectó al ecosistema npm tras el acceso no autorizado a la cuenta de GitHub de un responsable del mantenimiento de los paquetes de código abierto Keyv y Cacheable, de amplio uso . El gusano autopropagable resultante se extendió a más de 2 251 versiones de 452 paquetes distintos, con aproximadamente 2 000 millones de descargas mensuales, lo que afectó a organizaciones como Deliveroo, Ornikar, OneReach, Picsart, Qlik y ServiceTitan.
Keyv es un sencillo almacén de clave-valor para Node.js compatible con múltiples backends, mientras que Cacheable es su marco de caché HTTP subyacente. Juntos, estos paquetes constituyen la infraestructura de almacenamiento en caché fundamental en todo el ecosistema de JavaScript. Ambos paquetes son mantenidos por el mismo desarrollador, cuya cuenta de GitHub fue comprometida por el autor del ataque.
El atacante publicó la primera versión maliciosa, keyv@6.0.0, aproximadamente a las 9:00 UTC del 4 de agosto de 2026. El malware es un descendiente de la familia «Mini» Shai-Hulud; la atribución a TeamPCP sigue sin confirmarse. El gusano se propagó rápidamente recopilando tokens de npm y GitHub de entornos comprometidos y utilizándolos para publicar nuevas versiones contaminadas. Socket verificó 2 251 versiones contaminadas en 452 paquetes.
La carga útil roba una amplia gama de datos confidenciales, entre los que se incluyen credenciales de la nube, secretos de CI/CD, tokens de desarrolladores, archivos de configuración de IA y carteras de criptomonedas. Además, implanta hooks en Claude Code y Visual Studio Code para una ejecución persistente basada en el IDE. En el momento de redactar este informe, no se han asignado identificadores CVE a esta campaña.
Resumen de la vulnerabilidad
Acceso inicial
El actor malicioso comprometió la cuenta de GitHub del mantenedor de Keyv, lo que le otorgó acceso directo de «push» a los repositorios de código fuente de Keyv, Cacheable, cache-manager, flat-cache, file-entry-cache y numerosos paquetes relacionados dentro del monorepo del mantenedor.
Publicación maliciosa
A partir de las 9:00 UTC, el atacante introdujo cargas maliciosas de persistencia en el IDE en el repositorio de Keyv y, a continuación, publicó keyv@6.0.0 y las versiones maliciosas posteriores. Dado que los paquetes se compilaron y publicaron a través de sus flujos de trabajo legítimos de GitHub Actions, las versiones comprometidas incluían información de procedencia SLSA válida, lo que les permitió eludir los controles de verificación de integridad. Cada paquete contaminado contenía una entrada «preinstall»: «node setup.mjs» en su archivo package.json, lo que provocaba que el dropper se ejecutara automáticamente antes de que se completara cualquier instalación de npm.
Propagación del gusano
La capacidad de autopropagación del malware le permitió infectar paquetes pertenecientes a otros mantenedores cuyos proyectos dependían de un paquete previamente comprometido. Al recopilar tokens de publicación de npm válidos de los ejecutores de CI/CD y de las estaciones de trabajo de los desarrolladores, el gusano volvía a publicar paquetes posteriores con la carga útil maliciosa inyectada, creando una cadena de compromiso en cascada. Este mecanismo permitió que el ataque escapara del espacio de nombres original de Keyv/Cacheable y se propagara más allá de los límites de la organización.
Paquetes afectados
Entre los paquetes principales comprometidos se encontraban:
|
Nombre del paquete |
Versiones maliciosas afectadas |
Versión verificada como segura / restaurada |
|
keyv |
6.0.0 (y versiones posteriores maliciosas) |
5.6.0 |
|
flat-cache |
6.1.24 |
6.1.23 |
|
caché de entradas de archivo |
11.1.6 |
11.1.5 (o versión estable anterior) |
|
solicitud-almacenable-en-caché |
13.0.20 |
13.0.19 (o versión estable anterior) |
|
gestor-de-caché |
7.2.10 |
7.2.9 |
|
cacheable |
2.5.1 |
2.5.0 (o versión estable anterior) |
|
@cacheable/utils |
2.5.1 |
2.5.0 (o versión estable anterior) |
|
@cacheable/memory |
2.2.1 |
2.2.0 (o versión estable anterior) |
|
@cacheable/node-cache |
3.1.2 |
3.1.1 (o versión estable anterior) |
|
@cacheable/net |
2.1.1 |
2.1.0 (o versión estable anterior) |
|
ecto |
5.0.1 |
5.0.0 (o versión estable anterior) |
Posteriormente, el gusano se propagó a paquetes de organizaciones como Ornikar (entre ellos, @ornikar/eslint-config-*, @ornikar/prettier-config, @ornikar/babel-preset-* y muchos más), Qlik (@nebula.js/nucleus), HubSync (@hubsync/web-sdk-react) y muchos otros. Wiz Research mantiene una lista completa y actualizada continuamente en su repositorio público de GitHub.
Importante: El registro de npm sufrió cambios rápidos a lo largo del incidente. A las 17:40 h IST del 4 de agosto, se restauraron como versiones más recientes versiones limpias anteriores de al menos nueve paquetes fundamentales, entre los que se incluyen keyv@5.6.0, flat-cache@6.1.23 y cache-manager@7.2.9. Las organizaciones deben verificar la exposición utilizando las versiones exactas de los paquetes resueltas en los archivos de bloqueo, y no las etiquetas actuales del registro.
Análisis del malware
Descripción general de la carga útil
El malware se despliega en una arquitectura de dos etapas:
Fase 1: setup.mjs (dropper): este script ligero de Node.js actúa como carga útil inicial. Comprueba si existe el entorno de ejecución de JavaScript Bun y, en caso de que no esté presente, descarga la versión v1.3.13 de Bun desde las versiones oficiales de GitHub. A continuación, transfiere la ejecución al paquete principal del ladrón de información antes de eliminar el directorio temporal del entorno de ejecución para reducir el rastro forense.
Etapa 2: Math_Symbol.js / math_init.js (ladrón de información): un paquete compilado de 727 680 bytes, fuertemente ofuscado, que se ejecuta a través del entorno de ejecución Bun. Este es el motor central de recopilación de credenciales. Se han observado en el entorno real ambos nombres de archivo (Math_Symbol.js y math_init.js), que contienen una funcionalidad idéntica con el mismo hash SHA-1.
Objetivos de la recopilación de credenciales
El ladrón de información enumera y extrae de forma sistemática:
- Credenciales en la nube: claves de acceso de AWS, valores del almacén de parámetros de SSM (con WithDecryption: true), secretos de AWS Secrets Manager, credenciales de Azure y GCP
- Secretos de CI/CD: secretos de GitHub Actions, incluida la extracción de valores «isSecret»:true de la memoria de los runners autohospedados; tokens de flujos de trabajo de GitHub
- Tokens de desarrollador: tokens de acceso personal de GitHub (prefijos ghp_, gho_, ghs_), tokens de publicación de npm (prefijo npm_); cada token se valida en tiempo real contra registry.npmjs.org/-/whoami antes de su exfiltración
- Secretos de infraestructura: secretos de Kubernetes de todos los espacios de nombres accesibles, tokens de HashiCorp Vault y secretos KV, credenciales de Terraform, claves SSH y cadenas de conexión a bases de datos
- Configuración de IA e IDE: archivos de configuración de la API de Claude, credenciales de servicios relacionados con la IA
- Carteras de criptomonedas: archivos y claves de carteras locales
- Credenciales de servicios: claves de API de Stripe, Slack, Twilio y otros servicios de terceros
- Entorno completo del proceso: todas las variables de entorno del proceso comprometido
Mecanismos de persistencia
El repositorio de Keyv conservaba ganchos independientes para Claude Code (.claude/settings.json) y Visual Studio Code (.vscode/). Estos mecanismos de persistencia a nivel de IDE pueden ejecutar la carga útil una vez que el usuario confía en el espacio de trabajo o autoriza la configuración del proyecto, lo que proporciona una vía de ejecución secundaria independiente de los scripts del ciclo de vida de «npm install».
El malware también instala un observador de revocación de credenciales —una trampa que activa un controlador local proporcionado por el atacante cuando se renuevan los tokens—. Los responsables de la respuesta deben eliminar este observador antes de renovar cualquier credencial.
Mando y control
El malware recupera sus dominios C2 de un contrato inteligente de Ethereum (StringListStore) mediante una llamada eth_call, en lugar de incrustarlos en la carga útil. Esta resolución C2 basada en la cadena de bloques permite al operador actualizar la infraestructura sin modificar el malware. El historial en cadena muestra que el contrato se configuró inicialmente con tres dominios antes de actualizarse para devolver únicamente npm-cache[.]com. El propietario del contrato recibió fondos de una dirección previamente señalada por actividad fraudulenta.
Exfiltración
Los datos robados se cifran mediante un esquema híbrido de AES-256 y RSA-4096, y luego se exfiltran a través de dos canales:
- Repositorios públicos de GitHub creados con identidades comprometidas, que llevan la descripción «Shai-Hulud: Here We Go Again»
- El dominio npm-cache[.]com
Las primeras entradas en los repositorios de exfiltración contienen la frase intimidatoria: «Si bloqueas esta clave API, se colgarán los servidores de producción en vivo de todos los clientes de terceros».
Indicadores de compromiso (IoC) conocidos
|
Categoría |
Indicador |
Descripción |
|
Dominio |
npm-cache[.]com |
Dominio principal de C2 y exfiltración de datos; alojado a través de Cloudflare |
|
Dominio |
registry[.]npmjs[.]org |
Punto final de validación de tokens utilizado indebidamente para la verificación de credenciales en tiempo real antes de la exfiltración |
|
Dominio |
eth-mainnet[.]nodereal[.]io |
Punto final RPC de Ethereum utilizado para la recuperación de dominios C2 basados en contratos inteligentes |
|
Dominio |
go[.]getblock[.]io |
Punto final RPC de Ethereum utilizado para la recuperación de dominios C2 basados en contratos inteligentes |
|
Dominio |
eth[.]llamarpc[.]com |
Punto final RPC de Ethereum utilizado para la recuperación del dominio C2 basado en contratos inteligentes |
|
Dominio |
pypi-get[.]com |
Infraestructura adicional controlada por los atacantes |
|
Dominio |
js-mirror[.]com |
Infraestructura adicional controlada por los atacantes |
|
IPv4 |
104[.]21[.]35[.]216 |
Dirección IP de Cloudflare asociada a npm-cache[.]com |
|
IPv4 |
172[.]67[.]167[.]200 |
Dirección IP de Cloudflare asociada a eth[.]llamarpc[.]com |
|
IPv4 |
185[.]44[.]207[.]215 |
Dirección IP asociada a go[.]getblock[.]io RPC de ETH |
|
IPv4 |
35[.]175[.]164[.]77 |
Dirección IP de AWS asociada a eth-mainnet[.]nodereal[.]io |
|
Nombre de archivo |
Math_Symbol.js |
Carga útil ofuscada de un programa de robo de información (727 680 bytes); desplegada a través de scripts de preinstalación del paquete npm |
|
Nombre del archivo |
math_init.js |
Variante de la carga útil del programa de robo de información con funcionalidad idéntica |
|
Nombre del archivo |
setup.mjs |
Dropper de la etapa 1; descarga el entorno de ejecución de Bun y activa la ejecución del infostealer |
|
Hash del archivo (SHA-1) |
35a672cf34b996b91f3e1c28cbf3a05a37e036e4 |
Math_Symbol.js / math_init.js: carga útil del ladrón de información |
|
Hash del archivo (SHA-1) |
686aa40d0fc22c8d569494543a0f891f359f2f99 |
setup.mjs ubicado en el directorio .claude (gancho de Claude Code) |
|
Hash del archivo (SHA-1) |
f525d52ceb966516686b482d3dc0137028cc6a63 |
setup.mjs ubicado en el directorio .vscode (gancho de VS Code) |
|
User-Agent |
Bun/1.3.13 |
Cadena de agente de usuario HTTP utilizada durante la descarga en tiempo de ejecución de Bun y las comunicaciones C2 |
|
Ruta del sistema de archivos |
/tmp/bun-dl-*/ |
Directorio temporal utilizado para la descarga del tiempo de ejecución de Bun y la preparación |
|
Ruta del sistema de archivos |
node_modules/keyv/Math_Symbol.js |
Ubicación de la carga útil del programa de robo de información dentro del árbol de paquetes instalado |
|
Cadena |
Shai-Hulud: Here We Go Again |
Descripción del repositorio de GitHub utilizada para los repositorios de exfiltración |
|
Cadena |
Si bloqueas esta clave API, se colgarán los servidores de producción en vivo de todos los clientes de terceros |
Cadena de intimidación en las primeras confirmaciones de exfiltración |
Detecciones y búsqueda de amenazas
Los equipos de seguridad deben implementar las siguientes medidas de detección:
- Auditoría de dependencias: Analiza todos los archivos package-lock.json, yarn.lock, pnpm-lock.yaml y bun.lockb en busca de los paquetes y versiones afectados. No te fíes de las etiquetas actuales del registro; utiliza exclusivamente las versiones resueltas de los archivos de bloqueo.
- Indicadores del sistema de archivos: Busca en las estaciones de trabajo de desarrollo, los ejecutores de CI/CD y los servidores de compilación la presencia de Math_Symbol.js, math_init.js y setup.mjs dentro de los árboles node_modules y en los directorios temporales que coincidan con /tmp/bun-dl-*/.
- Supervisión de la red: Alerta ante conexiones salientes y consultas DNS dirigidas a npm-cache[.]com, pypi-get[.]com, js-mirror[.]com y los puntos finales RPC de Ethereum enumerados en los IOC anteriores. Supervisa la presencia de la cadena de agente de usuario «Bun/1.3.13».
- Auditoría de GitHub: Revisa las organizaciones en busca de repositorios públicos inesperados con la descripción «Shai-Hulud: Here We Go Again» o commits que contengan la cadena de intimidación.
- Supervisión de scripts de preinstalación: audita los archivos package.json de todos los proyectos en busca de entradas inesperadas de scripts de preinstalación que hagan referencia a setup.mjs.
- Confianza del espacio de trabajo del IDE: comprueba si hay configuraciones no autorizadas en .claude/settings.json y .vscode/ que puedan cargar cargas útiles externas.
Medidas de mitigación y corrección
Acciones inmediatas
- Identificar y eliminar las versiones afectadas: Auditar todos los entornos y eliminar cualquier instalación de las versiones maliciosas del paquete. Actualizar a versiones limpias y verificadas.
- Elimina primero el observador de revocación de credenciales: No realices la rotación de tokens antes de eliminar el malware. La carga maliciosa instala un observador que se activa ante la revocación de un token. La rotación prematura de credenciales puede ejecutar un controlador proporcionado por el atacante. Elimina el malware de los sistemas afectados antes de proceder a la rotación de credenciales.
- Asumir que todas las credenciales están comprometidas: si se ha instalado alguna versión afectada, considerar robadas todas las credenciales a las que se pueda acceder desde ese entorno. Esto incluye credenciales en la nube, tokens de npm, PAT de GitHub, claves SSH, secretos de CI/CD, credenciales de bases de datos y claves de API de terceros.
- Rota todas las credenciales expuestas: tras eliminar el malware, rota: todos los tokens de publicación de npm, los tokens de acceso personal (PAT) y los tokens OAuth de GitHub, las claves de acceso de AWS IAM, las credenciales de entidades de servicio de Azure y GCP, los tokens de cuentas de servicio de Kubernetes, los tokens de HashiCorp Vault, las contraseñas de bases de datos, las claves SSH y las claves de API de terceros (Stripe, Slack, Twilio, etc.).
- Revoca y vuelve a emitir los tokens de npm y GitHub: utiliza las consolas de administración de npm y GitHub para revocar de forma forzada todos los tokens que puedan haber quedado expuestos. Genera nuevos tokens con los ámbitos mínimos necesarios.
Recuperación del sistema
- Reconstruye los sistemas afectados: considera que cualquier estación de trabajo de desarrollo, ejecutor de CI/CD o servidor de compilación en el que se haya instalado un paquete malicioso está potencialmente comprometido. Reconstruye a partir de una imagen o instantánea que se sepa que está limpia.
- Purga las cachés locales: Borra la caché de npm (npm cache clean --force), la caché de Yarn, el almacén de pnpm y la caché de Bun para eliminar los paquetes maliciosos almacenados en caché.
- Elimina y vuelve a crear los entornos virtuales: no te limites a bajar de versión los paquetes afectados. Elimina los directorios `node_modules` y vuelve a crear por completo los entornos virtuales de Python.
Revisión de la nube y del código fuente
- Audita los entornos en la nube: revisa AWS CloudTrail, los registros de actividad de Azure y los registros de auditoría de GCP en busca de llamadas a la API no autorizadas que se hayan producido durante el periodo en que se instaló el paquete. Comprueba si se han creado usuarios o roles de IAM inesperados, si se ha accedido al almacén de parámetros de SSM con descifrado y si se ha accedido a Secrets Manager.
- Revisa los registros de auditoría de GitHub: examina los registros de auditoría de la organización en busca de creaciones inesperadas de repositorios, generación de tokens y modificaciones en los flujos de trabajo.
- Busca repositorios de exfiltración: busca repositorios públicos creados con las identidades de tu organización que incluyan la descripción «Shai-Hulud: Here We Go Again».
Fortalecimiento de la cadena de suministro
- Fijación de dependencias: Utilice la fijación de versiones exactas en todos los archivos package.json y incorpore los archivos de bloqueo al control de versiones.
- Actualización a npm 12: npm 12 bloquea de forma predeterminada los scripts del ciclo de vida de las dependencias no aprobadas, lo que impide la ejecución automática de scripts de preinstalación. Las versiones anteriores de npm siguen siendo vulnerables.
- Aplicar SLSA y OIDC: Exigir certificados de procedencia SLSA y verificarlos en el momento de la compilación. Sin embargo, ten en cuenta que esta campaña ha demostrado que la procedencia por sí sola es insuficiente cuando el atacante controla el proceso de compilación; la procedencia debe combinarse con la verificación de identidad.
- Activa las listas de dependencias permitidas: restringe las fuentes y versiones de paquetes permitidas mediante herramientas como Socket, Semgrep Supply Chain o las políticas de tu registro de artefactos.
- Implementa la verificación de la integridad de los paquetes: utiliza las firmas de auditoría de npm y verifica los hash de integridad del registro cuando sea posible.
- Activa la autenticación multifactorial (MFA) en todas las cuentas de mantenedores: exige la autenticación multifactorial respaldada por hardware para todas las cuentas de npm y GitHub con permisos de publicación.
- Bloquee los dominios maliciosos: añada los indicadores de compromiso (IOC) enumerados anteriormente a sus listas de bloqueo del perímetro de red y a sus soluciones de filtrado de DNS.
Si le preocupa alguna de las amenazas descritas en este boletín o necesita ayuda para determinar qué medidas debe tomar para protegerse de las amenazas más graves a las que se enfrenta su organización, póngase en contacto con su gestor de cuentas o, si lo prefiere, contáctenos para averiguar cómo puede proteger su organización.