Threat Advisories

ChainDrop: el ataque a la cadena de suministro de npm mediante Keyv y Cacheable

Escrito por Integrity360 | 5 ago 2026, 13:26:50

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.