Organisation GitHub compromise ? Guide de réponse
Organisation GitHub compromise : préserver le journal d'audit, révoquer jetons et workflows, délimiter avec les événements Git, puis enquêter dans le cloud.
En bref. Une organisation GitHub compromise, c'est le plus souvent un incident de chaîne d'approvisionnement logicielle à son tout début : quelqu'un détient un jeton, une clé ou une session de propriétaire, et votre code source, votre CI/CD et vos identifiants cloud sont à sa portée. Travaillez dans cet ordre : préserver le journal d'audit et les événements Git (conservés 7 jours), contenir (révoquer les identifiants, arrêter les workflows, supprimer la persistance), délimiter le périmètre avec les preuves, puis rebondir vers les comptes cloud dont les clés étaient stockées dans les secrets Actions. Vous pouvez passer les exports dans l'analyseur gratuit dans le navigateur, qui n'envoie rien nulle part.
GitHub est souvent la première prise d'un attaquant dans le cloud. Un seul jeton d'accès personnel volé ouvre les dépôts privés, les workflows qui déploient en production et les secrets que ces workflows détiennent. Ce guide est le point d'entrée de la série : chaque section résume un scénario et renvoie vers le playbook qui le traite en détail.
Comment les organisations GitHub se font compromettre
Presque tous les incidents que je vois commencent par l'un de ces scénarios :
| Point d'entrée | Traces typiques dans le journal d'audit | Playbook |
|---|---|---|
| Jeton d'accès personnel volé par un infostealer ou fuité dans du code | même hashed_token depuis un nouveau pays / une nouvelle IP, rafales de git.clone | Jeton GitHub fuité |
| Jeton d'OAuth app ou de GitHub App volé chez un tiers | activité API d'une intégration, dépôts listés puis téléchargés | Jeton GitHub fuité |
| Workflow empoisonné ou action tierce compromise | workflows.created_workflow_run sur une nouvelle branche, blobs encodés dans les logs d'exécution | Fuite de secrets Actions |
| Runner auto-hébergé malveillant ou compromis | org.register_self_hosted_runner, jobs sur des runners inconnus | Sécurité des runners auto-hébergés |
| Prise de contrôle d'un compte propriétaire | org.update_member vers admin, org.disable_two_factor_requirement | Prise de contrôle d'organisation |
Ce n'est pas de la théorie. En avril 2022, GitHub a signalé qu'un attaquant avait utilisé des jetons OAuth d'utilisateurs volés, émis pour Heroku et Travis CI, pour lister et télécharger des dépôts privés de dizaines d'organisations. En mars 2025, la CISA a publié une alerte sur la compromission de tj-actions/changed-files (CVE-2025-30066) : une action très utilisée avait été modifiée pour afficher les secrets de CI dans les logs des workflows. En septembre 2025, l'alerte de la CISA sur l'écosystème npm décrivait un malware qui récoltait des PAT GitHub et des clés cloud sur les postes de développeurs.
Étape 1 : préserver les preuves d'abord
La durée de conservation dicte l'ordre du travail. GitHub conserve les événements d'audit de l'organisation 180 jours, mais les événements Git seulement sept jours. Par défaut, les logs Actions sont conservés 90 jours, et toute personne ayant un accès en écriture au dépôt peut supprimer les logs d'une exécution. Un attaquant qui détient votre jeton peut donc effacer les preuves les plus utiles.
Avant de toucher à quoi que ce soit :
- Exportez le journal d'audit de l'organisation en JSON pour toute la fenêtre suspecte, plus quelques semaines de référence avant.
- Sur GitHub Enterprise Cloud, récupérez les événements Git (
git.clone,git.fetch,git.push) via l'API REST avecinclude=all, ou via le menu « Export Git Events » de l'entreprise. - Téléchargez l'archive de logs de chaque exécution de workflow suspecte.
- Si vous diffusez le journal d'audit vers S3, Azure Blob ou GCS, copiez les dossiers
YYYY/MM/DD/HH/MM/concernés. - Stockez les copies en lecture seule et calculez leurs empreintes.
Le guide d'export détaille chaque source étape par étape, avec l'offre GitHub requise pour chacune.
Étape 2 : contenir
Le tutoriel de réponse à incident de GitHub recommande de choisir les mesures de confinement en fonction de la menace, et non de toutes les appliquer à l'aveugle. Voici les actions qui arrêtent la plupart des incidents réels :
- Révoquer les identifiants en cause. Si vous avez la valeur du jeton, n'importe qui peut le révoquer via l'API de révocation d'identifiants. Sinon, c'est son propriétaire qui le révoque, ou un propriétaire de l'organisation qui révoque son autorisation SSO. Vérifiez l'absence d'infostealer sur le poste du développeur avant d'émettre un nouveau jeton.
- Arrêter le pipeline. Annulez les workflows en cours, supprimez les branches de l'attaquant et, si nécessaire, désactivez Actions sur les dépôts touchés.
- Supprimer la persistance. Cherchez les clés de déploiement, clés SSH, webhooks, GitHub Apps, approbations d'OAuth apps, collaborateurs externes et runners auto-hébergés ajoutés pendant la fenêtre d'incident.
- Renouveler tous les secrets que les workflows touchés pouvaient lire, pas seulement ceux que vous avez vus s'afficher.
Étape 3 : délimiter le périmètre avec le journal d'audit
Délimiter, c'est répondre à trois questions : quel identifiant, qu'a-t-il lu, qu'a-t-il modifié.
Quel identifiant
Les événements authentifiés par jeton portent hashed_token, programmatic_access_type et token_scopes. GitHub documente comment identifier les événements du journal d'audit effectués par un jeton d'accès. Regroupez par hashed_token plutôt que par acteur. Un jeton volé appartient à un utilisateur légitime, donc son activité se mélange à la sienne, mais les IP et les pays de l'attaquant diffèrent.
Ce qu'il a lu
Les événements Git indiquent quels dépôts ont été clonés et quand. Dix dépôts distincts ou plus clonés depuis une même IP en moins d'une heure, ce n'est pas le comportement normal d'un développeur. Consultez la détection de l'exfiltration de dépôts pour les autres voies de sortie : repo.download_zip, changements de visibilité et transferts.
Ce qu'il a modifié
Filtrez sur les actions qui affaiblissent un contrôle ou ajoutent un accès. Ce tableau donne le minimum à revoir :
| Domaine | Actions à examiner |
|---|---|
| Propriétaires et admins | org.update_member / org.add_member avec permission: admin, business.add_admin |
| Authentification | org.disable_two_factor_requirement, org.disable_saml, org.update_saml_provider_settings |
| Réseau | ip_allow_list.disable, ip_allow_list.disable_for_installed_apps |
| Journalisation | audit_log_streaming.destroy, audit_log_streaming.update |
| Intégrité du code | protected_branch.destroy, repository_ruleset.destroy, protected_branch.policy_override |
| CI/CD | environment.remove_protection_rule, org.register_self_hosted_runner, *.create_actions_secret |
| Persistance | public_key.create, hook.create, integration_installation.create |
L'article sur la protection de branche et celui sur la prise de contrôle expliquent comment lire chacune de ces actions.
Étape 4 : suivre les secrets jusque dans le cloud
Les secrets Actions sont surtout des identifiants cloud. Quand ils fuient, l'incident ne fait que commencer. Les preuves suivantes se trouvent dans CloudTrail, dans les journaux d'audit de Google Cloud ou dans les journaux d'activité Azure. L'article sur le rebond vers le cloud couvre cette étape et renvoie vers les outils frères pour AWS, Google Cloud et Azure.
Où intervient l'analyseur de ce site
L'outil GitHub Forensics lit les exports décrits plus haut : JSON / CSV de l'interface, pages de l'API REST, fichiers de streaming .json.log.gz, export des événements Git, archives ZIP de logs Actions et alertes de secret scanning. Il évalue 31 règles de détection publiées et renvoie un verdict (Aucun signe de compromission, Activité suspecte ou Compromis), les constats avec leurs preuves et leurs techniques ATT&CK, une chronologie et une liste de remédiation. Tout s'exécute localement en WebAssembly.
Les règles sont des heuristiques à seuils, et un verdict sans signe de compromission ne couvre que ce que contient l'export. L'article sur les limites liste ce que le journal d'audit n'enregistre jamais. Pour voir d'abord à quoi ressemble une analyse complète, le cas fictif northwind-labs parcourt l'incident d'exemple constat par constat.
FAQ
Que faire en premier quand une organisation GitHub est compromise ?
Exporter les preuves avant de toucher à quoi que ce soit : le journal d'audit de l'organisation (JSON) et, sur Enterprise Cloud, les événements Git, conservés sept jours seulement. Télécharger les archives de logs des exécutions de workflow suspectes. Ensuite seulement, révoquer les jetons en cause et arrêter les workflows malveillants.
GitHub enregistre-t-il les dépôts clonés ?
Oui, sous forme d'événements git.clone, git.fetch et git.push. On ne les obtient que par l'API REST avec include=git ou include=all, par le streaming du journal d'audit ou par l'export des événements Git de l'entreprise, et uniquement sur GitHub Enterprise Cloud. Ils sont conservés sept jours.
Peut-on enquêter sur un incident GitHub sans SIEM ?
Oui. L'export du journal d'audit, l'export des événements Git et les archives de logs Actions sont de simples fichiers JSON, gzip et ZIP. On peut les lire avec jq, ou les déposer dans un analyseur hors ligne comme celui de ce site, qui s'exécute entièrement dans le navigateur.
Pour aller plus loin
- GitHub Docs : Common security incident investigation areas
- OWASP : Top 10 CI/CD Security Risks
- MITRE ATT&CK : T1213.003 Code Repositories, T1677 Poisoned Pipeline Execution
- Glossaire : journal d'audit GitHub, jeton d'accès personnel, streaming du journal d'audit