⏱️ Lectura: 11 min
El 1 de junio de 2026, la firma de seguridad StepSecurity reportó que 31 paquetes npm comprometidos aparecieron publicados bajo el scope @redhat-cloud-services, el namespace oficial que Red Hat usa para los clientes JavaScript de su plataforma Hyperscale Cloud Console (Insights, RBAC, remediations y compañía). En conjunto, las versiones maliciosas sumaban 158.590 descargas semanales.
📑 En este artículo
- TL;DR
- Qué pasó
- Cómo funciona el malware
- Qué credenciales roba
- El gusano: auto-propagación y bypass de 2FA
- Persistencia: cuando borrar el paquete no alcanza
- Datos y cifras
- Cómo saber si estás afectado y qué hacer
- Contexto: por qué los preinstall scripts son el talón de Aquiles
- Qué sigue
- Preguntas frecuentes
- Referencias
No es un robo de tokens cualquiera. El payload roba credenciales de nube y CI/CD, se auto-propaga publicando nuevas versiones backdooreadas con los tokens que captura, y deja persistencia incluso dentro de tu editor. Si instalaste alguna versión afectada, la recomendación oficial es asumir que tu pipeline o tu máquina ya están comprometidos.
TL;DR
- El 1 de junio de 2026, StepSecurity detectó 31 paquetes del scope @redhat-cloud-services en npm con versiones maliciosas.
- Las versiones afectadas sumaban 158.590 descargas semanales; las más usadas: [email protected] y [email protected].
- El malware se ejecuta vía script preinstall, antes de cualquier código de tu app, al correr npm install.
- Roba tokens de GitHub, npm, AWS, GCP, Azure, Kubernetes, Vault, claves SSH y archivos .env del sistema.
- Lee /proc/<pid>/mem del proceso Runner.Worker para extraer secretos en vivo de GitHub Actions.
- Se auto-propaga: usa tokens npm robados y el parámetro bypass_2fa para publicar nuevas versiones backdooreadas.
- Deja persistencia en ~/.claude/settings.json (hook SessionStart) y .vscode/tasks.json (folderOpen).
Qué pasó
El reporte arrancó como un issue de seguridad en el repositorio RedHatInsights/javascript-clients (issue #492), abierto por la cuenta sailikhith-stepsecurity. En paralelo, StepSecurity publicó un análisis técnico y abrió issues equivalentes en otros dos repositorios de Red Hat: frontend-components y platform-frontend-ai-toolkit. La conclusión: alguien logró publicar versiones nuevas de 31 librerías legítimas, todas firmadas con la cuenta de mantenedor del proyecto.
El patrón es idéntico al de otros gusanos recientes de npm: no se trató de un typosquatting (paquetes con nombres parecidos a los reales) sino de un compromiso directo de paquetes auténticos. Es decir, no hace falta que el desarrollador se equivoque escribiendo el nombre; basta con que tenga una de estas dependencias en su árbol y corra una instalación para activar el payload. Por eso este tipo de paquetes npm comprometidos es tan peligroso: el vector de entrada es la confianza que ya le tenías al proveedor.
⚠️ Ojo: que un paquete venga de un proveedor serio como Red Hat no lo hace inmune. El compromiso fue de la cuenta de publicación, no del código fuente en GitHub. El tarball que llega a tu node_modules puede diferir del repositorio público.
Cómo funciona el malware
El punto de entrada es un script preinstall declarado en el package.json del paquete. Los lifecycle scripts de npm se ejecutan automáticamente durante la instalación, y preinstall corre antes que cualquier otra cosa: antes de compilar, antes de que tu aplicación arranque, con los permisos del usuario que lanzó npm install. Ese es el momento exacto en que el implante toma el control.
A partir de ahí, StepSecurity documentó una cadena de ofuscación de cuatro capas pensada para frustrar el análisis estático:
- ROT-21 + arrays numéricos — el
index.jspesa unos anómalos 4,2 MB y guarda su contenido como códigos de carácter codificados con ROT-21. - AES-128-GCM — dos blobs cifrados se descifran en tiempo de ejecución:
_b(un descargador del runtime Bun) y_p(el implante principal, 634 KB ya descifrado). - obfuscator.io — un alfabeto base64 no estándar con una tabla de 2.219 cadenas rotada 284 ciclos.
- Cifra propia “B5” — valores sensibles protegidos con PBKDF2 (200.000 iteraciones, SHA-256, salida de 32 bytes).
El detalle del descargador de Bun no es decorativo: al traer su propio runtime, el malware se independiza de la versión de Node que tengas instalada y reduce la huella de detección de las herramientas que vigilan procesos de Node.
graph TD
A["npm install"] --> B["preinstall hook"]
B --> C["index.js 4.2MB ofuscado"]
C --> D["descifra _b: descarga Bun"]
C --> E["descifra _p: implante 634KB"]
E --> F["roba credenciales"]
E --> G["persistencia editor"]
E --> H["auto-propagacion npm"]
F --> I["exfiltra via HTTPS / GitHub"]
Qué credenciales roba
El implante barre el sistema buscando todo secreto útil para moverse lateralmente. Según el análisis, los objetivos incluyen:
- GitHub y npm —
GITHUB_TOKEN,ACTIONS_RUNTIME_TOKEN,ACTIONS_ID_TOKEN_REQUEST_TOKENyNPM_TOKEN. - AWS —
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKENy el archivo~/.aws/credentials. - Multinube — claves de cuentas de servicio de GCP, service principals de Azure y tokens de Kubernetes.
- Herramientas dev — tokens de HashiCorp Vault, claves SSH privadas,
configde Docker y claves GPG. - Archivos .env — recorre el sistema de archivos buscando variables de entorno en disco.
El componente más sofisticado es la extracción desde memoria. En un runner de GitHub Actions, el implante lee /proc/<pid>/mem del proceso Runner.Worker para sacar secretos directamente de la memoria viva, apuntando a las variables marcadas como isSecret: true por la API de Actions. Esto le permite capturar credenciales que nunca tocaron el disco ni quedaron en una variable de entorno persistente.
La exfiltración usa dos canales. El primero es HTTPS directo. El segundo es un dead-drop en GitHub: con el GITHUB_TOKEN robado crea refs y commits que contienen los datos sustraídos, de modo que el tráfico sale por api.github.com y se mimetiza con el ruido normal de un pipeline de CI.
El gusano: auto-propagación y bypass de 2FA
Lo que eleva este incidente de “robo de credenciales” a “gusano de cadena de suministro” es su capacidad de republicación autónoma. Con los tokens de npm capturados, el payload intenta publicar nuevas versiones backdooreadas de cualquier paquete al que la cuenta de la víctima tenga acceso. Y para eso explota el parámetro bypass_2fa de npm, que le permite saltarse la protección de doble factor que en teoría debería frenar publicaciones no autorizadas.
💭 Clave: el 2FA protege el login interactivo, pero los tokens de automatización históricamente podían publicar sin segundo factor. Ese hueco es justo lo que el gusano aprovecha para multiplicarse sin intervención humana.
El resultado es un efecto dominó: cada desarrollador o pipeline infectado se vuelve un nuevo emisor de paquetes maliciosos hacia el ecosistema. Es el mismo modelo de propagación que vimos en oleadas previas de npm, y explica por qué la ventana entre la publicación y la detección importa tanto.
Persistencia: cuando borrar el paquete no alcanza
Aunque desinstales la dependencia, el malware deja dos anclas pensadas para sobrevivir:
- Secuestro de Claude Code — inyecta un hook
SessionStarten~/.claude/settings.json, de modo que el código se reactiva cada vez que arranca una sesión del agente. - Inyección en VS Code — escribe una tarea con disparador
folderOpenen.vscode/tasks.json, que se ejecuta apenas abrís la carpeta del proyecto en el editor.
Estos dos mecanismos son lo que convierte una limpieza superficial en falsa sensación de seguridad. Eliminar node_modules y reinstalar versiones limpias no toca esos archivos.
Datos y cifras
El alcance se entiende mejor mirando los paquetes más descargados dentro del lote de paquetes npm comprometidos:
@redhat-cloud-services/[email protected]— 15.060 descargas semanales.@redhat-cloud-services/[email protected]— 14.166.@redhat-cloud-services/[email protected]— 13.721.@redhat-cloud-services/[email protected]— 13.551.
En total, 31 paquetes y 158.590 descargas semanales combinadas. Otras versiones afectadas confirmadas en el issue incluyen [email protected], [email protected], [email protected], [email protected], los tres paquetes MCP ([email protected], [email protected], [email protected]) y varios de la familia frontend-components-*.
Cómo saber si estás afectado y qué hacer
El primer paso es revisar si tenés instalada alguna de las versiones señaladas. Podés inspeccionar el árbol resuelto con npm ls en tu proyecto:
# macOS / Linux
npm ls --all 2>/dev/null | grep '@redhat-cloud-services'
# Windows (PowerShell)
npm ls --all 2>$null | Select-String '@redhat-cloud-services'
# Windows (CMD)
npm ls --all | findstr "@redhat-cloud-services"
Si aparece alguna versión de la lista, no esperés: rotá todas las credenciales que hayan podido estar accesibles desde esa máquina o ese runner (tokens de npm, GitHub, AWS, GCP, Azure, claves SSH) y revisá los archivos de persistencia:
# Revisar persistencia (macOS / Linux)
cat ~/.claude/settings.json
cat .vscode/tasks.json
# Windows (PowerShell)
Get-Content $env:USERPROFILE\.claude\settings.json
Get-Content .vscode\tasks.json
Para futuras instalaciones, la mitigación más directa contra payloads en preinstall es desactivar los lifecycle scripts:
# Instalar sin ejecutar scripts de ciclo de vida
npm install --ignore-scripts
# Hacerlo permanente en el proyecto (.npmrc)
echo "ignore-scripts=true" >> .npmrc
💡 Tip: en CI, anclá versiones exactas con un lockfile y usánpm cien vez denpm install.npm cirespeta elpackage-lock.jsonal pie de la letra, lo que evita que una versión “parche” recién publicada entre sin que lo notes.
También conviene pinear versiones conocidas-buenas con overrides en el package.json hasta que Red Hat publique versiones limpias y npm despublique las maliciosas:
{
"overrides": {
"@redhat-cloud-services/types": "3.5.0",
"@redhat-cloud-services/frontend-components": "7.7.1"
}
}
Contexto: por qué los preinstall scripts son el talón de Aquiles
Los lifecycle scripts existen por buenas razones: compilar bindings nativos, generar archivos, preparar el entorno. Pero le dan a cualquier paquete del árbol la capacidad de ejecutar código arbitrario en tu máquina con solo instalarlo. En un proyecto típico de frontend, ese árbol tiene cientos de dependencias transitivas, y basta una comprometida para abrir la puerta.
Este incidente se suma a una racha de ataques con el mismo molde durante 2025 y 2026: scopes legítimos comprometidos, payloads que roban tokens de CI y se auto-replican publicando nuevas versiones. La superficie de ataque ya no es solo el código que escribís, sino toda la maquinaria que lo construye y lo despliega. Tratar al pipeline de CI/CD como un entorno de confianza es, hoy, una apuesta peligrosa.
Qué sigue
npm suele despublicar las versiones maliciosas en cuestión de horas tras la confirmación, y Red Hat publicará versiones limpias con número incrementado. Pero la responsabilidad de la limpieza recae en cada equipo: rotar credenciales, purgar la persistencia y auditar qué se publicó desde cuentas potencialmente comprometidas. Para organizaciones que dependen del scope @redhat-cloud-services, este es el recordatorio de que la seguridad de la cadena de suministro no se delega: se verifica.
📖 Resumen en Telegram: Ver resumen
Preguntas frecuentes
¿Cuántos paquetes fueron comprometidos y cuántas descargas tenían?
Fueron 31 paquetes del scope @redhat-cloud-services, que en conjunto sumaban 158.590 descargas semanales según el análisis de StepSecurity del 1 de junio de 2026.
¿Cómo se activa el malware?
A través de un script preinstall en el package.json. Se ejecuta automáticamente al correr npm install, antes que cualquier código de tu aplicación, con los permisos del usuario que lanzó la instalación.
Desinstalé el paquete, ¿ya estoy a salvo?
No necesariamente. El malware deja persistencia en ~/.claude/settings.json (hook SessionStart) y en .vscode/tasks.json (disparador folderOpen). Hay que revisar y limpiar esos archivos además de reinstalar versiones limpias.
¿Qué credenciales debería rotar?
Todas las que hayan estado accesibles desde la máquina o el runner afectado: tokens de npm y GitHub, claves de AWS, GCP y Azure, tokens de Kubernetes y Vault, claves SSH y GPG, y cualquier secreto en archivos .env.
¿Cómo prevengo este tipo de ataques en el futuro?
Usá npm ci con lockfile en CI, considerá ignore-scripts=true en .npmrc, aplicá overrides para pinear versiones conocidas-buenas y monitoreá feeds de seguridad de la cadena de suministro.
¿Esto es lo mismo que el ataque a TanStack?
Es de la misma familia de gusanos de npm, pero un incidente distinto: otro scope (@redhat-cloud-services), otras versiones y otra fecha. Comparten el patrón de auto-propagación y robo de tokens de CI.
Referencias
- StepSecurity — análisis técnico completo del ataque, IoCs y remediación.
- GitHub Issue #492 — reporte oficial con la lista de paquetes y versiones comprometidas.
- StepSecurity OSS Security Feed — feed en vivo de los paquetes afectados.
- npm docs: scripts — documentación oficial de los lifecycle scripts (preinstall, postinstall).
📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.
0 Comentarios