Alerta crítica: campaña Mini Shai‑Hulud exfiltra secretos y se propaga vía pipelines CI/CD de TanStack

Recientemente se reportó una  campaña de ataque de cadena de suministro bautizada como Mini Shai‑Hulud. Se trata de un malware del tipo worm o gusano, que ha comprometido múltiples paquetes npm, empezando por el ecosistema @tanstack/*, publicando versiones maliciosas a través de los propios pipelines de GitHub Actions de los proyectos utilizando tokens OIDC robados. Estas versiones maliciosas incluyen payloads ofuscados que roban credenciales de CI/CD, cloud, repositorios y herramientas de desarrollo, se mantienen persistentes en máquinas de desarrolladores y se auto‑propagan a otros paquetes utilizando tokens npm y OIDC válidos.

npm es el principal registry de paquetes para el ecosistema JavaScript y TypeScript, y se ha convertido en una pieza crítica de la cadena de suministro de software moderna. Millones de aplicaciones y pipelines CI/CD consumen automáticamente nuevas versiones de paquetes publicados en npm, lo que convierte cualquier compromiso de cuentas de mantenedores o de sus pipelines en un vector de impacto masivo y transversal. Esta dependencia estructural hace imprescindible aplicar controles de seguridad adicionales (registries intermedios, validación de versiones, políticas de publicación y observabilidad) en lugar de confiar ciegamente en que todo lo que llega desde npm es confiable.

La gravedad es crítica porque el worm puede leer memoria del runner de GitHub Actions y extraer absolutamente todos los secretos de un pipeline (incluyendo secretos “mascarados”), abusar de OIDC para publicar nuevos paquetes maliciosos con SLSA Build Level 3 válido, y además establece persistencia en estaciones de desarrollo e IDEs. En la práctica, cualquier entorno que haya instalado versiones comprometidas debe asumir compromiso total de todos los secretos accesibles desde esa máquina o pipeline.

Paquete comprometidos

PaqueteEcosistemaVersiones comprometidas Comentario rápido
@tanstack/react-routernpm1.169.5, 1.169.8Router React muy utilizado en front‑end.
@tanstack/solid-routernpm1.169.5, 1.169.8Router para aplicaciones SolidJS.
@tanstack/vue-routernpm1.169.5, 1.169.8Router para aplicaciones Vue.
@tanstack/react-startnpm1.167.68, 1.167.71Framework full‑stack sobre React.
@tanstack/solid-startnpm1.167.65, 1.167.68Framework full‑stack sobre Solid.
@tanstack/vue-startnpm1.167.61, 1.167.64Framework full‑stack sobre Vue.
@mistralai/mistralainpm2.2.3, 2.2.4SDK TypeScript oficial de Mistral AI.
mistralaiPyPI2.4.6SDK Python de Mistral AI.
guardrails-aiPyPI0.10.1Librería popular para guardrails de LLM.
@opensearch-project/opensearchnpm3.6.2Cliente de OpenSearch.
@uipath/clinpm1.0.1CLI de UiPath usada en automatización/RPA.
@uipath/robotnpm1.3.4Componente de robot UiPath.
@uipath/project-packagernpm1.1.16Paquete clave en proyectos UiPath.
@draftlab/authnpm0.24.1, 0.24.2Auth para aplicaciones DraftLab.
@squawk/airport-datanpm0.7.4, 0.7.5, 0.7.7Datos aeronáuticos, usado en soluciones de aviación.

Detalles técnicos del ataque

Vector de compromiso y publicación de paquetes

El grupo de amenazas TeamPCP lanzó una nueva ola del worm Mini Shai‑Hulud dirigida contra TanStack y otros proyectos de alto valor. Para ello, el atacante creó un fork malicioso del repositorio TanStack/router usando la cuenta voicproducoes y añadió dos artefactos clave: un paquete falso @tanstack/setup y un archivo de gran tamaño y fuertemente ofuscado llamado tanstack_runner.js. Este paquete @tanstack/setup se declara como dependencia opcional mediante una URL github: con un commit hash que aparenta pertenecer al repositorio oficial de TanStack, pero que en realidad apunta a ese fork malicioso.

En cada paquete comprometido se observaron dos cambios característicos:

  • Se agregó en package.json un bloque optionalDependencies apuntando a @tanstack/setup a través de una URL github:tanstack/router#<commit-malicioso>.
  • Se añadió un archivo grande router_init.js en la raíz del paquete, que no debería estar presente según el campo files (que solo lista dist y src), evidenciando manipulación del tarball fuera del proceso de build normal.

La publicación de los paquetes maliciosos no se hizo con credenciales robadas a un maintainer, sino comprometiento el propio pipeline de GitHub Actions, aprovechando el token OIDC del runner para publicar directamente en npm. Como parte de la campaña, los paquetes maliciosos incluyen attestations SLSA v1 de nivel 3 emitidas por la infraestructura de firmado de npm (Sigstore/Fulcio/Rekor), de forma que parecen legítimos a pesar de haber sido generados por un pipeline ya comprometido.

Características del gusano y del payload

El archivo router_init.js:

(hash SHA‑256 ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c) contiene un payload de unas 2,3 MB altamente ofuscado que implementa un stealer multi‑stage con capacidades de worm.

Principales capacidades:

  • Ofuscación avanzada: tres capas de ofuscación, incluyendo tabla de strings ofuscada, un cifrado de sustitución con PBKDF2‑SHA256 y cargas secundarias cifradas con AES‑256‑GCM que se descomprimen con Bun.gunzipSync.
  • Uso del runtime Bun: el payload instala y utiliza Bun 1.3.13 en vez de Node.js, evadiendo herramientas de seguridad centradas en hooks de Node (–require, etc.).
  • Scraping de memoria del runner de GitHub Actions: un payload en Python lee /proc/<pid>/mem del proceso Runner.Worker para extraer JSON internos del tipo {«value»:»…»,»isSecret»:true}, obteniendo todos los secretos configurados en el workflow, aunque estén enmascarados o nunca se escriban a disco.
  • Robo de credenciales cloud: invoca metadatos de AWS (169.254.169.254 IMDSv2), ECS (169.254.170.2), y sockets locales de HashiCorp Vault (127.0.0.1:8200) para obtener credenciales temporales, tokens y secretos.
  • Recolección de ficheros de credenciales: recorre más de 100 rutas conocidas para extraer tokens npm, PATs de GitHub, claves de AWS, GCP, Azure, archivos de wallets de criptomonedas, historiales de shell y configuraciones de herramientas de IA y mensajería.
  • Persistencia en estaciones de trabajo: crea archivos en .claude/ y .vscode/ para engancharse a eventos de apertura de sesión en Claude Code y apertura de carpeta en VS Code, y además instala servicios tipo gh-token-monitor vía LaunchAgents en macOS o systemd user en Linux.

Auto‑propagación

Mini Shai‑Hulud se comporta como un gusano (worm) de npm, utilizando tokens robados para comprometer más paquetes:

  • Busca tokens npm con bypass_2fa true (publicación sin 2FA) y, si los encuentra, enumera todos los paquetes del mismo maintainer usando la API de búsqueda de npm.
  • En entornos CI/CD, intercambia tokens OIDC de GitHub por tokens de publicación npm por paquete mediante el endpoint de oidc/token/exchange, evitando el flujo estándar de autenticación.
  • Publica artefactos infectados para todos los paquetes con permisos suficientes, replicando la infección.

Además, inyecta workflows maliciosos en .github/workflows/codeql_analysis.yml que utilizan toJSON(secrets) para serializar todos los secretos del repositorio y exfiltrarlos directamente a infraestructura de C2 (por HTTP POST) o como artefactos subidos en acciones aparentemente legítimas.

Exfiltración y ransom

La exfiltración de datos combina dos canales principales:

  • Session Protocol CDN: subida de blobs cifrados a filev2.getsession.org, parte de la infraestructura de la red Session, usando cifrado híbrido RSA‑4096 + AES‑256‑GCM.
  • Dead‑drop en GitHub: usa la API GraphQL de GitHub para crear commits en repositorios controlados por el atacante, firmados como claude@users.noreply.github.com, con mensajes chore: update dependencies y ramas que imitan la convención de Dependabot (dependabot/github_actions/format/<palabra-dune>).

La campaña incluye también un componente de extorsión/ransom: el malware crea tokens npm con descripción IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner, indicando que revocar el token puede disparar una rutina destructiva que borra el equipo del propietario.

Detección e indicadores claves

A nivel de sysadmin/DevOps, son indicadores muy prácticos:

  • Presencia de router_init.js en node_modules o en la raíz de paquetes @tanstack/*.
  • package.json con optionalDependencies apuntando a github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c.
  • Crecimiento inusual del tamaño del tarball (paquetes ~900 KB frente a ~190 KB para versiones limpias).
  • Procesos bun ejecutándose durante npm install de paquetes que normalmente no usan Bun.
  • Procesos python3 leyendo /proc/*/mem en runners de CI.
  • Conexiones de red desde npm install o jobs de CI hacia filev2.getsession.org, api.masscan.cloud o git-tanstack.com.
  • Tokens npm con descripción  IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner.
  • Commits inesperados firmados como claude@users.noreply.github.com con ramas tipo dependabot/github_actions/format/<palabra-dune>.
TipoValorDescripción / uso sugerido
SHA‑256ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266cHash de router_init.js (payload principal en paquetes TanStack). Bloquear/alertar en EDR, AV y búsquedas forenses.
ProcesobunEjecuciones de bun durante npm install o jobs de CI en repos que no usan Bun de forma legítima. Crear regla de detección/anomalía.
Fichero localrouter_init.jsArchivo malicioso añadido en la raíz de paquetes @tanstack/*. Buscar en disco, contenedores y artefactos.
Fichero local.claude/router_runtime.js, .claude/setup.mjsPersistencia en entornos de desarrollo (Claude Code). Revisar y eliminar.
Fichero local.vscode/setup.mjsPersistencia en VS Code vía tareas/extensiones. Revisar y eliminar.
Serviciogh-token-monitorServicio de usuario (systemd / LaunchAgent) usado para monitorizar tokens. Buscar y deshabilitar.
Dominio C2filev2.getsession.orgCDN de la red Session usado para exfiltrar datos cifrados. Bloquear o alertar en proxy/firewall.
Dominio/C2api.masscan.cloudInfraestructuras relacionadas usadas en campañas previas de la misma familia. Monitorizar conexiones salientes.
Infra GitHubCommits con autor claude@users.noreply.github.com en ramas dependabot/github_actions/format/<palabra-dune>Dead‑drop en repos de la víctima. Crear hunting query en GitHub / código fuente.
Red internahttp://169.254.169.254Acceso a AWS IMDS (robo de credenciales). Alertar si se ve desde contenedores/runners que no deberían tocar metadatos.
Red internahttp://169.254.170.2Endpoint ECS/Task metadatos. Idem anterior.
Red internahttp://127.0.0.1:8200Intentos de acceso a HashiCorp Vault local. Monitorizar en runners de CI.
Token npmDescripción IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwnerTokens maliciosos creados por el malware. Enumerar tokens npm y tratarlos como indicador de compromiso crítico.

Mitigación recomendada

Evaluar exposición (dev y CI/CD)

  • Revisar package-lock.json / pnpm-lock.yaml / yarn.lock por versiones afectadas de @tanstack/* y otros paquetes listados (UiPath, DraftLab, etc.).
  • Buscar router_init.js y referencias a @tanstack/setup en node_modules y repos locales.
  • Revisar logs de GitHub Actions (o CI) por:
    • menciones a @tanstack/setup,
    • uso de bun run tanstack_runner.js u otros comandos Bun,
    • conexiones de red inusuales durante la instalación de dependencias.

Contención y análisis forense

  • Aislar cualquier dev machine o runner CI que haya hecho npm install con versiones comprometidas de @tanstack/*.
  • Obtener imagen forense antes de limpiar, sobre todo si hay tokens npm con el mensaje de amenaza.
  • No revocar de inmediato tokens npm con IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner hasta aislar y analizar el equipo.

Eliminación de persistencia:

En equipos de desarrollo

  • Claude / VS Code:
    • Borrar .claude/router_runtime.js y .claude/setup.mjs.
    • Revisar/restaurar .claude/settings.json.
    • Revisar .vscode/tasks.json y borrar .vscode/setup.mjs.
  • Servicios de monitorización de tokens:
    • macOS: launchctl unload ~/Library/LaunchAgents/com.user.gh-token-monitor.plist y borrar plist, ~/.local/bin/gh-token-monitor.sh y ~/.config/gh-token-monitor.
    • Linux: systemctl –user stop/disable gh-token-monitor y borrar ~/.config/systemd/user/gh-token-monitor.service.

En repositorios

  • Auditar .github/workflows/codeql_analysis.yml y workflows recientes que usen toJSON(secrets) o hagan POST a dominios no esperados, y eliminarlos o corregirlos.

Rotación de secretos

Asumiendo compromiso donde se instaló una versión maliciosa:

  • Rotar:
    • tokens npm (incluidos los de CI),
    • PATs y tokens de GitHub (usuario y Actions),
    • credenciales de AWS, GCP, Azure, Vault y otros clouds,
    • claves SSH usadas en esos equipos o runners.
  • Si hay wallets de criptomonedas, mover fondos a nuevas wallets.

Solución y recomendaciones de hardening

Actualización / limpieza de dependencias

  • Pin a versiones seguras de @tanstack/* según las tablas de versiones limpias publicadas por TanStack y los reportes de StepSecurity/Snyk; la regla operativa es regresar a la última versión anterior a las fechas del ataque (11 de mayo de 2026 para TanStack) y eliminar versiones publicadas en esa ventana.
  • Tras ajustar versiones, ejecutar:
    • rm -rf node_modules
    • npm install (o equivalente en pnpm/yarn) para regenerar el árbol con versiones no comprometidas.

Endurecer pipelines CI/CD

Buenas prácticas específicas para este caso:

  • Restringir al mínimo los permisos de tokens OIDC en workflows (id-token: write solo donde sea imprescindible) y usar jobs separados con políticas de menor privilegio para publicación en registries.
  • Implementar un cooldown para versiones nuevas de paquetes (p.ej. 3–10 días) en un registry intermedio (Artifactory, Nexus, o soluciones como StepSecurity Secure Registry), de modo que paquetes publicados hace minutos no se consuman automáticamente en CI.
  • Implementar egress control en runners de CI (whitelists de dominios permitidos), de forma similar a soluciones como Harden‑Runner, que bloquean conexiones a dominios de C2 en tiempo real.
  • Automatizar comprobaciones de reputación de paquetes y firmas (SLSA, Sigstore) entendiendo que la provenance no garantiza ausencia de compromiso, solo identifica el pipeline que generó el artefacto.

Controles adicionales en desarrollo

  • Introducir escaneo continuo de dependencias para malware y campañas conocidas (Mini Shai‑Hulud, Shai‑Hulud 2.0, etc.) utilizando feeds de inteligencia actualizados de proveedores de seguridad.
  • Limitar el uso de dependencias github: en package.json, sobre todo cuando apuntan a commits de forks, y establecer revisiones manuales en PRs que introduzcan este patrón.

Referencias

Anexo – Todos los paquetes comprometidos

En total, la campaña Mini Shai‑Hulud afectó a 170 paquetes distintos y dio lugar a al menos 348 artefactos maliciosos publicados en los registries npm y PyPI durante esta ola.

#Scope / paqueteVersiones comprometidasEcosistemaNotas relevantes
1@mistralai/mistralai2.2.3, 2.2.4npmSDK TypeScript oficial de Mistral AI
2@mistralai/mistralai-azure1.7.2, 1.7.3npmCliente para despliegues en Azure
3@mistralai/mistralai-gcp1.7.2, 1.7.3npmCliente para despliegues en GCP
4mistralai2.4.6PyPISDK Python de Mistral; tratar entornos como comprometidos
5guardrails-ai0.10.1PyPILibrería de guardrails para LLM afectada en la campaña
6@opensearch-project/opensearch3.6.2npmCliente relacionado con OpenSearch
7@tanstack/router-utils1.161.11, 1.161.14npmParte del stack de routing de TanStack
8@tanstack/router-core1.169.5, 1.169.8npmNúcleo del router TanStack
9@tanstack/history1.161.9, 1.161.12npmGestión de historial para el router
10@tanstack/react-router1.169.5, 1.169.8npmRouter React, muy usado en front‑end
11@tanstack/react-router-devtools1.166.16, 1.166.19npmDevtools del router React
12@tanstack/react-router-ssr-query1.166.15, 1.166.18npmIntegración SSR + queries
13@tanstack/react-start1.167.68, 1.167.71npmFramework full‑stack react‑start
14@tanstack/react-start-client1.166.51, 1.166.54npmCliente de react‑start
15@tanstack/react-start-rsc0.0.47, 0.0.50npmSoporte React Server Components
16@tanstack/react-start-server1.166.55, 1.166.58npmServidor react‑start
17@tanstack/router-cli1.166.46, 1.166.49npmCLI del router TanStack
18@tanstack/router-devtools1.166.16, 1.166.19npmDevtools del router (core)
19@tanstack/router-devtools-core1.167.6, 1.167.9npmNúcleo de devtools para router
20@tanstack/router-generator1.166.45, 1.166.48npmGenerador de rutas
21@tanstack/router-plugin1.167.38, 1.167.41npmPlugin de router
22@tanstack/router-ssr-query-core1.168.3, 1.168.6npmCore para SSR + queries
23@tanstack/router-vite-plugin1.166.53, 1.166.56npmPlugin de Vite para router
24@tanstack/solid-router1.169.5, 1.169.8npmRouter para SolidJS
25@tanstack/solid-router-devtools1.166.16, 1.166.19npmDevtools para Solid router
26@tanstack/solid-router-ssr-query1.166.15, 1.166.18npmSSR + queries para Solid
27@tanstack/solid-start1.167.65, 1.167.68npmFramework Solid Start
28@tanstack/solid-start-client1.166.50, 1.166.53npmCliente Solid Start
29@tanstack/solid-start-server1.166.54, 1.166.57npmServidor Solid Start
30@tanstack/start-client-core1.168.5, 1.168.8npmCore cliente TanStack Start
31@tanstack/start-fn-stubs1.161.9, 1.161.12npmStubs de funciones Start
32@tanstack/start-plugin-core1.169.23, 1.169.26npmCore de plugins Start
33@tanstack/start-server-core1.167.33, 1.167.36npmCore de servidor Start
34@tanstack/start-static-server-functions1.166.44, 1.166.47npmFunciones de servidor estático
35@tanstack/start-storage-context1.166.38, 1.166.41npmContexto de almacenamiento Start
36@tanstack/arktype-adapter1.166.12, 1.166.15npmAdaptador de tipos ArkType
37@tanstack/valibot-adapter1.166.12, 1.166.15npmAdaptador Valibot
38@tanstack/virtual-file-routes1.161.10, 1.161.13npmRutas basadas en archivos virtuales
39@tanstack/vue-router1.169.5, 1.169.8npmRouter para Vue
40@tanstack/vue-router-devtools1.166.16, 1.166.19npmDevtools Vue router
41@tanstack/vue-router-ssr-query1.166.15, 1.166.18npmSSR + queries para Vue
42@tanstack/vue-start1.167.61, 1.167.64npmFramework Vue Start
43@tanstack/vue-start-client1.166.46, 1.166.49npmCliente Vue Start
44@tanstack/vue-start-server1.166.50, 1.166.53npmServidor Vue Start
45@tanstack/zod-adapter1.166.12, 1.166.15npmAdaptador Zod
46@tanstack/eslint-plugin-router1.161.9, 1.161.12npmPlugin ESLint para router
47@tanstack/eslint-plugin-start0.0.4, 0.0.7npmPlugin ESLint para Start
48@squawk/airways0.4.2, 0.4.3, 0.4.5npmParte del ecosistema aeronáutico Squawk
49@squawk/airport-data0.7.4, 0.7.5, 0.7.7npmDatos de aeropuertos
50@squawk/airports0.6.2, 0.6.3, 0.6.5npmAPI de aeropuertos
51@squawk/airspace0.8.1, 0.8.2, 0.8.4npmDatos de espacio aéreo
52@squawk/airspace-data0.5.3, 0.5.4, 0.5.6npmDataset de espacio aéreo
53@squawk/airway-data0.5.4, 0.5.5, 0.5.7npmDataset de aerovías
54@squawk/fix-data0.6.4, 0.6.5, 0.6.7npmPuntos de navegación (fixes)
55@squawk/fixes0.3.2, 0.3.3, 0.3.5npmAPI de fixes
56@squawk/flight-math0.5.4, 0.5.5, 0.5.7npmUtilidades de cálculo de vuelo
57@squawk/flightplan0.5.2, 0.5.3, 0.5.5npmPlanificación de vuelos
58@squawk/geo0.4.4, 0.4.5, 0.4.7npmFunciones geoespaciales
59@squawk/icao-registry0.5.2, 0.5.3, 0.5.5npmRegistro ICAO
60@squawk/icao-registry-data0.8.4, 0.8.5, 0.8.7npmDataset ICAO
61@squawk/mcp0.9.1, 0.9.2, 0.9.4npmHerramientas MCP
62@squawk/navaid-data0.6.4, 0.6.5, 0.6.7npmDatos de radio‑ayudas
63@squawk/navaids0.4.2, 0.4.3, 0.4.5npmAPI de navaids
64@squawk/notams0.3.6, 0.3.7, 0.3.9npmNOTAMs
65@squawk/procedure-data0.7.3, 0.7.4, 0.7.6npmDatos de procedimientos
66@squawk/procedures0.5.2, 0.5.3, 0.5.5npmAPI de procedimientos
67@squawk/types0.8.1, 0.8.2, 0.8.4npmTipos compartidos
68@squawk/units0.4.3, 0.4.4, 0.4.6npmUnidades de medida
69@squawk/weather0.5.6, 0.5.7, 0.5.9npmDatos meteorológicos
70@uipath/* (múltiples paquetes)Versiones listadas en tu texto originalnpmMás de 50 paquetes en el scope @uipath afectados
71@draftauth/client0.2.1, 0.2.2npmCliente de autenticación DraftAuth
72@draftauth/core0.13.1, 0.13.2npmNúcleo DraftAuth
73@draftlab/auth0.24.1, 0.24.2npmAuth de DraftLab
74@draftlab/auth-router0.5.1, 0.5.2npmRouter de autenticación
75@draftlab/db0.16.1, 0.16.2npmCapa de datos DraftLab
76@beproduct/nestjs-auth0.1.2–0.1.17, 0.1.19npmMúltiples versiones comprometidas
77@dirigible-ai/sdk0.6.2, 0.6.3npmSDK Dirigible AI
78@ml-toolkit-ts/preprocessing1.0.2, 1.0.3npmParte de ml‑toolkit‑ts
79@ml-toolkit-ts/xgboost1.0.3, 1.0.4npmBinding XGBoost
80ml-toolkit-ts1.0.4, 1.0.5npmPaquete principal
81@tallyui/* (components, core, etc.)Versiones indicadas en tu listanpmScope @tallyui afectado en bloque
82@mesadev/rest0.28.3npmParte del ecosistema MesaDev
83@mesadev/saguaro0.4.22npmPaquete MesaDev
84@mesadev/sdk0.28.3npmSDK MesaDev
85safe-action0.8.3, 0.8.4npmPaquete de acciones seguras
86@supersurkhet/cli0.0.2–0.0.7npmCLI Supersurkhet
87@supersurkhet/sdk0.0.2–0.0.7npmSDK Supersurkhet
88cmux-agent-mcp0.1.3–0.1.8npmHerramienta MCP
89git-git-git1.0.8–1.0.10, 1.0.12npmUtilidades git
90git-branch-selector1.3.3–1.3.5, 1.3.7npmSelector de ramas git
91nextmove-mcp0.1.3–0.1.5, 0.1.7npmCliente MCP
92agentwork-cli0.1.4, 0.1.5npmCLI AgentWork
93wot-api0.8.1, 0.8.2, 0.8.4npmAPI WoT
94cross-stitch1.1.3, 1.1.4, 1.1.6npmLibrería utilitaria
95ts-dna3.0.1, 3.0.2, 3.0.4npmPaquete TypeScript

Noticias Recientes

Etiquetas

[mostrar_tags]