Aikido

Keyv y sus amigos se han visto afectados por un ataque activo a la cadena de suministro de Shai-Hulud

Escrito por
Ilyas Makari

El 4 de agosto de 2026, unos atacantes se hicieron con el control de la cuenta de GitHub del responsable del mantenimiento de keyv, una biblioteca de almacenamiento de clave-valor con aproximadamente 127 millones de descargas semanales en npm, y aprovechó ese acceso para introducir un gusano que robaba credenciales en toda la familia de paquetes. El mismo responsable es propietario de almacenable en caché (29 millones de descargas al mes), caché plana (565 millones de descargas al mes), caché de entradas de archivo (557 millones de descargas al mes) y otras muchas utilidades de almacenamiento en caché muy utilizadas, todas las cuales se vieron afectadas por el mismo ataque.

El ataque se llevó a cabo introduciendo archivos maliciosos directamente en el principal rama y, a continuación, crear inmediatamente una nueva versión, lo que significa que las versiones maliciosas se publicaron en npm con una procedencia válida firmada por GitHub Actions.

Los paquetes afectados son los siguientes:

  • keyv 6,0,0 (604 M al mes)
  • caché plana 6,1,24 (580 millones al mes)
  • caché de entradas de archivo 11,1,6 (571 millones al mes)
  • solicitud almacenable en caché 13,0,20 (137 M al mes)
  • almacenable en caché 2,5,1 (30 millones al mes)
  • @cacheable/memoria 2,2,1 (28 millones al mes)
  • gestor de caché 7,2,10 (16 millones al mes)
  • @cacheable/node-cache 3.1.2 (6 millones al mes)
  • @cacheable/utils 2,5,1 (34 millones al mes)
  • @cacheable/net 2,1,1 (3,7 mil al mes)
  • ecto 5,0,1 (4,5 K al mes)

También estamos observando una propagación comunitaria muy activa de este gusano de la cadena de suministro a otros responsables de mantenimiento y paquetes, incluidas organizaciones importantes:

  • @deliveroo/reevent 1.0.1
  • @or-sdk/invitaciones 1.4.9
  • @picsart/ai-sdk 3.32.2
  • @qlik/embed-runtime 1.6.4
  • picasso.js 2.11.6

Actualización — 4 de agosto de 2026, 13:37 CEST: Al menos 434 paquetes (que abarcan 1 381 versiones) se han visto afectados por el gusano, con un total combinado de más de 2 000 millones de instalaciones mensuales en el momento de redactar esta noticia.

Qué ocurrió

Cada paquete de la familia recibió dos archivos nuevos, setup.mjs y Math_Symbol.js, junto con un "preinstall": "node setup.mjs" entrada añadida a cada uno package.json. Cualquiera que haya corrido npm install si se hubiera aplicado a una versión afectada, habría tenido setup.mjs se ejecutan automáticamente antes de que finalice su instalación.

setup.mjs es un dropper muy ofuscado. Su única función es descargar de forma silenciosa el entorno de ejecución 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 muy ofuscado que contiene programas de robo de credenciales que recopilan información confidencial del entorno de la víctima, cifran los datos obtenidos y los envían a un repositorio público de GitHub cuya descripción reza: «Shai-Hulud: Ya estamos otra vez». La carga útil también incluye una función de propagación similar a la de un gusano para infectar los paquetes de otros responsables que hayan instalado alguno de los paquetes comprometidos.

Qué roba

El Math_Symbol.js El archivo implementa un conjunto de extractores de credenciales, cada uno de los cuales se dirige a un almacén de secretos diferente en el equipo de la víctima.

Tokens de npm

Lee ~/.npmrc y analiza el sistema de archivos en busca de cualquier otro .npmrc archivos. Extractos authToken valores y cualquier //registry.*:_authToken=... entradas. Valida cada token en tiempo real con respecto a registry.npmjs[.]org/-/whoami antes de la exfiltración.

Tokens de GitHub

Se han seleccionado tres formatos de tokens: los PAT clásicos (ghp_...) y tokens de OAuth (gho_...), tokens de servidor a servidor de la aplicación de GitHub (ghs_...), y tokens JWT OIDC. Entre las fuentes se incluyen ~/.config/gh/hosts.yml, las variables de entorno y un análisis del sistema de archivos.

En los runners de GitHub Actions, la carga útil también ejecuta un comando de shell que lee directamente la memoria del proceso del runner para volcar todo el almacén de secretos. Lee ACTIONS_ID_TOKEN_REQUEST_TOKEN y ACTIONS_ID_TOKEN_REQUEST_URL para robar los tokens OIDC que se utilizan para publicar en npm.

Credenciales de AWS

  • ~/.aws/credentials y ~/.aws/config, analizando todos los perfiles con nombre
  • AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, y AWS_SESSION_TOKEN variables de entorno
  • Servicio de metadatos de instancias de EC2 en 169.254.169.254, probando primero con IMDSv2 y, en su defecto, con IMDSv1
  • Punto final de metadatos de contenedores de ECS en 169.254.170.2
  • AWS Secrets Manager, llamando a... secretsmanager:ListSecrets en varias regiones para identificar y extraer toda la información confidencial almacenada en ellas

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 por orden de prioridad: la VAULT_TOKEN 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 Kubernetes mediante el JWT de la cuenta de servicio robada y el punto final de autenticación de Vault para AWS IAM utilizando cualquier credencial robada de AWS. Tras obtener un token, enumera todos los almacenes clave-valor 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 la API de Stripe (tanto de prueba como de producción, sk_ y pk_ prefijos) 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 que distingue entre plataformas (macOS frente a Linux) ejecuta aproximadamente 200 patrones «glob» en todo el sistema de archivos, centrándose, entre otras cosas, en:

  • .env, .env.*, y .envrc archivos
  • Archivos de claves privadas (*.pem, *.key, *.p12, *.pfx, *.jks)
  • Claves SSH y configuración (id_rsa, id_ed25519, .ssh/config)
  • Los archivos de estado de Terraform y .tfvars
  • Archivos de credenciales del registro de Docker (docker/config.json)
  • Las bases de datos de KeePass (*.kdbx)
  • Configuraciones de VPN (*.ovpn)
  • Archivos de configuración del IDE, entre los que se incluyen .vscode/tasks.json y .claude/settings.json

Se omiten los archivos de más de 5 MB. Se utilizan hasta 64 lecturas simultáneas. Además, se aplica un motor genérico de expresiones regulares a todos los archivos analizados, que marca las claves privadas PEM, las claves públicas SSH, las claves de almacenamiento de Azure, las cadenas de conexión a bases de datos con credenciales incrustadas y otros elementos genéricos clave=valor patrones que coincidan con nombres comunes de campos secretos.

Exfiltración

Una vez recopiladas las credenciales, la carga útil cifra todo el paquete antes de enviarlo a cualquier destino. Solo el atacante, que posee la clave privada correspondiente de RSA , puede descifrar lo que se sube. Esto significa que los datos robados se encuentran a la vista de todos en una infraestructura pública, pero son ilegibles para cualquier otra persona.

El principal destino 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 informe, GitHub contiene aproximadamente 1.300 repositorios públicos que coinciden con esa cadena, cada uno de los cuales sirve como punto de entrega del paquete de credenciales cifradas de una víctima.

Si falla la subida a GitHub, la carga útil recurre a https://npm-cache[.]com:443/router, un dominio registrado el 22 de mayo de 2026 que no parece tener ninguna finalidad legítima. Este dominio se obtiene de forma dinámica a partir de un contrato inteligente de Ethereum en 0xE1f2395ee43e45A1556EC6438a88c31B83493103, lo que permite al atacante cambiar la infraestructura en cualquier momento sin modificar la carga útil.

Gusano autorreplicante

Además 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 de un archivo tarball de npm

Utilizando el token de npm robado, la carga útil llama a https://registry.npmjs[.]org/-/npm/v1/tokens para enumerar todos los paquetes sobre los que ese token tiene derechos de publicación y, a continuación, descarga y descomprime el archivo tar correspondiente a cada uno de ellos. Antes de volver a publicarlos, realiza las siguientes modificaciones:

  • Aumenta en uno el número de versión del parche (p. ej., 1.2.3 se convierte en 1.2.4)
  • Añade "preinstall": "node setup.mjs" a los scripts del paquete
  • Inyecciones setup.mjs y math_init.js (funcionalmente idéntico a Math_Symbol.js) en el paquete

A continuación, vuelve a empaquetar y publica el archivo tar modificado en el registro.

Así es como se propaga el gusano más allá del administrador original. Tras el ataque inicial al keyv Como responsables del mantenimiento, hemos observado que más de 400 paquetes se han visto afectados por la propagación en la comunidad. 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 infectados por el gusano de inyección de archivos tar de npm.

Infección de un repositorio de GitHub

Cuando la carga útil encuentra un ghs_ token, realiza el commit en todas las ramas a las que puede acceder, hasta un máximo de 50 ramas por repositorio, procesando primero las ramas más recientes y omitiendo dependabot y copiloto ramas. Añade ganchos maliciosos a .claude/settings.json y .vscode/tasks.json de modo que la carga útil 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 desde allí, sin necesidad de ejecutar «npm install». Las confirmaciones se han creado como Claude por correo electrónico claude@users.noreply.github[.]com y difundir el mensaje tarea: actualizar la configuración, mezclándose con las modificaciones 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
  • setup.mjs (versión de transmisión comunitaria)
    • SHA-256 fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb
  • Math_Symbol.js y math_init.js
    • SHA-256 9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc

Red

  • https://npm-cache[.]com:443/router — punto final de exfiltración
  • eth-mainnet.nodereal[.]io solicitud que contiene 0xE1f2395ee43e45A1556EC6438a88c31B83493103

Otros

  • Cualquier repositorio público de GitHub cuya descripción contenga «Shai-Hulud: Ya estamos otra vez» — Estos son los repositorios controlados por los atacantes que se utilizan para la exfiltración de credenciales.

Compartir:

https://www.aikido.dev/blog/keyv-and-friends-compromised-in-npm-supply-chain-attack

Escanear en busca de malware

Empieza gratis
4.7/5
¿Cansado de los falsos positivos?

Prueba Aikido como otros 100k.
Empiece ahora
Obtenga un recorrido personalizado

Con la confianza de más de 100k equipos

Reservar ahora
Escanee su aplicación en busca de IDORs y rutas de ataque reales

Con la confianza de más de 100k equipos

Empezar a escanear
Vea cómo el pentesting de IA prueba su aplicación

Con la confianza de más de 100k equipos

Empezar a probar

Asegura tu plataforma ahora

Protege tu código, la nube y el entorno de ejecución en un único sistema central.
Encuentra y corrije vulnerabilidades de forma rápida y automática.

No se requiere tarjeta de crédito | Resultados del escaneo en 32 segundos.