El 4 de agosto de 2026, unos atacantes vulneraron la cuenta de GitHub del mantenedor de keyv, una librería de almacenamiento clave-valor con aproximadamente 127 millones de descargas semanales en npm, y utilizó ese acceso para inyectar un gusano de robo de credenciales en toda la familia de paquetes. El mismo mantenedor posee cacheable (29M de descargas al mes), flat-cache (565M de descargas al mes), file-entry-cache (557 millones de descargas al mes) y varias otras utilidades de caché muy utilizadas, todas las cuales se vieron afectadas por el mismo ataque.
El compromiso se llevó a cabo enviando archivos maliciosos directamente a principal rama y lanzando inmediatamente una nueva versión, lo que significa que las versiones envenenadas se publicaron en npm con procedencia válida firmada por GitHub Actions.
Los paquetes comprometidos incluyen:
keyv6.0.0 (604M/mes)flat-cache6.1.24 (580M/mes)file-entry-cache11.1.6 (571M/mes)cacheable-request13.0.20 (137M/mes)cacheable2.5.1 (30M/mes)@cacheable/memory2.2.1 (28M/mes)cache-manager7.2.10 (16M/mes)@cacheable/node-cache3.1.2 (6M/mes)@cacheable/utils2.5.1 (34M/mes)@cacheable/net2.1.1 (3,7K/mes)ecto5.0.1 (4,5K/mes)
También estamos observando una propagación muy activa de este gusano de la cadena de suministro en la comunidad hacia otros mantenedores y paquetes, incluidas grandes organizaciones:
@deliveroo/reevent1.0.1@or-sdk/invitations1.4.9@picsart/ai-sdk3.32.2@qlik/embed-runtime1.6.4picasso.js2.11.6
Actualización — 5 de agosto de 2026, 13:15 CEST: Al menos 444 paquetes (en 1,381 versiones) se han visto comprometidos por el gusano, con un total combinado de más de 2.000 millones de instalaciones mensuales en el momento de redactar este artículo.
Qué ocurrió
Todos los paquetes de la familia recibieron dos archivos nuevos, setup.mjs y Math_Symbol.js, junto con una "preinstall": "node setup.mjs" entrada añadida a cada package.json. Cualquiera que ejecutara npm install en una versión afectada habría visto setup.mjs ejecutarse automáticamente antes de que se completara su instalación.
setup.mjs es un cuentagotas (dropper) altamente ofuscado. Su única tarea es descargar silenciosamente el runtime de JavaScript Bun desde github[.]com/oven-sh/bun/releases/download/bun-v1.3.13/ y utilizarlo para ejecutar la carga útil real, Math_Symbol.js:
execFileSync(<bun binary>, ['<script_dir>/Math_Symbol.js'], {
stdio: 'inherit',
cwd: <script_dir>
})El Math_Symbol.js es un archivo JavaScript de 728 KB altamente ofuscado que contiene robacredenciales que extraen secretos del entorno de la víctima, cifran los hallazgos y los exfiltran a un repositorio público de GitHub cuya descripción dice "Shai-Hulud: Here We Go Again". La carga útil también contiene funcionalidad de propagación similar a un gusano para infectar paquetes de otros desarrolladores que hayan instalado uno de los paquetes comprometidos.
Qué roba
El Math_Symbol.js el archivo implementa un conjunto de extractores de credenciales, cada uno dirigido a un almacén de secretos diferente en la máquina de la víctima.
Tokens de npm
Lee ~/.npmrc y escanea el sistema de archivos en busca de cualquier otro .npmrc archivos. Extrae authToken valores y cualquier //registry.*:_authToken=... entradas. Valida cada token en tiempo real contra registry.npmjs[.]org/-/whoami antes de realizar la exfiltración.
Tokens de GitHub
Se apunta a tres formatos de token: PAT clásicos (ghp_...) y tokens de OAuth (gho_...), tokens de servidor a servidor de GitHub App (ghs_...) y tokens JWT OIDC. Las fuentes incluyen ~\.config\gh\hosts.yml, variables de entorno y un análisis del sistema de archivos.
En los ejecutores de GitHub Actions, el payload también ejecuta un comando de shell que lee directamente la memoria del proceso del ejecutor para volcar todo el almacenamiento de secretos. Lee ACTIONS_ID_TOKEN_REQUEST_TOKEN y ACTIONS_ID_TOKEN_REQUEST_URL para robar tokens OIDC utilizados para la publicación en npm.
Credenciales de AWS
~/.aws/credentialsy~\.aws\config, analizando todos los perfiles con nombreAWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY, yAWS_SESSION_TOKENvariables de entorno- el servicio de metadatos de instancias de EC2 en
169.254.169.254, probando primero IMDSv2 con un respaldo a IMDSv1 - el punto de conexión de metadatos de contenedores de ECS en
169.254.170.2 - AWS Secrets Manager, llamando a
secretsmanager:ListSecretsen múltiples regiones para enumerar y exfiltrar todos los secretos almacenados allí
secretos de Kubernetes
Lee el token de la cuenta de servicio, el certificado de la CA y el espacio de nombres desde /var/run/secrets/kubernetes.io/serviceaccount/. Utiliza el token de la cuenta de servicio para consultar directamente la API de Kubernetes y recuperar todos los secretos del espacio de nombres. También se dirige a KUBECONFIG y ~/.kube/config.
tokens de HashiCorp Vault
Comprueba seis fuentes en orden de prioridad: VAULT_TOKEN la variable de entorno ~/.vault-token, la ruta del ejecutor de GitHub Actions /home/runner/.vault-token, varias rutas de contenedores conocidas, un inicio de sesión de autenticación de Kubernetes mediante el JWT de la cuenta de servicio robada y el punto de enlace de autenticación AWS IAM de Vault utilizando cualquier credencial de AWS robada. Tras obtener un token, enumera todos los almacenes KV a través de /v1/sys/mounts y lee todos los secretos de las rutas KV v1 y v2.
Tokens de Stripe y Slack
Busca claves de API de Stripe (tanto de prueba como de producción, con los prefijos sk_ y pk_ ) y tokens de Slack (xox[baprs]-...) en todos los archivos analizados por el escáner del sistema de archivos.
Análisis genérico del sistema de archivos
Un escáner adaptado a la plataforma (macOS frente a Linux) ejecuta aproximadamente 200 patrones glob en el sistema de archivos, dirigiéndose, entre otros, a:
.env,.env.*, y.envrcarchivos- Archivos de clave privada (
*.pem,*.key,*.p12,*.pfx,*.jks) - Claves y configuración SSH (
id_rsa,id_ed25519,.ssh/config) - Archivos de estado de Terraform y
.tfvars - Archivos de credenciales de registros de Docker (
docker/config.json) - Bases de datos KeePass (
*.kdbx) - Configuraciones de VPN (
*.ovpn) - Archivos de configuración de IDE que incluyen
.vscode/tasks.jsony.claude/settings.json
Se omiten los archivos de más de 5 MB. Se utilizan hasta 64 lecturas simultáneas. También se aplica un motor de expresiones regulares genérico en todos los archivos analizados, que marca claves privadas PEM, claves públicas SSH, claves de almacenamiento de Azure, cadenas de conexión de bases de datos con credenciales incrustadas y clave=valor patrones genéricos que coinciden con nombres comunes de campos de secretos.
Exfiltración
Una vez recopiladas las credenciales, el payload cifra todo el paquete antes de enviarlo a cualquier parte. Solo el atacante, que posee la clave privada RSA correspondiente, puede descifrar lo que se sube. Esto significa que los datos robados permanecen a la vista de todos en una infraestructura pública, pero resultan ilegibles para cualquier otra persona.
El destino principal de la exfiltración es un repositorio público de GitHub cuya descripción contiene la cadena "Shai-Hulud: Here We Go Again". En el momento de redactar este artículo, GitHub contiene aproximadamente 1300 repositorios públicos que coinciden con esa cadena, y cada uno sirve como punto de depósito para el paquete de credenciales cifradas de una víctima.

Si la subida a GitHub falla, el payload recurre a https://npm-cache[.]com:443/router, un dominio registrado el 2026-05-22 que parece no tener ningún propósito legítimo. Este dominio se obtiene dinámicamente de un contrato inteligente de Ethereum en 0xE1f2395ee43e45A1556EC6438a88c31B83493103, lo que permite al atacante rotar la infraestructura en cualquier momento sin tocar la carga útil.
Gusano autorreplicante
Más allá de robar credenciales, la carga útil las utiliza activamente para propagar el malware a otros mantenedores y repositorios. Cuenta con dos vectores de infección distintos.
infección del tarball de npm
Utilizando el token de npm robado, el payload llama a https://registry.npmjs[.]org/-/npm/v1/tokens para enumerar cada paquete sobre el que ese token tiene permisos de publicación, y luego descarga y desempaca el tarball actual de cada uno. Antes de republicar, realiza las siguientes modificaciones:
- Incrementa la versión de parche en uno (p. ej.
1.2.3se convierte en1.2.4) - Añade
"preinstall": "node setup.mjs"a los scripts del paquete - Inyecta
setup.mjsymath_init.js(funcionalmente idéntico aMath_Symbol.js) en el paquete
A continuación, vuelve a empaquetar y publicar el tarball modificado en el registro.
Así es como el gusano se propaga más allá del mantenedor original. Tras el compromiso inicial del mantenedor de keyv , observamos que más de 400 paquetes se infectaron a través de la propagación comunitaria. Estas infecciones de segunda generación se pueden identificar por el uso de math_init.js en lugar de Math_Symbol.js, ya que fueron propagadas por el gusano de inyección de tarballs de npm.
Infección de repositorio de GitHub
Cuando el payload encuentra un token de ghs_ , hace commit en cada rama a la que puede acceder, hasta 50 ramas por repositorio, empezando por las ramas activas más recientemente y omitiendo las ramas dependabot y copilot . Añade ganchos maliciosos a .claude/settings.json y .vscode/tasks.json para que el payload se ejecute automáticamente la próxima vez que cualquier desarrollador abra el repositorio en VS Code o inicie una sesión de Claude Code en su interior, sin necesidad de ejecutar npm install. Los commits se firman como claude con el correo electrónico claude@users.noreply.github[.]com y llevan el mensaje chore: update config, integrándose con los commits reales.
Cómo Aikido detecta esto
Si es usuario de Aikido, revise su feed central y filtre por problemas de malware. Esto aparecerá como un problema crítico de 100/100. Aikido realiza reescaneos cada noche, pero recomendamos activar un reescaneo manual ahora.
Si aún no es usuario de Aikido, puede crear una cuenta y conectar sus repositorios. Nuestra cobertura de malware está incluida en el plan gratuito, sin necesidad de tarjeta de crédito.
Para una cobertura más amplia en todo su equipo, Device Protection de Aikido le proporciona visibilidad y control sobre los paquetes de software instalados en los dispositivos de su equipo. Cubre extensiones de navegador, bibliotecas de código, plugins IDE y dependencias de compilación, todo en un solo lugar. Detenga el malware antes de que se instale.
Para una protección futura, considere Aikido Safe Chain (código abierto). Safe Chain se integra en su flujo de trabajo existente, interceptando comandos npm, npx, yarn, pnpm y pnpx y verificando los paquetes con Aikido Intel antes de la instalación.
Indicadores de compromiso (IOC)
Archivos
setup.mjs- SHA-256
54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668
- SHA-256
setup.mjs(versión distribuida por la comunidad)- SHA-256
fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb
- SHA-256
Math_Symbol.jsymath_init.js- SHA-256
9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc
- SHA-256
Red
https://npm-cache[.]com:443/router— endpoint de exfiltracióneth-mainnet.nodereal[.]iosolicitud que contiene0xE1f2395ee43e45A1556EC6438a88c31B83493103
Otros
- Cualquier repositorio público de GitHub con una descripción que contenga
"Shai-Hulud: Here We Go Again"— estos son los repositorios controlados por el atacante utilizados para la exfiltración de credenciales.