Tokens de GitHub robados han permitido una campaña automatizada para extraer secretos de repositorios privados y de entornos de GitHub Enterprise Server, según la empresa de seguridad en la nube Wiz. La operación, llamada Gheist, convierte los registros de GitHub Actions en un canal de recopilación: los flujos de trabajo maliciosos escriben credenciales codificadas en los registros, que los atacantes recuperan antes de eliminar las ejecuciones.
Wiz recuperó más de 13.500 ramas creadas por los atacantes, asociadas a más de 850 cuentas comprometidas, que abarcan proyectos personales y repositorios empresariales. Esas cifras miden ramas y cuentas; los hallazgos no establecen cuántas organizaciones tuvieron secretos expuestos.
Los investigadores siguen a Gheist desde el 15 de septiembre. Seguía activo y continuaba cambiando a fecha del 24 de octubre, según El informe de The Hacker News sobre el análisis de Wiz. El nombre procede de gheist, el user agent que su herramienta proporciona en las solicitudes a la API de GitHub.
Dónde se ejecutaron los flujos de trabajo
Los registros de auditoría de GitHub proporcionaron evidencia de ejecución en repositorios privados y en GitHub Enterprise Server: las ejecuciones en las ramas maliciosas se completaron, tras lo cual solicitudes que usaban el user agent gheist las eliminaron. Los registros de AWS CloudTrail también mostraron flujos de trabajo legítimos de las organizaciones ejecutándose desde esas ramas y asumiendo roles de AWS, lo que indica que los jobs de Actions se habían ejecutado sin aprobación.
Dentro de las organizaciones empresariales, la herramienta alternó entre tokens pertenecientes a usuarios y cuentas de automatización en cuestión de minutos. Clonó repositorios privados antes de introducir el flujo de trabajo malicioso. Horas después, se usaron credenciales de nube de la misma organización para enumerar AWS Secrets Manager. Wiz no determinó el origen de esas credenciales ni identificó a quien las usó.
Los repositorios públicos mostraron un resultado distinto. Todas las ejecuciones públicas de Gheist que Wiz examinó, en todas las versiones observadas de la herramienta, seguían pendientes de aprobación y, por tanto, no habían ejecutado su carga útil.
GitHub introdujo esa retención el 28 de julio para los flujos de trabajo que identifica como potencialmente maliciosos. Un colaborador con permiso de escritura debe aprobar una ejecución marcada. La medida solo cubre los repositorios públicos en github.com; los repositorios privados y GitHub Enterprise Server quedan fuera de su alcance. Además, su protección depende de que GitHub marque la ejecución.
De acceso al repositorio a credenciales legibles
Gheist parte de un token ya robado que concede acceso de escritura a los repositorios. Wiz rastreó algunos tokens hasta registros de infostealers y compromisos previos de la cadena de suministro, entre ellos el compromiso de marzo del escáner de vulnerabilidades Trivy.
En los repositorios accesibles con un token, la herramienta envía un commit sin firmar a nombre del propietario del token. Su combinación identificativa es una rama que coincide con ^ci-sync-[0-9a-z]{6}$, el mensaje de commit ci: sync config y un único archivo añadido: .github/workflows/sync-check.yml.
Ese envío lanza un flujo de trabajo que escribe el entorno del job y los secretos accesibles en sus registros, usando codificación base32. La documentación de GitHub advierte que el enmascaramiento de secretos podría fallar cuando los valores han sido transformados. Según Wiz, la representación base32 evade el enmascaramiento porque difiere del secreto original. Una cadena de detección publicada es toJSON(secrets) }}' | base32 -w 120.
Aunque el flujo de trabajo declara un bloque de permisos vacío, eso restringe su GITHUB_TOKEN en lugar de impedirle leer secretos. Los atacantes usan su token robado para recuperar los registros resultantes a través de la API de GitHub y, segundos después, eliminan la ejecución. Por tanto, la recopilación utiliza el propio GitHub sin necesidad de un destino separado controlado por los atacantes.
Una variante también programa un job para cada entorno de despliegue, con el fin de acceder a secretos asociados a esos entornos, que según Wiz suelen incluir credenciales de producción. Los revisores obligatorios pueden impedir ese acceso: la documentación de GitHub indica que los secretos de un entorno permanecen no disponibles para un job hasta que un revisor obligatorio lo aprueba.
Alrededor del 19 de octubre, otra variante añadió un job dirigido a runners autoalojados, es decir, las máquinas que las organizaciones operan para ejecutar jobs de Actions. Aparentemente apunta a repositorios con un runner registrado e intenta buscar 31 tipos de tokens y credenciales, incluidas claves privadas, variables de entorno de procesos, credenciales del rol de instancia de AWS, archivos de credenciales del runner y el token de la cuenta de servicio de Kubernetes. Wiz describió el job como defectuoso y no informó de ninguna ejecución exitosa.
Detección y contención de la exposición
Wiz recomienda revisar los registros de auditoría de GitHub en busca del user agent gheist y de ejecuciones en ramas ci-sync-* eliminadas en el minuto siguiente a su finalización. En los runners autoalojados, los indicadores incluyen .gdmp_pat, .gdmp_patg, .gdmp_sweep y el marcador de registro del job ===== gdmp-pivot:.
Los registros de la nube ofrecen otra vía de detección cuando los flujos de trabajo usan OIDC, que proporciona credenciales de corta duración en lugar de claves de nube almacenadas. Las asunciones de roles o los intercambios de tokens pueden identificar una rama ci-sync- en su subject. Hay una limitación: el subject OIDC predeterminado de GitHub incluye el nombre de la rama solo en los jobs que no usan un entorno de despliegue.
Eliminar una rama o borrar una ejecución no puede revertir las credenciales ya expuestas. Wiz recomienda a los repositorios afectados rotar todos los secretos accesibles de repositorio, organización y entorno, revocar los tokens detrás de los commits y revisar los dispositivos de sus propietarios en busca de infostealers. Las ejecuciones retenidas de sync-check deben permanecer sin aprobar, y no debe ejecutarse código de las ramas plantadas. Si alguna de esas ramas se ejecutó en un runner autoalojado, Wiz recomienda considerar comprometidos tanto la máquina como su rol de instancia en la nube.
Para limitar accesos futuros, Wiz recomienda revisores obligatorios para los entornos que contienen secretos de producción, confianza OIDC restringida a ramas o entornos protegidos, y GitHub Apps o tokens de grano fino y corta duración en lugar de tokens de acceso personal. También recomienda runners autoalojados de corta duración, sin acceso al socket de Docker ni acceso irrestricto a los metadatos de la nube.




