Mini Shai-Hulud AntV Supply Chain Attack Campaign es la última oleada del gusano Mini Shai-Hulud, descubierta el 19 de mayo de 2026, que comprometió la cuenta npm atool (i@hust.cc), mantenedora de la biblioteca timeago.js (1.5 millones de descargas semanales) y miembro del equipo de mantenimiento de AntV, el conjunto de tecnologías de visualización de datos de código abierto de Alibaba (que incluye @antv/g2, @antv/g6, @antv/l7, @antv/s2, entre otros). El atacante publicó 637 versiones maliciosas en 317 paquetes en una ráfaga automatizada de 22 minutos, distribuidas en dos oleadas (01:56 UTC y 02:06 UTC). Cada paquete comprometido contiene una carga útil ofuscada de 486-498 KB (ejecutada con el runtime Bun) que coincide estructuralmente con el toolkit Mini Shai-Hulud utilizado en el compromiso de SAP tres semanas antes: misma arquitectura de escáner, mismo conjunto de expresiones regulares para credenciales, mismo patrón de ofuscación. La carga útil roba credenciales de AWS (toda la cadena: variables de entorno, archivos de configuración, EC2 IMDSv2, ECS metadata, Secrets Manager) , GCP, Azure, tokens de Kubernetes, HashiCorp Vault, GitHub PATs, tokens de npm, claves SSH, y bóvedas de gestores de contraseñas locales (1Password, Bitwarden, pass, gopass). Los datos robados se exfiltran a través de dos canales paralelos: (1) objetos Git confirmados en repositorios públicos de GitHub creados bajo el token comprometido (con User-Agent falsificado como python-requests/2.31.0), y (2) solicitudes HTTPS con cifrado RSA+AES al dominio t.m-kosche[.]com/api/public/otel/v1/traces, disfrazadas como datos de trazado de OpenTelemetry. En entornos CI/CD, el payload intercambia tokens OIDC de GitHub Actions por tokens de publicación de npm, firma artefactos a través de Sigstore (Fulcio + Rekor) utilizando la identidad robada, e inyecta persistencia en .github/workflows/codeql.yml. El malware también secuestra Claude Code y Codex inyectando hooks SessionStart que re-ejecutan el payload en cada sesión de IA, tanto localmente como mediante commits a repositorios accesibles. VS Code recibe un tasks.json con "runOn": "folderOpen" para el mismo efecto. Un servicio persistente systemd / LaunchAgent de macOS llamado kitty-monitor instala una puerta trasera C2 de dead-drop de GitHub (un demonio Python que consulta la API de búsqueda de commits de GitHub cada hora en busca de comandos firmados con RSA-PSS que contienen la palabra clave firedalazer, luego descarga y ejecuta Python arbitrario). Un demonio separado, gh-token-monitor, sondea tokens de GitHub robados en intervalos de 60 segundos. El payload también intenta escapar de contenedores Docker a través del socket del host y propaga la infección a otros proyectos Node.js locales en la misma máquina. La campaña utiliza dos mecanismos de entrega: (A) un hook preinstall directo ("preinstall": "bun run index.js"), y (B) una dependencia opcional que apunta a commits impostores en el repositorio legítimo antvis/G2 (ej., github:antvis/G2#1916faa365f2...). Estos commits huérfanos, con autoría falsificada como el mantenedor legítimo Alexzjt, fueron empujados a un fork y luego eliminados, pero los objetos commit permanecen accesibles por SHA a través del espacio de nombres del repositorio padre. Más de 2500 repositorios públicos (con nombres temáticos de Dune y la descripción invertida niagA oG eW ereH :duluH-iahS que se lee "Shai-Hulud: Here We Go Again") han sido creados utilizando tokens de GitHub robados, confirmando la escala masiva de la exfiltración. Esta campaña representa una de las mayores operaciones coordinadas de ataque a la cadena de suministro de npm jamás documentadas.
Nombres Alternativos / Aliases
- AntV Supply Chain Attack (Por el ecosistema principal afectado)
- atool Account Compromise (Por la cuenta npm comprometida)
- Mini Shai-Hulud AntV Wave (Por la campaña en curso)
- 637 Versions / 317 Packages Attack (Por las estadísticas)
- Shai-Hulud: Here We Go Again (Por la descripción de repositorios)
Fecha de Detección / Actividad
- Compromiso de la Cuenta atool: Antes del 19 de mayo de 2026
- Oleada 1 (Publicaciones Maliciosas): 19 de mayo de 2026, 01:39 - 01:56 UTC (~317 versiones)
- Oleada 2 (Segundo Bump de Versión): 19 de mayo de 2026, 02:05 - 02:06 UTC (~314 versiones)
- Creación de Commits Impostores en antvis/G2: 19 de mayo de 2026, 01:25:54 UTC (commit
1916faa), 01:42:14 UTC (commit 7cb42f5), 01:47:31 UTC (commit dc3d62a, ya recolectado por GC)
- Fecha de Divulgación: 19 de mayo de 2026
- Estado: Activo. El ataque continúa propagándose; más de 2500 repositorios públicos de exfiltración han sido creados.
Actor de Amenaza (Atribución)
- Nombre del Actor: No atribuido a un grupo específico; mismo actor o relacionado con Mini Shai-Hulud (compromiso de SAP, tres semanas antes).
- Tipo de Actor: Grupo de ciberdelincuencia altamente sofisticado con motivación financiera (robo de credenciales de nube y gestores de contraseñas) y posible acceso a recursos significativos.
- Sofisticación: Extremadamente alta. El actor demuestra capacidades excepcionales: (a) compromiso de una cuenta npm con 547 paquetes (
atool), (b) publicación automatizada de 637 versiones en 317 paquetes en 22 minutos (dos oleadas), (c) desarrollo de carga útil de ~500 KB con ofuscación de dos capas (tabla de búsqueda hex-variable + decodificador de cadenas cifradas fc2edea72 con base64+XOR), (d) uso de Bun runtime para ejecución rápida multiplataforma, (e) escáner de credenciales que cubre más de 130 rutas de archivo y toda la cadena de credenciales de AWS (env vars, archivos, IMDSv2, ECS, Secrets Manager), (f) integración con gestores de contraseñas locales (1Password, Bitwarden, pass, gopass), (g) exfiltración dual (GitHub Git Data API + HTTPS a t.m-kosche.com), (h) intercambio de tokens OIDC de GitHub Actions por tokens de publicación de npm, (i) firma de artefactos con Sigstore (Fulcio + Rekor), (j) persistencia multi-capa (workflow de GitHub Actions, hooks de Claude Code/Codex, tasks.json de VS Code, servicios systemd/LaunchAgent), (k) puerta trasera C2 de dead-drop de GitHub con firma RSA-PSS, (l) escape de contenedores Docker, y (m) propagación local a otros proyectos Node.js. El uso de commits impostores en antvis/G2 (autoría falsificada como Alexzjt, un mantenedor legítimo de AntV) sin necesidad de acceso de escritura al repositorio demuestra un profundo conocimiento de la semántica de objetos de Git y del mecanismo de resolución de dependencias github: de npm.
Resumen Ejecutivo
Mini Shai-Hulud AntV Supply Chain Attack Campaign es una operación de escala masiva que comprometió la cuenta npm atool, propiedad de un mantenedor de AntV (el conjunto de visualización de datos de Alibaba), y publicó 637 versiones maliciosas en 317 paquetes en una ráfaga automatizada de 22 minutos el 19 de mayo de 2026. Los paquetes afectados incluyen timeago.js (1.5M descargas/semana), echarts-for-react (3.8M), @antv/scale (2.2M), y cientos de paquetes bajo el espacio de nombres @antv (que abarcan gráficos con @antv/g2, visualización de grafos con @antv/g6, mapeo con @antv/l7, renderizado de hojas de cálculo con @antv/s2, y utilidades de soporte). Cada versión comprometida agrega un hook preinstall ("preinstall": "bun run index.js") y, en 630 de 637 versiones, una dependencia opcional que apunta a un commit impostor en el repositorio legítimo antvis/G2 (ej., github:antvis/G2#1916faa365f2...). Cuando un desarrollador o pipeline CI/CD ejecuta npm install en un proyecto que depende de estos paquetes (especialmente con rangos semver como ^3.0.6), npm resuelve la dependencia opcional, clona el commit de GitHub y ejecuta su script prepare ("prepare": "bun run index.js && exit 1"). El && exit 1 es crucial: hace que la dependencia opcional "falle" silenciosamente, sin errores visibles en los logs, mientras que el payload ya se ha ejecutado en segundo plano. El payload (un archivo index.js de 486-498 KB) está ofuscado con dos capas: (1) una tabla de cadenas de búsqueda hex-variable (_0x1169 resolviendo desde el array _0x5e03), y (2) un decodificador de cadenas cifradas (fc2edea72) que usa base64 + XOR para todos los strings sensibles. El payload se ejecuta con el runtime Bun (instalado automáticamente si no está presente). El escáner de credenciales del payload es masivo, cubriendo más de 130 rutas de archivo y 80+ variables de entorno. En entornos AWS, recorre toda la cadena de resolución de credenciales: variables de entorno (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN), archivos de configuración (~/.aws/credentials, ~/.aws/config), EC2 IMDSv2 (http://169.254.169.254/latest/api/token y luego el endpoint de credenciales de IAM), ECS container metadata (http://169.254.170.2), y AWS Secrets Manager (intenta ListSecrets y GetSecretValue en múltiples regiones). También extrae el ARN de la cuenta desde respuestas STS GetCallerIdentity. En Kubernetes, lee el token de cuenta de servicio de /var/run/secrets/kubernetes.io/serviceaccount/token, las variables KUBERNETES_SERVICE_HOST y KUBECONFIG, y utiliza el User-Agent kubectl/v1.28.0. En HashiCorp Vault, busca tokens en ocho ubicaciones (/var/run/secrets/vault/token, ~/.vault-token, etc.), lee VAULT_ADDR y VAULT_TOKEN, y consulta /v1/auth/aws/login y /v1/auth/kubernetes/login. En gestores de contraseñas locales, el escáner (class fo, categoría password-managers, modo aggressive) detecta y extrae de 1Password (op CLI), Bitwarden (bw CLI), pass (Unix password store), y gopass. Todos los secretos extraídos fluyen a través de un despachador por lotes (class Po) con un umbral de vaciado de 100KB (0x19000 bytes) y se exfiltran a través de dos canales paralelos. Canal 1 (GitHub API): el payload valida el token GitHub robado (GET /user), enumera organizaciones (GET /user/orgs), crea un repositorio público con nombre temático de Dune (dos palabras + número, ej., harkonnen-melange-742) y la descripción invertida niagA oG eW ereH :duluH-iahS ("Shai-Hulud: Here We Go Again"), luego confirma los datos robados como objetos Git usando la API de Git Data (POST /git/blobs, /git/trees, /git/commits, PATCH /git/refs/heads/{branch}). El User-Agent está falsificado como python-requests/2.31.0 para mezclarse con tráfico legítimo de Python. Canal 2 (HTTPS a t.m-kosche.com): el payload envía los datos cifrados con RSA+AES a hxxps://t.m-kosche[.]com/api/public/otel/v1/traces, una ruta elegida deliberadamente para imitar un punto final de recopilación de OpenTelemetry, que a menudo está en la lista blanca de los firewalls corporativos. En entornos CI/CD con GitHub Actions, el payload también realiza un intercambio de tokens OIDC: lee ACTIONS_ID_TOKEN_REQUEST_TOKEN y ACTIONS_ID_TOKEN_REQUEST_URL de las variables de entorno, solicita un token OIDC, lo intercambia en https://registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/<package-name> por un token de publicación de npm, y publica nuevas versiones de paquetes utilizando la identidad del pipeline comprometido. Luego firma los artefactos a través de Sigstore, obteniendo un certificado de Fulcio (https://fulcio.sigstore.dev/api/v2/signingCert) y enviando la procedencia a Rekor (https://rekor.sigstore.dev). Esto significa que los paquetes maliciosos publicados tienen atestación de procedencia SLSA válida y parecen legítimos incluso para sistemas que verifican firmas. El payload también inyecta persistencia en múltiples capas: (1) inyecta un flujo de trabajo malicioso de GitHub Actions en .github/workflows/codeql.yml (con nombre Run Copilot) que serializa ${{ toJSON(secrets) }} y lo sube como artefacto; (2) inyecta hooks SessionStart en .claude/settings.json (Claude Code) que ejecutan node .claude/setup.mjs en cada sesión; (3) inyecta tasks.json en .vscode/tasks.json con "runOn": "folderOpen" que se ejecuta cuando VS Code abre el proyecto; (4) instala un daemon persistente systemd/LaunchAgent llamado kitty-monitor (script Python en ~/.local/share/kitty/cat.py) que consulta la API de búsqueda de commits de GitHub cada hora por la palabra clave firedalazer, extrae una URL y una firma RSA-PSS de los mensajes de commit, verifica la firma con una clave pública RSA hardcodeada de 4096 bits, luego descarga y ejecuta Python arbitrario; (5) instala un daemon gh-token-monitor en ~/.local/bin/gh-token-monitor.sh que sondea tokens de GitHub robados cada 60 segundos. El payload también intenta escapar de contenedores Docker detectando /var/run/docker.sock y creando un contenedor privilegiado con montaje del sistema de archivos del host (Privileged: true, Binds: [host_path:container_path]). Finalmente, el payload se propaga a otros proyectos Node.js locales en la misma máquina a través del escáner class Vo (que corre en el bloque finally de J2()) usando Bun.Glob("**/settings.json") para encontrar configuraciones de Claude Code/Codex e inyectar los hooks. Los commits impostores en antvis/G2 son una técnica particularmente innovadora. El atacante no necesitaba acceso de escritura al repositorio antvis/G2. Simplemente bifurcó el repositorio, creó commits huérfanos (sin padres) con autoría falsificada (git config user.email "Alexzjt@users.noreply.github.com"), y luego eliminó el fork. Los objetos commit permanecieron accesibles porque GitHub comparte almacenamiento de objetos entre un repositorio y sus forks a través de enlaces alternativos (Git alternates). La resolución github:antvis/G2#<sha> de npm extrae el contenido por SHA sin verificar pertenencia a ninguna rama o si el commit todavía existe en un fork. Esto convierte cualquier repositorio popular en un host de payloads encubierto. Más de 2500 repositorios públicos que coinciden con el patrón de nombres de Dune ({X1}-{D1}-{0-999}) y la descripción invertida ya han sido creados, estableciendo un límite inferior de la cantidad de entornos CI/CD comprometidos. Si instaló alguna versión comprometida, asuma que todos los secretos accesibles desde ese entorno están comprometidos: tokens de npm, PAT de GitHub, claves AWS, claves SSH, credenciales de proveedores de nube, tokens de Kubernetes, secretos de Vault, y todas las contraseñas almacenadas en 1Password, Bitwarden, pass o gopass. Rote inmediatamente.
Vectores de Ataque / Métodos de Distribución
- Compromiso de Cuenta npm atool: El atacante comprometió la cuenta npm
atool (i@hust.cc), que mantiene 547 paquetes (incluyendo timeago.js y paquetes en el espacio de nombres @antv).
- Publicación Automatizada de Versiones Maliciosas: El atacante publicó 637 versiones en 317 paquetes en dos oleadas (22 minutos total). Cada versión maliciosa incrementa el número de versión menor en 1 en relación con la versión limpia anterior.
- Hook preinstall Directo:
"preinstall": "bun run index.js" en package.json. Se ejecuta cuando el paquete se instala.
- Dependencia Opcional con Commit Impostor (630/637 versiones):
"optionalDependencies": { "@antv/setup": "github:antvis/G2#1916faa365f2..." }. npm clona el commit, ejecuta su prepare script ("prepare": "bun run index.js && exit 1"). El exit 1 hace que la dependencia opcional "falle" silenciosamente.
- Commits Impostores en antvis/G2: Tres commits huérfanos (sin padres) empujados a un fork de
antvis/G2 (luego eliminado) con autoría falsificada como el mantenedor legítimo Alexzjt. Los objetos commit permanecen accesibles por SHA a través del espacio de nombres del repositorio padre.
- Rangos Semver: Muchos proyectos usan rangos como
^3.0.6 para echarts-for-react, que resuelven automáticamente a la versión maliciosa 3.2.7 (la más alta). La etiqueta latest no se movió, lo que no protege contra la resolución semver.
Técnicas, Tácticas y Procedimientos (TTPs) - MITRE ATT&CK
| Táctica (MITRE ATT&CK)
|
Técnica / Subtécnica
|
Descripción / Implementación
|
| Resource Development (TA0042)
|
Compromise Accounts (T1586) - Cloud Accounts (T1586.003)
|
Compromiso de la cuenta npm atool.
|
| Resource Development (TA0042)
|
Acquire Infrastructure (T1583)
|
Creación de forks de antvis/G2 para commits impostores, registro del dominio t.m-kosche.com.
|
| Initial Access (TA0001)
|
Supply Chain Compromise (T1195) - Compromise Software Supply Chain (T1195.002)
|
Publicación de versiones maliciosas de paquetes npm.
|
| Execution (TA0002)
|
Command and Scripting Interpreter: Node.js (T1059.013)
|
Script preinstall ejecuta bun run index.js.
|
| Execution (TA0002)
|
User Execution: Malicious File (T1204.002)
|
npm install ejecutado por desarrollador o pipeline CI/CD.
|
| Persistence (TA0003)
|
Event Triggered Execution (T1546)
|
Inyección de workflow de GitHub Actions (.github/workflows/codeql.yml), hooks SessionStart de Claude Code, tasks.json de VS Code con runOn: folderOpen.
|
| Persistence (TA0003)
|
Create or Modify System Process (T1543) - Windows Service (T1543.003), Launch Daemon (T1543.004)
|
Servicio systemd kitty-monitor y LaunchAgent com.user.kitty-monitor.plist.
|
| Defense Evasion (TA0005)
|
Obfuscated Files or Information (T1027)
|
Dos capas: tabla de búsqueda hex-variable (_0x1169 + _0x5e03) + decodificador fc2edea72 (base64+XOR).
|
| Defense Evasion (TA0005)
|
Masquerading (T1036)
|
User-Agent python-requests/2.31.0 para tráfico GitHub API; ruta /api/public/otel/v1/traces disfrazada como OpenTelemetry.
|
| Defense Evasion (TA0005)
|
Subvert Trust Controls: Code Signing (T1553.002)
|
Firma de artefactos con Sigstore (Fulcio + Rekor) usando tokens OIDC robados.
|
| Defense Evasion (TA0005)
|
Virtualization/Sandbox Evasion (T1497)
|
El payload evita ejecutarse en Apple Silicon Macs (darwin/arm64).
|
| Defense Evasion (TA0005)
|
Impair Defenses: Disable or Modify Tools (T1562.001)
|
Intento de escalada de privilegios via sudoers (echo 'runner ALL=(ALL) NOPASSWD:ALL' > /mnt/runner).
|
| Credential Access (TA0006)
|
Unsecured Credentials: Credentials In Files (T1552.001)
|
Lectura de ~/.aws/credentials, ~/.kube/config, ~/.npmrc, ~/.ssh/id_rsa, etc.
|
| Credential Access (TA0006)
|
Credentials from Password Stores (T1555.003)
|
Extracción de 1Password (op), Bitwarden (bw), pass, gopass.
|
| Credential Access (TA0006)
|
Cloud Instance Metadata API (T1552.005)
|
Consulta a EC2 IMDSv2 (169.254.169.254) y ECS metadata (169.254.170.2).
|
| Credential Access (TA0006)
|
Steal Application Access Token (T1528)
|
Intercambio de tokens OIDC de GitHub Actions por tokens de publicación de npm.
|
| Discovery (TA0007)
|
System Information Discovery (T1082)
|
Detección de entorno CI (20+ plataformas via variables de entorno).
|
| Discovery (TA0007)
|
File and Directory Discovery (T1083)
|
Escaneo de más de 130 rutas de archivo usando fs.readdirSync y fs.statSync.
|
| Collection (TA0009)
|
Archive Collected Data (T1560) - Archive via Utility (T1560.001)
|
Los datos robados se comprimen en JSON y se envían por lotes.
|
| Command and Control (TA0011)
|
Application Layer Protocol: Web Protocols (T1071.001)
|
HTTPS a api.github.com y t.m-kosche.com.
|
| Command and Control (TA0011)
|
Web Service (T1102) - Dead Drop Resolver (T1102.001)
|
GitHub commit search API como dead drop C2 (palabra clave firedalazer).
|
| Command and Control (TA0011)
|
Encrypted Channel (T1573)
|
RSA+AES para exfiltración HTTPS; RSA-PSS para comandos C2.
|
| Exfiltration (TA0010)
|
Exfiltration Over Web Service (T1567)
|
Subida de datos robados a repositorios públicos de GitHub (API de Git Data).
|
| Lateral Movement (TA0008)
|
Container Administration Command (T1609)
|
Escape de contenedor Docker via socket (/var/run/docker.sock) y creación de contenedor privilegiado.
|
| Impact (TA0040)
|
Data Destruction (T1485)
|
Comando rm -rf ~/; rm -rf ~/Documents presente en strings decodificados (posible dead man's switch).
|
| Impact (TA0040)
|
Financial Theft (sin código ATT&CK específico)
|
Venta de credenciales robadas (incluyendo gestores de contraseñas).
|
Flujo de Ataque (Cyber Kill Chain)
- Reconocimiento: El atacante identifica la cuenta npm
atool (mantenedor de timeago.js y miembro del equipo AntV) como objetivo de alto valor debido a la cantidad de paquetes (547) y su alcance (millones de descargas).
- Armamento: El atacante desarrolla el payload de ~500 KB con ofuscación de dos capas, configura la infraestructura C2 (
t.m-kosche.com), crea commits impostores en un fork de antvis/G2 (con autoría falsificada como Alexzjt), y prepara scripts de publicación automatizada.
- Entrega: El 19 de mayo de 2026, el atacante utiliza la cuenta comprometida para publicar 637 versiones maliciosas en 317 paquetes en dos oleadas de 22 minutos.
- Explotación: Un desarrollador o pipeline CI/CD ejecuta
npm install en un proyecto con una dependencia afectada (directa o transitiva). npm clona el commit impostor de antvis/G2 y ejecuta su script prepare, que ejecuta bun run index.js && exit 1. El exit 1 causa que la dependencia opcional falle silenciosamente.
- Instalación: Bun runtime ejecuta el payload descifrado. El payload: (a) detecta si está en un entorno CI (20+ señales), (b) extrae credenciales de variables de entorno, archivos y APIs cloud (AWS IMDSv2, ECS), (c) escanea el sistema de archivos en busca de más de 130 rutas de credenciales, (d) extrae bóvedas de 1Password, Bitwarden, pass, gopass, (e) intenta escapar del contenedor Docker si está disponible.
- Comando y Control (C2): El payload exfiltra datos robados a través de dos canales: (a) repositorios públicos de GitHub creados bajo el token robado (con nombres temáticos de Dune), (b) HTTPS a
t.m-kosche.com/api/public/otel/v1/traces.
- Acción sobre Objetivos:
- Propagación: El payload utiliza tokens npm robados para publicar nuevas versiones maliciosas de otros paquetes (gusano).
- Persistencia: Inyecta workflow de GitHub Actions, hooks de Claude Code/Codex, tasks.json de VS Code, y servicios systemd/LaunchAgent (
kitty-monitor, gh-token-monitor).
- Publicación de Confianza: Intercambia tokens OIDC por tokens de publicación npm, firma artefactos con Sigstore.
- C2 Dead-Drop: El daemon
kitty-monitor consulta GitHub en busca de la palabra clave firedalazer, descarga y ejecuta comandos firmados.
- Propagación Local: Infecta otros proyectos Node.js en la misma máquina a través de
Vo class.
Infraestructura Utilizada
| Tipo
|
Indicador / Característica
|
Propósito
|
| Cuenta npm Comprometida
|
atool (i@hust.cc)
|
Publicación de versiones maliciosas.
|
| Ecosistema Afectado
|
AntV (Alibaba), timeago.js
|
Paquetes objetivo (317 paquetes).
|
| Commits Impostores
|
1916faa365f2..., 7cb42f57561c..., dc3d62a2181b... en antvis/G2
|
Alojamiento de payload en repositorio legítimo.
|
| Autoría Falsificada
|
Alexzjt (huiyu.zjt@users.noreply.github.com)
|
Misdirección, apunta a mantenedor legítimo.
|
| Dominio C2
|
t.m-kosche[.]com:443
|
Exfiltración HTTPS.
|
| Ruta C2
|
/api/public/otel/v1/traces
|
Disfraz de OpenTelemetry.
|
| User-Agent Falsificado
|
python-requests/2.31.0
|
Mezcla con tráfico legítimo.
|
| Patrón de Repositorios de Exfiltración
|
{X1}-{D1}-{0-999}, descripción invertida niagA oG eW ereH :duluH-iahS
|
Almacenamiento de datos robados.
|
| X1 (16 palabras)
|
sardaukar, mentat, fremen, atreides, harkonnen, gesserit, prescient, fedaykin, tleilaxu, siridar, kanly, sayyadina, ghola, powindah, prana, kralizec
|
Primera parte del nombre del repo.
|
| D1 (16 palabras)
|
sandworm, ornithopter, heighliner, stillsuit, lasgun, sietch, melange, thumper, navigator, fedaykin, futar, phibian, slig, cogitor, laza, ghola
|
Segunda parte del nombre del repo.
|
| Palabra Clave C2 Dead-Drop
|
firedalazer
|
Búsqueda en commits de GitHub.
|
| Clave Pública RSA (C2)
|
4096-bit hardcodeada en cat.py
|
Verificación de firmas RSA-PSS.
|
| Daemon Persistente
|
kitty-monitor (systemd/LaunchAgent), script ~/.local/share/kitty/cat.py
|
C2 dead-drop persistente.
|
| Daemon de Monitoreo de Tokens
|
gh-token-monitor (~/.local/bin/gh-token-monitor.sh)
|
Soneo de tokens GitHub robados cada 60s.
|
| Sigstore Endpoints
|
fulcio.sigstore.dev, rekor.sigstore.dev
|
Firma de artefactos con OIDC robado.
|
| Gestores de Contraseñas Objetivo
|
1Password (op), Bitwarden (bw), pass, gopass
|
Extracción de bóvedas locales.
|
Objetivos y Sectores Afectados
- Objetivo Principal: Desarrolladores de software, equipos de DevOps, pipelines CI/CD, científicos de datos, ingenieros de ML.
- Sectores Prioritarios: Tecnología, Visualización de Datos, Inteligencia Artificial, Análisis, SaaS, Nube.
- Ecosistemas Afectados: npm (317 paquetes, 637 versiones), GitHub Actions, Sigstore, Claude Code, Codex, VS Code.
- Organizaciones Afectadas (Paquetes Comprometidos): AntV (Alibaba), usuarios de
timeago.js (1.5M descargas/semana), echarts-for-react (3.8M), jest-canvas-mock, jest-date-mock, size-sensor, etc.
- Número de Repositorios de Exfiltración Creados: Más de 2500 (al momento del análisis).
- Distribución Geográfica: Global.
Impacto y Riesgo
- Nivel de Riesgo: Crítico para cualquier organización o desarrollador que haya instalado las versiones comprometidas (directa o transitivamente). El ataque afecta a millones de descargas semanales.
- Impacto Potencial (Secretos Robados):
- Gestores de Contraseñas: Extracción completa de bóvedas de 1Password, Bitwarden, pass, gopass (todas las contraseñas del usuario).
- Credenciales de Nube (AWS, Azure, GCP): Acceso a infraestructura cloud, potencialmente incluyendo cuentas de producción.
- Tokens de Kubernetes: Control de clústeres enteros.
- Tokens de GitHub y npm: Publicación de nuevas versiones maliciosas de paquetes, propagación del gusano.
- Claves SSH: Acceso a servidores internos y sistemas de producción.
- Secretos de Vault: Acceso a todos los secretos almacenados.
- Impacto en la Cadena de Suministro: La publicación de 637 versiones en 317 paquetes en 22 minutos representa una de las mayores campañas coordinadas de npm jamás documentadas. Los proyectos que usan rangos semver fueron actualizados automáticamente a versiones maliciosas.
- Impacto en Sigstore: La capacidad de firmar artefactos maliciosos con certificados Sigstore legítimos utilizando OIDC robado socava la confianza en la verificación de procedencia SLSA.
- Impacto en Herramientas de IA (Claude Code, Codex): Los hooks
SessionStart inyectados significan que cada vez que un desarrollador usa estas herramientas en un proyecto infectado, el payload se re-ejecuta.
- Escape de Contenedores: En entornos CI/CD que exponen
/var/run/docker.sock, el atacante puede escapar al host y comprometer toda la infraestructura subyacente.
- C2 Persistente: Los daemons
kitty-monitor y gh-token-monitor proporcionan al atacante acceso remoto persistente incluso después de que se eliminen los paquetes comprometidos.
Recomendaciones de Mitigación
- Contención Inmediata y Rotación de Credenciales (M1026, M1053):
- Auditar lockfiles: Buscar cualquier versión publicada el 19 de mayo de 2026 de los paquetes listados en la tabla (317 paquetes). Priorizar
timeago.js, echarts-for-react, @antv/*.
- Revertir a versiones seguras: La última versión limpia anterior a la ráfaga (ej., para
echarts-for-react, la versión limpia es 3.0.6, no 3.0.7 o superior).
- Rotar todas las credenciales: Asumir que todos los secretos están comprometidos: tokens npm, PAT de GitHub, claves AWS/Azure/GCP, claves SSH, tokens de Kubernetes, secretos de Vault, y todas las contraseñas en 1Password, Bitwarden, pass, gopass (el malware extrae bóvedas completas).
- Auditar GitHub: Buscar repositorios públicos con nombres temáticos de Dune (ej.,
harkonnen-melange-742) y la descripción invertida. Eliminarlos y revocar los tokens utilizados para crearlos.
- Eliminar daemons:
systemctl --user stop kitty-monitor, systemctl --user disable kitty-monitor, eliminar ~/.local/share/kitty/cat.py y ~/.local/bin/gh-token-monitor.sh.
- Fortalecimiento de Pipelines CI/CD (M1030, M1037):
- Restringir acceso al socket de Docker: No montar
/var/run/docker.sock en runners de CI/CD a menos que sea absolutamente necesario.
- Restringir IMDSv2: Configurar IMDSv2 con un límite de saltos (hop limit) de 1 para evitar que contenedores accedan a metadatos.
- Bloquear t.m-kosche.com a nivel de DNS/firewall.
- Implementar períodos de enfriamiento (cooldown): No aceptar versiones de paquetes publicadas en las últimas 48 horas.
- Usar
npm ci en lugar de npm install para respetar estrictamente el lockfile.
- Protección de Cuentas y Autenticación (M1032, M1018):
- Exigir autenticación multifactor (MFA) resistente al phishing (WebAuthn/FIDO2) para todas las cuentas npm y GitHub.
- Revocar tokens npm de larga duración y usar publicación de confianza (trusted publishing) con OIDC. Auditar los flujos de trabajo que tienen acceso OIDC.
- Monitorear commits con autoría falsificada: Verificar firmas GPG en commits (los commits impostores no tienen GPG).
- Detección de Malware en Tiempo de Ejecución (M1040):
- Monitorear procesos hijos de npm/Bun: Alertar sobre
bun run index.js durante npm install.
- Detectar acceso a
/proc/*/mem desde procesos Python (lectura de memoria de Runner.Worker).
- Detectar creación de repositorios públicos con nombres temáticos de Dune y la descripción invertida.
- Detectar User-Agent
python-requests/2.31.0 desde hosts CI/CD (el payload usa este UA para GitHub API).
- Prevención de Futuros Ataques (M1038, M1049):
- Implementar análisis de composición de software (SCA) con SBOM: Herramientas como StepSecurity AI Package Analyst, Socket, SafeDep
pmg pueden detectar hooks preinstall sospechosos y dependencias opcionales apuntando a commits de GitHub.
- Usar
pmg (Package Manager Guard): Proxy que intercepta npm install y evalúa paquetes contra inteligencia de amenazas antes de que se ejecuten scripts preinstall.
- Verificar firmas GPG en commits de dependencias de GitHub (el atacante no puede falsificar GPG fácilmente).
- Fijar dependencias usando lockfiles y
npm ci. Evitar rangos semver (^, ~) en package.json.
Herramientas de Detección
- StepSecurity AI Package Analyst: Detectó esta campaña en tiempo real, evaluando anomalías como dependencias opcionales apuntando a forks de GitHub y hooks
preinstall con Bun.
- StepSecurity Harden-Runner: Bloquea el dominio C2
t.m-kosche.com y detecta lecturas de memoria de Runner.Worker (volcado de secretos).
- SafeDep
pmg (Package Manager Guard): Proxy open-source que intercepta npm install, verifica paquetes contra inteligencia de amenazas, y aplica períodos de enfriamiento. Evita que los scripts preinstall se ejecuten.
- Microsoft Defender for Cloud: Escaneo de código sin agente (agentless) para detectar paquetes maliciosos a través de SBOM.
- Caza Avanzada (KQL): Consultas para buscar procesos
bun run index.js, accesos a /proc/*/mem, tráfico a t.m-kosche.com, y creación de repositorios públicos con el patrón de nombres de Dune.
Referencias y Fuentes
- Likhith, S. (2026, 19 de mayo). Shai-Hulud: ¡Otra vez lo mismo! Un ataque masivo a la cadena de suministro de npm afecta al ecosistema de AntV. StepSecurity. (Artículo proporcionado por el usuario).
- SafeDep Team. (2026, 19 de mayo). *Mini Shai-Hulud Strikes Again: 317 npm Packages Compromised*. SafeDep. (Artículo proporcionado por el usuario).
- MITRE ATT&CK Framework. Enterprise Matrix. (Para mapeo de TTPs).
Historial de Revisiones
| Fecha
|
Versión
|
Autor
|
Cambios Realizados
|
| 2026-05-19
|
1.0
|
Fernando Vargas Hincapié
|
Creación inicial de la entrada basada en análisis de StepSecurity y SafeDep sobre la oleada AntV de Mini Shai-Hulud.
|
Indicadores de Compromiso (IOCs)
| Tipo
|
Valor
|
| Nombre de la Campaña
|
Mini Shai-Hulud (AntV Wave)
|
| Cuenta npm Comprometida
|
atool (i@hust.cc)
|
| Fecha de Publicación
|
19 de mayo de 2026 (01:39 - 02:06 UTC)
|
| Número de Paquetes Afectados
|
317
|
| Número de Versiones Maliciosas
|
637
|
| Paquetes Clave Afectados
|
timeago.js (4.1.2, 4.2.2), echarts-for-react (3.0.7, 3.1.7, 3.2.7), jest-canvas-mock (2.5.3, 2.6.3, 2.7.3), jest-date-mock (1.0.11, 1.1.11, 1.2.11), size-sensor (1.0.4, 1.1.4, 1.2.4), y todos los paquetes @antv/* (ver lista completa en análisis)
|
| Hook Malicioso
|
"preinstall": "bun run index.js"
|
| Dependencia Opcional Maliciosa
|
"optionalDependencies": { "@antv/setup": "github:antvis/G2#<sha>" }
|
| Commits Impostores (SHA)
|
1916faa365f2788b6e193514872d51a242876569 (626 versiones), 7cb42f57561c321ecb09b4552802ae0ac55b3a7a (2 versiones), dc3d62a2181beb9f326952a2d212900c94f2e13d (1 versión, GC)
|
| Autoría Falsificada
|
Alexzjt (huiyu.zjt@users.noreply.github.com)
|
| SHA256 del Payload
|
a68dd1e6a6e35ec3771e1f94fe796f55dfe65a2b94560516ff4ac189390dfa1c
|
| Dominio C2
|
t.m-kosche[.]com:443
|
| Ruta C2
|
/api/public/otel/v1/traces
|
| User-Agent C2
|
python-requests/2.31.0
|
| Patrón de Repositorios de Exfiltración
|
{sardaukar,mentat,fremen,atreides,harkonnen,gesserit,prescient,fedaykin,tleilaxu,siridar,kanly,sayyadina,ghola,powindah,prana,kralizec}-{sandworm,ornithopter,heighliner,stillsuit,lasgun,sietch,melange,thumper,navigator,fedaykin,futar,phibian,slig,cogitor,laza,ghola}-{0-999}
|
| Descripción del Repositorio
|
niagA oG eW ereH :duluH-iahS (reverso: "Shai-Hulud: Here We Go Again")
|
| Palabra Clave C2 Dead-Drop
|
firedalazer
|
| Daemon Persistente
|
kitty-monitor (systemd/LaunchAgent), script ~/.local/share/kitty/cat.py
|
| Script Python C2
|
cat.py (GitHub dead-drop backdoor, verificación RSA-PSS)
|
| Daemon de Monitoreo de Tokens
|
gh-token-monitor (~/.local/bin/gh-token-monitor.sh)
|
| Archivo de Estado C2
|
/var/tmp/.gh_update_state
|
| Número de Repositorios de Exfiltración
|
2500+ (y creciendo)
|
| Gestores de Contraseñas Objetivo
|
1Password (op), Bitwarden (bw), pass, gopass
|
| Puntos finales de Metadatos AWS
|
169.254.169.254, 169.254.170.2
|
| Punto final Vault
|
vault.svc.cluster.local:8200
|
| Sigstore Endpoints
|
fulcio.sigstore.dev, rekor.sigstore.dev
|
| Workflow Inyectado
|
.github/workflows/codeql.yml (nombre: "Run Copilot")
|
| Hook Claude Code
|
.claude/settings.json con SessionStart hook ejecutando node .claude/setup.mjs
|
| Hook VS Code
|
.vscode/tasks.json con "runOn": "folderOpen"
|
| Comportamiento Anómalo (npm)
|
Dependencias opcionales apuntando a commits específicos de GitHub con scripts prepare
|
| Comportamiento Anómalo (Procesos)
|
bun run index.js durante npm install, python3 leyendo /proc/*/mem
|
| Comportamiento Anómalo (Red)
|
Tráfico a t.m-kosche.com, User-Agent python-requests/2.31.0
|
| Comportamiento Anómalo (GitHub)
|
Repositorios públicos con nombres temáticos de Dune y descripción invertida
|
| Comportamiento Anómalo (Archivos)
|
.claude/settings.json con hooks SessionStart, .vscode/tasks.json con runOn: folderOpen, ~/.local/share/kitty/cat.py, ~/.local/bin/gh-token-monitor.sh
|
Etiquetas / Categorías
mini-shai-hulud antv alibaba npm-supply-chain-attack atool timeago.js echarts-for-react bun preinstall-hook optional-dependencies github-imposter-commits t.m-kosche.com opentelemetry-disguise firedalazer kitty-monitor gh-token-monitor sigstore fulcio rekor 1password bitwarden pass gopass aws-imdsv2 ecs-metadata kubernetes hashicorp-vault container-escape claude-code codex vscode dune shai-hulud stepsecurity safedep pmg harden-runner ciberwiki sai-likhith 19-mayo-2026 637-versions 317-packages