1 puntos por GN⁺ 2025-04-01 | 1 comentarios | Compartir por WhatsApp
  • Si se aprovechaba la breve ventana de validez de un GITHUB_TOKEN expuesto públicamente, podía abrirse una ruta de ataque a la cadena de suministro capaz de derivar en ejecución de código en workflows de GitHub Actions de varios repositorios que usan CodeQL
  • El token estaba dentro de un workflow artifact subido por un workflow de depuración fallido del repositorio github/codeql-action, y era un token de instalación de GitHub App con permisos contents: write, actions: write y packages: write
  • El éxito del ataque dependía de una condición de carrera: descargar y usar el token entre la subida del artifact y el fin del job; según los logs reales, en unos 1.022 segundos, y en observaciones dentro de unos 2 segundos, se logró crear una branch, hacer push de poc.txt y crear un tag
  • Como la configuración predeterminada de CodeQL ejecuta internamente el tag v3 de github/codeql-action, y ese tag no era inmutable, si un atacante movía el tag a un commit malicioso, incluso los repositorios que usaban el workflow predeterminado de CodeQL podían verse afectados
  • GitHub publicó un PR unas 3 horas después del reporte para desactivar la subida de artifacts de depuración, asignó CVE-2025-24362 y afirmó en el advisory que no hay evidencia de compromiso de la plataforma ni de sus sistemas

Ruta de ataque a la cadena de suministro de CodeQL iniciada desde un artifact público

  • Un solo secreto público pudo haber derivado en un potencial ataque a la cadena de suministro contra GitHub CodeQL
  • Ese secreto era válido solo durante aproximadamente 1.022 segundos cada vez, pero dentro de esa ventana un atacante podía ejecutar los pasos necesarios para correr código dentro de un workflow de GitHub Actions
  • El alcance podía llegar tanto a GitHub Cloud como a GitHub Enterprise
  • Los posibles daños eran los siguientes
    • Filtración de código fuente de repositorios privados que usan CodeQL
    • Robo de GitHub Actions secrets dentro del job del workflow de CodeQL
    • Ejecución de código en infraestructura interna donde se ejecuta el workflow de CodeQL
    • Robo de secrets de workflows que usan GitHub Actions Cache en repositorios que usan CodeQL
  • Según el advisory de GitHub, GitHub no encontró evidencia de compromiso de la plataforma ni de sus sistemas

Cómo se encontró el secreto

  • Se usó Actions Artifacts Secret Scanner, que descarga artifacts de workflows de GitHub Actions, descomprime recursivamente los archivos y escanea secretos con Nosey Parker
  • Esta función se integró en Chariot y se liberó como open source como una función de Gato
  • Tras escanear durante un día, se encontró un token en un artifact del repositorio github/codeql-action
    • El artifact objetivo era el zip my-debug-artifacts subido por el workflow “PR Check – Debug artifacts after failure”
    • Al extraer el archivo interno my-db-java-partial.zip, se detectó en un crash report un GitHub Token que empezaba con ghs_
    • Una verificación manual confirmó que era un token de instalación de GitHub App almacenado en un archivo que contenía variables de entorno del GitHub Runner

Estructura de conexión entre GitHub Actions, artifacts y CodeQL

  • GitHub Actions es una plataforma de CI/CD que ejecuta en runners las tareas definidas en workflows YAML
  • Cada workflow run crea automáticamente un GITHUB_TOKEN, que es un token de instalación de GitHub App generado automáticamente
    • El runner usa este token para autenticarse ante GitHub y realizar las tareas que requiere el workflow
    • Los permisos pueden configurarse a nivel de archivo de workflow, repositorio u organización
  • Un artifact de workflow de GitHub Actions es un archivo que el workflow almacena en GitHub Actions para usarlo más adelante
    • De forma predeterminada, es accesible para quienes tienen permiso de lectura sobre el repositorio
    • Se conserva hasta 90 días
  • CodeQL es el motor de análisis de código de GitHub y se usa para realizar análisis estático en repositorios de GitHub para encontrar vulnerabilidades
  • Las herramientas de seguridad como CodeQL a veces necesitan acceder a sistemas y datos sensibles, por lo que, si se ven comprometidas, pueden convertirse en una puerta trasera

Permisos del token y condición de carrera

  • En la etapa “Setup Job” de los logs del workflow se confirmó que ese GITHUB_TOKEN tenía permisos completos de escritura
  • Los permisos especialmente importantes eran los siguientes
    • contents: write: permite crear branches, crear tags y subir release artifacts
    • actions: write: permite disparar trabajos de Actions y eventos workflow_dispatch
    • packages: write: permite subir packages
  • Como el token solo era válido mientras se ejecutaba ese workflow job, el ataque requería tres condiciones
    • El token debía tener permisos de escritura
    • Había que usar la upload artifact API v4 para obtener el artifact antes de que terminara el job
    • El tiempo entre la subida del artifact y la finalización del job debía ser suficiente para descargar, descomprimir y usar el token
  • CodeQL no usaba directamente la action actions/upload-artifact, sino que utilizaba manualmente el cliente de upload artifact desde el código fuente, y los comentarios del código indicaban que usaba la versión 4
  • Según los logs crudos de GitHub, el intervalo era muy corto
    • “Finalizing artifact upload”: 17:22:09.888
    • “Cleaning up orphan processes”: 17:22:10.911
    • El cálculo daba aproximadamente 1.022 segundos, y por observación el token seguía siendo válido cerca de 1 segundo más después del cleanup, por lo que se consideró una ventana de unos 2 segundos

PoC: ejecución de artifact_racer.py

  • Se escribió un script de Python de verificación llamado artifact_racer.py
  • El flujo de funcionamiento era el siguiente
    • Consultaba continuamente el repositorio github/codeql-action para detectar si se iniciaba el workflow “PR Check – Debug artifacts after failure”
    • Monitoreaba los artifacts del workflow en ejecución
    • Cuando aparecía un artifact, lo descargaba y extraía el GITHUB_TOKEN
    • Con el token extraído, creaba una nueva branch
    • Hacía push de un archivo vacío llamado poc.txt a esa branch
    • Creaba un nuevo tag sobre ese commit
  • Como el artifact pesaba alrededor de 21 MB, se consideró que había posibilidades de éxito dentro del tiempo disponible
  • Tras tener éxito en un repositorio de prueba, se ejecutó contra el repositorio github/codeql-action
  • Unas 2 horas después se ejecutó el workflow, y el script obtuvo el token y logró crear la branch, hacer push del archivo y crear el tag

Mayor alcance por manipulación de tags

  • El atacante podía agregar código malicioso a una branch no protegida usando ese token
  • De forma más sigilosa, también era posible insertar un pequeño cambio malicioso en una feature branch antes del merge y esperar a que se fusionara
  • El permiso para crear tags también era importante
    • Si se colocaba el tag v3 en un commit con código malicioso, los usuarios que usaran manualmente codeql-action@v3 podían ejecutar ese código malicioso
  • El mayor impacto venía de la configuración predeterminada de CodeQL
    • Cuando se activa CodeQL con la configuración predeterminada desde la configuración del repositorio, se ejecuta un workflow especial de GitHub Actions que no aparece en la lista de workflows del repositorio
    • Este workflow hace checkout del repositorio, inicializa CodeQL, ejecuta el scan de CodeQL y sube los resultados
    • Internamente ejecuta la action del repositorio github/codeql-action en el commit al que apunta el tag v3
  • En ese momento, el tag v3 no era inmutable y tampoco se usaba el pinning de workflows recomendado por GitHub
  • Si un atacante movía el tag v3 a un commit malicioso usando el GITHUB_TOKEN comprometido, los repositorios que usaban el workflow predeterminado de CodeQL podían ejecutar la action de CodeQL maliciosa
  • Como la action de CodeQL hace checkout del código fuente del repositorio objetivo, una action maliciosa podía filtrar el código fuente de repositorios que usan la configuración predeterminada de CodeQL

Ruta de envenenamiento de GitHub Actions Cache

  • El GITHUB_TOKEN de la action predeterminada de CodeQL solo tiene permisos de lectura, por lo que solo con la configuración predeterminada no era posible realizar de inmediato operaciones de escritura en el repositorio, instalar una puerta trasera en un release ni robar secrets mediante workflow_dispatch
  • En cambio, la action predeterminada de CodeQL se ejecuta en la main branch del repositorio
  • La main branch de un repositorio de GitHub puede escribir cache entries que se usan en todo el repositorio, lo que abre una oportunidad de envenenamiento de GitHub Actions Cache
  • GitHub Actions Cache Poisoning es una técnica tratada en el artículo de Adnan Khan, y Cacheract es malware que deja persistencia en el build pipeline mediante cache poisoning
  • Si un atacante desplegaba Cacheract desde el workflow de CodeQL, podía darse el siguiente flujo
    • Predicción de cache entries
    • Sobrescritura de esa entry con una action maliciosa
    • Obtención de ejecución de código en workflows que usan actions/cache
    • Robo de los GitHub Actions secrets de ese workflow y de un GITHUB_TOKEN con mayores privilegios
  • Incluso si se descubría la action de CodeQL maliciosa y se corregía la vulnerabilidad, Cacheract podía seguir realizando cache poisoning
  • Como ejemplos de repositorios importantes que usan CodeQL junto con actions/cache se identificaron Homebrew, Angular y Grafana

CVE-2025-24362 y corrección

  • Como resultado de esta divulgación se asignó CVE-2025-24362
  • El GITHUB_TOKEN público estaba dentro de un debug artifact subido por CodeQL Action tras un workflow de code scanning fallido
  • El repositorio CodeQL Actions estaba provocando fallos de forma intencional, pero si otros usuarios de CodeQL Actions tenían fallos similares, también podrían haber expuesto sus propios secrets como variables de entorno del workflow
  • El problema se corrigió en CodeQL Action 3.28.3
  • El mayor impacto potencial no estaba tanto en el CVE en sí, sino en la ruta para explotar la vulnerabilidad contra el repositorio CodeQL Actions y realizar un ataque a la cadena de suministro contra usuarios de CodeQL

Respuesta de GitHub y mitigaciones recomendadas

  • El cronograma de respuesta de GitHub fue el siguiente
    • 22 de enero de 2025, 15:13 UTC: se envió el reporte a GitHub
    • 22 de enero de 2025, 17:48 UTC: GitHub confirmó la recepción
    • 22 de enero de 2025, 18:28 UTC: GitHub confirmó la vulnerabilidad, desactivó temporalmente el workflow “PR Check – Debug artifacts after failure” y envió un PR para desactivar la subida de artifacts de depuración
    • 24 de enero de 2025: GitHub asignó CVE-2025-24362 y publicó el security advisory
  • Las medidas para reducir el riesgo de exposición de secrets en artifacts de workflows de GitHub Actions son las siguientes
    • Subir como workflow artifact solo archivos o directorios específicos
    • Evitar subir artifacts que incluyan variables de entorno, .git/config o archivos del directorio <path_to_runner_dir>/_work/_temp/ del runner
    • Limitar los permisos de GITHUB_TOKEN a read-only
    • Ejecutar un escaneo de secrets antes de subir artifacts

1 comentarios

 
GN⁺ 2025-04-01
Comentarios de Hacker News
  • Si GitHub hubiera impulsado antes las acciones inmutables, el impacto de esto habría sido mucho menor.
    Sigo repitiendo que esta función bloquearía más del 70% de la superficie de ataque actual de GitHub Actions. Viendo que hay incidentes todas las semanas, creo que ya es hora de lanzarla.
    [1] https://github.com/features/preview/immutable-actions

    • Si todavía está en preview, probablemente sea por una buena razón. Podría haber bugs graves, agujeros de seguridad o rupturas de compatibilidad que, si se apresuran, causarían aún más daño.
  • No hay explicación de por qué este token temporal tenía permisos para crear nuevos despliegues y generar attestations de artefactos.
    Dijeron que desactivaron los logs de depuración como corrección, pero no respondieron si ajustaron los permisos del token temporal de forma más adecuada para el motor de análisis de código.

    • Parece una historia vieja que todos conocemos. El ingeniero que lo estaba construyendo se topó con denegaciones de permisos, le dio todos los permisos y nunca volvió después a reducirlos al mínimo privilegio.
    • Si el token temporal de GitHub Actions no tiene un alcance de permisos definido en el workflow, se aplica un alcance predeterminado permisivo o restrictivo según la configuración del repositorio. Esta configuración también puede definirse a nivel de organización para restringir todos los repositorios.
      En el pasado, el valor predeterminado era solo la configuración permisiva, así que lamentablemente esta configuración se usa con frecuencia en organizaciones y repositorios antiguos.
      Al crear un repositorio nuevo, hereda el valor predeterminado de la organización superior, por lo que, si nadie lo cambia, este valor inseguro sigue quedando. No hay una configuración global de usuario, así que los nuevos repositorios de propiedad personal usan el valor predeterminado restrictivo. Tengo entendido que las organizaciones recién creadas también usan mejores valores predeterminados.
      [0]: https://docs.github.com/en/actions/security-for-github-actio...
    • Los tokens temporales de actions tienen, por defecto, permisos completos de escritura. Hay que optar explícitamente por la versión de solo lectura.

      Read and write permissions
      Workflows have read and write permissions in the repository for all scopes.
      Leyendo solo esta línea de la documentación (https://docs.github.com/en/actions/security-for-github-actio...), uno podría pensar otra cosa.
      If the default permissions for the GITHUB_TOKEN are restrictive, you may have to elevate the permissions to allow some actions and commands to run successfully.
      Pero en nuestra organización de GitHub, "Read and write permissions" era el valor predeterminado, así que puedo confirmar que esa frase de la documentación no tiene sentido.

    • El hackeo a Microsoft de 2023 fue similar. Fue un caso en el que CISA los reprendió públicamente por su seguridad pésima, y aun así la entrada de blog que Microsoft publicó para explicar el incidente dejaba demasiadas preguntas sin responder.
    • Espero que desactivar los logs de depuración haya sido una medida temporal rápida. No debería ser la corrección final.
  • Cada vez estoy más convencido de que CI y CD deberían ser entornos completamente separados. Que CI se vea comprometido no debería implicar que se filtren tokens relacionados con CD.

    • Este es un tema que personalmente me interesa mucho, y creo que la solución no es tanto separar CD en un sistema pequeño y especializado aparte, sino hacer que CD funcione como algo multifactor.
      Por ejemplo, exigir que se cumpla una condición como "sub":"repo:octo-org/octo-repo:environment:prod"[1], y si se quiere endurecer más el sistema, se pueden agregar otros [claims divertidos][].
      1: https://docs.github.com/en/actions/security-for-github-actio...
      fun claims: https://github.com/github/actions-oidc-debugger#readme
    • En esencia, así se ve la separación de funciones e intereses. Algunos buenos proyectos también funcionan de esta manera. Las técnicas concretas, las herramientas y la frontera entre CI y CD dependen de la naturaleza del producto final, pero conceptualmente es totalmente correcto.
  • El tiempo de respuesta no fue ninguna broma. La respuesta de GitHub fue bastante impresionante.

    • Que se haya expuesto un token público con permisos completos de escritura no es tan impresionante.
  • Como alguien con el apellido Prater, y dado que este nombre proviene de Praetorian, realmente me gustaría tener praetorian.com.

    • Para eso tendrías que haberlo pensado antes de que saliera la película “The Net” en 1995.
    • Su proyecto gokart era excelente.
  • Usar GitHub Actions públicas es buscarse problemas, y más si no se analizan los procedimientos del workflow.
    Mejor autohospedar woodpecker u otros buenos builders de CI (circle, travis, gitlab, etc.).

  • Tenemos CodeQL aplicado a los PR de OpenZFS. Esto no es un problema para OpenZFS, porque nuestro código no es secreto :)

    • Aunque el código no sea secreto, no creo que sea una buena decisión. Un atacante podría haber agregado cualquier cosa al código o a los artefactos de release.
      Aun así, al menos es bueno que se haya resuelto rápido.
  • ¿Esto ya se corrigió?

    • Como dicen el artículo y los comentarios aquí, sí. Después de que se reportó en enero, GitHub lo mitigó en menos de 3 horas.
  • Este sitio tiene un rendimiento tan malo que casi ni puedo hacer scroll.