Menace d’attaque supply chain via CodeQL : un incident de sécurité déclenché par une clé secrète GitHub exposée
(praetorian.com)- Une fenêtre de validité très courte d’un GITHUB_TOKEN exposé pouvait ouvrir une voie d’attaque supply chain menant à l’exécution de code dans les workflows GitHub Actions de plusieurs dépôts utilisant CodeQL
- Le token se trouvait dans un workflow artifact publié par un workflow de débogage en échec du dépôt
github/codeql-action, et il s’agissait d’un token d’installation GitHub App avec les permissionscontents: write,actions: write,packages: write - Le succès de l’attaque dépendait d’une condition de concurrence : télécharger et utiliser le token après l’upload de l’artifact mais avant la fin du job ; d’après les logs, cela représentait environ 1,022 seconde, et en pratique environ 2 secondes ont suffi pour créer une branche, pousser
poc.txtet créer un tag - Comme la configuration CodeQL par défaut exécutait en interne le tag v3 de
github/codeql-action, et que ce tag n’était pas immuable, un attaquant pouvait le déplacer vers un commit malveillant et ainsi affecter aussi les dépôts utilisant le workflow CodeQL par défaut - Environ 3 heures après le signalement, GitHub a soumis une PR pour désactiver l’upload des artifacts de débogage, a attribué CVE-2025-24362 et a indiqué dans son advisory n’avoir trouvé aucune preuve de compromission de la plateforme ou de ses systèmes
Voie d’attaque supply chain contre CodeQL à partir d’un artifact public
- Un seul secret exposé pouvait déboucher sur une potentielle attaque supply chain visant GitHub CodeQL
- Ce secret n’était valide qu’environ 1,022 seconde à la fois, mais cela suffisait pour qu’un attaquant exécute les étapes nécessaires à une exécution de code à l’intérieur d’un workflow GitHub Actions
- L’impact potentiel concernait à la fois GitHub Cloud et GitHub Enterprise
- Les dommages possibles étaient les suivants
- Exfiltration du code source de dépôts privés utilisant CodeQL
- Vol des GitHub Actions secrets présents dans les jobs de workflow CodeQL
- Exécution de code sur l’infrastructure interne où les workflows CodeQL s’exécutaient
- Vol de secrets dans les workflows de dépôts utilisant CodeQL et GitHub Actions Cache
- Selon l’advisory GitHub, l’entreprise n’a trouvé aucune preuve de compromission de la plateforme ou de ses systèmes
Comment le secret a été découvert
- Un Actions Artifacts Secret Scanner a été utilisé : il télécharge les workflow artifacts GitHub Actions, les décompresse récursivement, puis recherche des secrets avec Nosey Parker
- Cette fonctionnalité a été intégrée à Chariot et open source via Gato
- Après une journée de scan, un token a été trouvé dans les artifacts du dépôt
github/codeql-action- L’artifact visé était le zip
my-debug-artifacts, envoyé par le workflow « PR Check – Debug artifacts after failure » - En décompressant
my-db-java-partial.zip, un token GitHub commençant parghs_a été détecté dans un crash report - Une vérification manuelle a montré qu’il s’agissait d’un token d’installation GitHub App stocké dans un fichier contenant les variables d’environnement du GitHub Runner
- L’artifact visé était le zip
Structure de liaison entre GitHub Actions, artifacts et CodeQL
- GitHub Actions est une plateforme CI/CD qui exécute sur des runners les tâches définies dans des workflows YAML
- Chaque exécution de workflow génère automatiquement un GITHUB_TOKEN, qui est un token d’installation GitHub App
- Le runner s’authentifie auprès de GitHub avec ce token pour effectuer les actions demandées par le workflow
- Les permissions peuvent être configurées au niveau du fichier de workflow, du dépôt ou de l’organisation
- Un workflow artifact GitHub Actions est un fichier qu’un workflow stocke dans GitHub Actions pour un usage ultérieur
- Par défaut, il est accessible à toute personne ayant un droit de lecture sur le dépôt
- Il est conservé jusqu’à 90 jours
- CodeQL est le moteur d’analyse de code de GitHub, utilisé pour faire de l’analyse statique sur les dépôts GitHub afin de trouver des vulnérabilités
- Les outils de sécurité comme CodeQL doivent parfois accéder à des systèmes et données sensibles ; s’ils sont compromis, ils peuvent devenir une porte dérobée
Permissions du token et condition de concurrence
- Dans l’étape « Setup Job » des logs de workflow, il a été confirmé que ce GITHUB_TOKEN disposait de permissions d’écriture complètes
- Les permissions les plus sensibles étaient les suivantes
contents: write: permet de créer des branches, créer des tags, envoyer des release artifactsactions: write: permet de lancer des tâches Actions et de déclencher l’événementworkflow_dispatchpackages: write: permet d’envoyer des packages
- Comme le token n’était valide que pendant l’exécution du job de workflow, l’attaque nécessitait trois conditions
- Le token devait avoir des permissions d’écriture
- Il fallait utiliser l’upload artifact API v4, qui permet de récupérer un artifact avant la fin du job
- Le délai entre l’upload de l’artifact et la fin du job devait suffire pour télécharger, extraire et utiliser le token
- CodeQL n’utilisait pas directement l’action
actions/upload-artifact, mais appelait manuellement le client d’upload d’artifacts dans son code source ; d’après un commentaire dans le code, il utilisait la version 4 - D’après les logs GitHub bruts, l’intervalle était extrêmement court
- « Finalizing artifact upload » : 17:22:09.888
- « Cleaning up orphan processes » : 17:22:10.911
- Le calcul donnait environ 1,022 seconde, mais l’observation montrait qu’après le cleanup le token restait encore valide environ 1 seconde, ce qui a conduit à retenir une fenêtre pratique d’environ 2 secondes
PoC : exécution de artifact_racer.py
- Un script Python de validation nommé
artifact_racer.pya été écrit - Son fonctionnement était le suivant
- Il interrogeait en continu le dépôt
github/codeql-actionpour détecter le démarrage du workflow « PR Check – Debug artifacts after failure » - Il surveillait les artifacts du workflow en cours d’exécution
- Lorsqu’un artifact apparaissait, il le téléchargeait et en extrayait le GITHUB_TOKEN
- Avec ce token, il créait une nouvelle branche
- Il poussait ensuite un fichier vide nommé
poc.txtsur cette branche - Enfin, il créait un nouveau tag sur ce commit
- Il interrogeait en continu le dépôt
- Comme l’artifact faisait environ 21 MB, il a été estimé qu’une réussite dans le temps imparti était possible
- Après un succès sur un dépôt de test, le script a été exécuté sur le dépôt
github/codeql-action - Environ 2 heures plus tard, le workflow s’est exécuté, et le script a réussi à récupérer le token, créer une branche, pousser le fichier et créer un tag
L’élargissement de l’impact via la manipulation des tags
- Avec ce token, un attaquant pouvait ajouter du code malveillant sur une branche non protégée
- De façon plus discrète, il pouvait aussi insérer une petite modification malveillante dans une feature branch avant fusion, puis attendre le merge
- Le droit de créer des tags était lui aussi crucial
- En plaçant le tag
v3sur un commit contenant du code malveillant, un attaquant pouvait faire exécuter ce code aux utilisateurs appelant manuellementcodeql-action@v3
- En plaçant le tag
- L’impact le plus important venait toutefois de la configuration CodeQL par défaut
- Lorsqu’on active CodeQL avec la configuration par défaut dans les paramètres du dépôt, un workflow GitHub Actions spécial s’exécute sans apparaître dans la liste des workflows du dépôt
- Ce workflow effectue le checkout du dépôt, l’initialisation de CodeQL, le scan CodeQL et l’upload des résultats
- En interne, il exécute l’action du dépôt
github/codeql-actionau commit pointé par le tag v3
- À ce moment-là, le tag
v3n’était pas immuable, et le pinning de workflow recommandé par GitHub n’était pas utilisé - Si un attaquant déplaçait le tag
v3vers un commit malveillant à l’aide du GITHUB_TOKEN compromis, les dépôts utilisant le workflow CodeQL par défaut pouvaient exécuter l’action CodeQL malveillante - Comme l’action CodeQL fait un checkout du code source du dépôt cible, une action malveillante pouvait exfiltrer le code source des dépôts utilisant la configuration CodeQL par défaut
Voie d’empoisonnement du cache GitHub Actions
- Le GITHUB_TOKEN de l’action CodeQL par défaut n’ayant que des permissions en lecture, la configuration par défaut ne permettait pas à elle seule d’effectuer directement des opérations d’écriture sur le dépôt, d’implanter une porte dérobée dans une release ou de voler des secrets via
workflow_dispatch - En revanche, l’action CodeQL par défaut s’exécute sur la branche main du dépôt
- Or la branche main d’un dépôt GitHub peut écrire des entrées de cache réutilisées à l’échelle du dépôt, ce qui ouvre une opportunité d’empoisonnement du cache GitHub Actions
- L’empoisonnement du cache GitHub Actions est une technique présentée dans l’article d’Adnan Khan, et Cacheract est un malware qui s’installe durablement dans un pipeline de build via ce mécanisme
- Si un attaquant déployait Cacheract depuis un workflow CodeQL, le scénario suivant devenait possible
- Prédire une entrée de cache
- Écraser cette entrée avec une action malveillante
- Obtenir une exécution de code dans un workflow utilisant
actions/cache - Voler les GitHub Actions secrets et un GITHUB_TOKEN à privilèges élevés associés à ce workflow
- Même si l’action CodeQL malveillante était découverte et la vulnérabilité corrigée, Cacheract pouvait continuer à empoisonner le cache
- Parmi les grands dépôts utilisant à la fois CodeQL et
actions/cache, des exemples identifiés incluaient Homebrew, Angular et Grafana
CVE-2025-24362 et correctif
- Cette divulgation a conduit à l’attribution de CVE-2025-24362
- Le GITHUB_TOKEN exposé se trouvait dans un debug artifact envoyé par l’action CodeQL après l’échec d’un workflow de code scanning
- Le dépôt CodeQL Actions provoquait volontairement un échec, mais si d’autres utilisateurs de CodeQL Actions avaient subi un échec similaire, ils auraient aussi pu exposer leurs propres secrets via les variables d’environnement du workflow
- Le problème a été corrigé dans CodeQL Action 3.28.3
- L’impact potentiel le plus important n’était pas tant le CVE lui-même que la possibilité d’exploiter la vulnérabilité sur le dépôt CodeQL Actions pour mener ensuite une attaque supply chain contre les utilisateurs de CodeQL
Réponse de GitHub et mesures d’atténuation recommandées
- Le calendrier de réponse de GitHub était le suivant
- 22 janvier 2025 à 15:13 UTC : soumission du signalement à GitHub
- 22 janvier 2025 à 17:48 UTC : accusé de réception par GitHub
- 22 janvier 2025 à 18:28 UTC : GitHub confirme la vulnérabilité, désactive temporairement le workflow « PR Check – Debug artifacts after failure » et soumet une PR pour désactiver l’upload des artifacts de débogage
- 24 janvier 2025 : GitHub attribue CVE-2025-24362 et publie un security advisory
- Les mesures suivantes sont recommandées pour réduire le risque d’exposition de secrets via les workflow artifacts GitHub Actions
- N’envoyer en artifact que des fichiers ou répertoires précis
- Éviter d’envoyer des artifacts contenant des variables d’environnement,
.git/configou des fichiers situés dans le répertoire<path_to_runner_dir>/_work/_temp/du runner - Limiter les permissions du GITHUB_TOKEN au mode lecture seule
- Effectuer un secret scan avant l’upload des artifacts
1 commentaires
Avis sur Hacker News
Si GitHub avait poussé plus vite les actions immuables, l’impact aurait été bien moindre
Je me retrouve à répéter sans cesse que cette fonctionnalité bloquerait aujourd’hui plus de 70 % de la surface d’attaque de GitHub Actions. Vu qu’il y a un incident chaque semaine, je pense qu’il est temps de la lancer
[1] https://github.com/features/preview/immutable-actions
Rien n’explique pourquoi ce jeton temporaire avait même le droit de créer de nouveaux déploiements et de générer des attestations d’artefacts
Ils disent avoir désactivé les journaux de débogage comme correctif, mais ne répondent pas à la question de savoir si les permissions du jeton temporaire ont été ajustées plus correctement pour le moteur d’analyse de code
Dans le passé, le seul réglage par défaut était permissif, si bien que, malheureusement, beaucoup d’anciennes organisations et d’anciens dépôts l’utilisent encore
Quand on crée un nouveau dépôt, il hérite de la valeur par défaut de l’organisation parente ; donc, si personne ne la change, cette valeur par défaut dangereuse reste en place. Il n’existe pas de réglage global utilisateur, donc les nouveaux dépôts appartenant à des particuliers utilisent une valeur par défaut restrictive. À ma connaissance, les organisations nouvellement créées utilisent aussi une meilleure valeur par défaut
[0]: https://docs.github.com/en/actions/security-for-github-actio...
Je suis de plus en plus convaincu que CI et CD doivent être des environnements totalement séparés. Le fait que la CI soit compromise ne devrait pas permettre la fuite de jetons liés au CD
Par exemple, imposer qu’une condition comme
"sub":"repo:octo-org/octo-repo:environment:prod"[1] soit satisfaite, puis, si l’on veut rendre le système plus robuste, ajouter d’autres [claims amusants][]1: https://docs.github.com/en/actions/security-for-github-actio...
claims amusants: https://github.com/github/actions-oidc-debugger#readme
Le temps de réponse n’était pas une blague. La réaction de GitHub est assez impressionnante
En tant que personne portant le nom de famille Prater, sachant que ce nom vient de Praetorian, j’aimerais vraiment posséder praetorian.com
Utiliser des GitHub Actions publiques, c’est chercher les ennuis, encore plus si l’on n’analyse pas les procédures de workflow
Autant auto-héberger woodpecker ou d’autres bons moteurs de CI (circle, travis, gitlab, etc.)
Nous avons activé CodeQL sur les PR d’OpenZFS. Ce n’est pas un problème pour OpenZFS, car notre code n’est pas secret :)
Cela dit, au moins, le problème a été résolu rapidement
Est-ce que ça a été corrigé ?
Ce site a des performances tellement mauvaises que je peux à peine faire défiler la page