CanisterWorm está activo. Esto es lo que tenés que hacer antes de mañana a la mañana

La semana pasada, el escáner de seguridad open source Trivy — usado en decenas de miles de pipelines de CI/CD en todo el mundo — fue comprometido. En menos de 24 horas, un gusano autopropagante llamado CanisterWorm empezó a expandirse por el ecosistema npm. Al momento de escribir este artículo, ya infectó 141 artefactos maliciosos en más de 66 paquetes npm. El grupo atacante sigue activo.

Esto no es un riesgo teórico de supply chain. Está pasando ahora mismo. Y si tu pipeline corrió Trivy en la última semana, puede que ya estés afectado.


Cómo llegamos hasta acá

El 19 de marzo, el grupo de amenazas conocido como TeamPCP — anteriormente conocido por atacar Docker APIs expuestas y clusters de Kubernetes — aprovechó credenciales comprometidas para publicar versiones maliciosas de Trivy (v0.69.4), trivy-action y setup-trivy. Los binarios backdooreados ejecutaban un stealer de credenciales de tres etapas que volcaba la memoria del proceso Runner.Worker, cosechaba claves SSH, credenciales cloud (AWS, GCP, Azure), tokens de Kubernetes y tokens de autenticación de npm — todo mientras el escaneo legítimo parecía ejecutarse con normalidad.

Esos tokens de npm robados fueron la semilla de CanisterWorm. Una herramienta deploy.js le permitió al atacante republicar versiones maliciosas de todos los paquetes accesibles con esos tokens. Una variante más reciente va más lejos: se autopropaga durante el postinstall, convirtiendo a cada desarrollador o pipeline de CI que instala un paquete afectado en un vector de propagación involuntario.

Lo que hace técnicamente interesante — y peligroso — a CanisterWorm es su infraestructura de C2. En lugar de un servidor web tradicional, el gusano consulta un canister de ICP: un smart contract a prueba de manipulaciones en la blockchain de Internet Computer Protocol. No hay un único host que tumbar, no hay email de abuso al que reportar. El atacante puede rotar el payload en cualquier momento actualizando una sola URL en el canister. Al momento del análisis de la mayoría de los investigadores, el canister servía un video de rickroll de YouTube (estado durmiente). Eso puede cambiar con una sola llamada.

El ataque escaló aún más. TeamPCP desplegó un wiper de Kubernetes (kamikaze.sh) dirigido a infraestructura iraní, defaceó públicamente 44 repositorios de Aqua Security, y publicó imágenes maliciosas adicionales en Docker Hub (0.69.5, 0.69.6) — todo usando un único token de cuenta de servicio comprometida que conectaba dos organizaciones de GitHub.


Tu checklist para hoy

Esto es lo que un dev o tech lead responsable debería hacer ahora mismo, en este orden:

1. Verificá si usaste trivy-action entre el 19 y 21 de marzo

Buscá en tus workflows: grep -r "aquasecurity/trivy-action" .github/workflows/

Si algún workflow corrió una referencia por tag de versión (no por SHA) entre el 19 de marzo 17:43 UTC y el 20 de marzo 05:40 UTC, tratá ese pipeline como comprometido.

2. Rotá todas las credenciales que ese pipeline tocó

Eso incluye: tokens de npm, claves AWS/GCP/Azure, claves SSH, tokens de cuentas de servicio de Kubernetes, credenciales de Docker registry. No esperes confirmación. Rotá ahora.

3. Fijate en las versiones seguras

  • Trivy: v0.69.3
  • trivy-action: @0.35.0 (SHA: 57a97c7e)
  • setup-trivy: v0.2.6

4. Pasá de version tags a SHA pinning para todos tus GitHub Actions

Los version tags pueden ser force-pushed. TeamPCP hizo force-push en 75 de 76 tags de trivy-action. Las referencias por SHA no pueden ser manipuladas. Este es el cambio de hardening de CI/CD con mayor impacto que podés hacer hoy.

5. Auditá tus tokens de publicación de npm

Si tenés paquetes en npm y tu pipeline tenía acceso a tokens de publicación, verificá si se publicaron versiones inesperadas. Buscá el patrón deploy.js: los paquetes que lo incluyen están infectados.

6. Revisá si existe el servicio systemd “pgmon”

En runners Linux self-hosted o máquinas de desarrollo que corrieron paquetes afectados, buscá un servicio systemd que se hace pasar por pgmon. Ese es el mecanismo de persistencia de CanisterWorm.

7. Revisá las imágenes de Docker Hub

Si bajás Trivy desde Docker Hub, la última versión limpia conocida es 0.69.3. Las versiones 0.69.4, 0.69.5 y 0.69.6 eran maliciosas y ya fueron removidas — pero si esas capas están cacheadas en tu entorno, tenés que limpiarlas.


El problema estructural que esto expone

TeamPCP no rompió criptografía. Explotó una rotación incompleta de credenciales después de una brecha anterior (28 de febrero), un workflow de pull_request_target mal configurado que una herramienta de seguridad había marcado tres meses antes de que fuera explotado, y la confianza implícita que los pipelines de CI/CD le otorgan a sus herramientas de seguridad.

El escáner de seguridad fue el vector de ataque. Esa es la verdad incómoda. Cuando un escáner de vulnerabilidades se convierte en la vulnerabilidad, tenés un problema de modelo de confianza — y ninguna cantidad de escaneos resuelve un problema de modelo de confianza.

Tres cambios estructurales que previenen esta clase de ataque:

Fijá Actions a SHAs, no a tags. Los tags son mutables. Los SHAs no.

Aplicá least privilege a los tokens de CI/CD. Un token de npm que solo puede publicar en un scope específico no puede sembrar un gusano en todo tu portfolio.

Tratá la rotación de credenciales como una operación atómica. Rotar algunas credenciales después de una brecha y no otras es equivalente a no rotar ninguna.


Qué viene después

El canister ICP usado como C2 fue marcado como “no disponible por violación de política” por la red ICP — pero los investigadores señalan que el atacante puede desplegar un nuevo canister de forma trivial. TeamPCP sigue activo. El payload del gusano todavía no fue armado completamente en la naturaleza. Eso no es tranquilizador — es una advertencia.

Si manejás un equipo o un pipeline, compartí este checklist. La naturaleza en cascada de este ataque significa que un solo token de desarrollador comprometido puede convertirse en un brote en todo tu portfolio de npm.


¿Tu pipeline usa trivy-action? ¿Ya auditaste los tokens que pudo haber expuesto? Contanos en los comentarios.


Referencias: