Skip to content

Cet outil n'est ni affilié à GitHub, Inc. ou à Microsoft Corporation, ni approuvé ou sponsorisé par elles. GitHub et GitHub Actions sont des marques de GitHub, Inc. Les autres noms sont des marques de leurs propriétaires respectifs.

Protection de branche désactivée ? Auditer les rulesets

Altération d'une protection de branche GitHub : protected_branch.destroy, rulesets modifiés, contournements admin, environnements et pushs survenus entre-temps.

Publié le 6 min de lecture

En bref. La protection de branche et les rulesets sont ce qui empêche un seul identifiant volé de réécrire main. Les attaquants les suppriment (protected_branch.destroy, repository_ruleset.destroy), les affaiblissent (protected_branch.update_*, repository_ruleset.update) ou les contournent (protected_branch.policy_override). Ils font de même avec les environnements de déploiement (environment.remove_protection_rule). Le journal d'audit vous indique qui a modifié quelle règle et quand. L'historique Git et les événements Git vous indiquent ce qui a été poussé pendant qu'elle était désactivée. Rétablissez la règle, puis passez en revue chaque push, force-push et déploiement survenu pendant cette fenêtre.

La revue de code, les status checks obligatoires et les commits signés sont des contrôles de la supply chain. Ils ne fonctionnent que tant qu'ils sont appliqués. Un attaquant disposant des droits d'administration ou du jeton d'un propriétaire peut les désactiver le temps nécessaire. Certains les réactivent ensuite pour masquer le trou. Le journal d'audit conserve les deux événements.

Ce qui peut être altéré

ContrôleSuppriméAffaibliContourné
Protection de branche classiqueprotected_branch.destroyprotected_branch.update_* (nombre de reviews, status checks, force-pushs, suppressions, application aux admins…)protected_branch.policy_override
Rulesets de dépôtrepository_ruleset.destroyrepository_ruleset.updateacteurs de contournement configurés dans le ruleset
Environnements de déploiementenvironment.deleteenvironment.remove_protection_rule, environment.update_protection_rule—

La documentation GitHub sur les branches protégées et les rulesets décrit chaque paramètre. L'entrée du glossaire sur les rulesets de dépôt explique comment rulesets et protection classique se combinent.

Comment l'analyseur le signale

RègleActionsGravité
branch-protection-removedprotected_branch.destroy, repository_ruleset.destroyélevée
branch-protection-overrideprotected_branch.policy_overridemoyenne
environment-protection-removedenvironment.remove_protection_rule, environment.deleteélevée

Les constats sont regroupés par dépôt. Les actions d'affaiblissement (protected_branch.update_*, repository_ruleset.update) ne sont pas des règles, car elles font partie de l'administration normale d'un dépôt. Elles apparaissent dans la chronologie, puisque toutes les actions protected_branch. et repository_ruleset. y sont conservées. Lorsqu'un constat se déclenche, ouvrez la chronologie de ce dépôt et lisez les modifications qui l'entourent.

Analyser une suppression

Un événement protected_branch.destroy contient le dépôt, le motif de branche (name) et les paramètres de la règle à ce moment-là, comme required_approving_review_count et admin_enforced. Vous savez ainsi ce qui a été perdu. Répondez ensuite à quatre questions :

  1. Qui, et avec quoi ? Acteur, adresse IP, user agent et hashed_token. Une session navigateur depuis le bureau du propriétaire n'a rien à voir avec curl/8.5.0 et un jeton utilisé depuis un VPS.
  2. La règle a-t-elle été rétablie, et par qui ? Un protected_branch.create sur la même branche quelques heures plus tard, par un autre acteur, signifie généralement que quelqu'un s'en est aperçu. Le même acteur qui la recrée quelques minutes plus tard laisse penser à une dissimulation.
  3. Qu'est-ce qui a été poussé entre-temps ? Filtrez les événements Git sur git.push pour ce dépôt pendant la fenêtre, et comparez avec l'historique des commits de la branche. Les événements Git excluent les pushs effectués via l'interface web ou l'API : consultez donc aussi la vue d'activité du dépôt.
  4. Quelque chose a-t-il été déployé ? Les exécutions de workflows sur la branche par défaut pendant la fenêtre (workflows.created_workflow_run avec head_branch: main) peuvent avoir livré la modification.

Les contournements, c'est autre chose

protected_branch.policy_override signifie qu'un administrateur du dépôt a poussé ou fusionné alors que la règle restait en place, en utilisant son privilège de contournement. C'est souvent légitime : correctifs urgents, ou responsables de release bénéficiant d'une exception documentée. C'est pour cette raison que la gravité est moyenne. Cela devient intéressant lorsque le compte administrateur présente aussi des constats liés aux jetons ou à la localisation, ou lorsque les contournements se concentrent sur des chemins sensibles comme .github/workflows/.

Environnements : les secrets derrière la barrière

Les règles de protection des environnements (reviewers obligatoires, délais d'attente, politiques de branches de déploiement) sont souvent la seule chose qui sépare un workflow lancé sur une branche quelconque des identifiants cloud de production stockés en secrets d'environnement. Supprimer une règle, ou supprimer puis recréer l'environnement, rend ces secrets accessibles à toute exécution de workflow qui référence l'environnement. Après un constat environment-protection-removed, listez les exécutions qui ont utilisé cet environnement pendant la fenêtre et considérez ses secrets comme exposés. L'article sur les secrets Actions explique comment en délimiter le périmètre.

Force-pushs et réécriture de l'historique

Les règles bloquent souvent les force-pushs. Une fois la protection désactivée, un attaquant peut réécrire l'historique pour insérer un commit malveillant, ou pour effacer la trace d'un tel commit. Le journal d'audit n'enregistre pas le contenu des commits, mais vous pouvez encore vous appuyer sur :

  • les événements git.push (si les événements Git sont disponibles) pour l'horodatage et l'identifiant utilisé ;
  • protected_branch.update_allow_force_pushes_enforcement_level, si l'attaquant a seulement assoupli ce paramètre ;
  • la vue d'activité du dépôt ainsi que vos miroirs ou caches de CI, qui peuvent encore contenir les commits d'avant la réécriture.

Mesurer le trou à la main

Si vous voulez vérifier la lecture de l'analyseur, ou si vous ne disposez que de l'export brut, la requête est courte. Extrayez chaque événement de protection et de ruleset du dépôt, dans l'ordre chronologique, avec l'acteur et l'adresse IP :

jq -r '.[] | select(.repo=="ORG/REPO")
  | select(.action|test("^(protected_branch|repository_ruleset|environment)\\."))
  | [(.["@timestamp"]/1000|todate), .action, .actor, (.actor_ip // "-"), (.name // "-")]
  | @tsv' audit-log.json | sort

Lisez le résultat par paires. Chaque destroy ou update qui affaiblit une règle devrait être suivi d'un create ou d'un update qui la rétablit. Le temps écoulé entre les deux constitue votre fenêtre d'exposition. Notez-la en UTC, car tout le reste du travail de périmètre (pushs, exécutions de workflows, déploiements) est filtré sur cette fenêtre. S'il n'existe pas de rétablissement correspondant, la fenêtre est toujours ouverte. Corrigez cela avant de poursuivre l'enquête. Vérifiez aussi si l'affaiblissement et le rétablissement proviennent du même compte. Quand une même personne désactive puis réactive la protection autour d'un unique push, vous devez parler à cette personne, quelle que soit la raison au final.

Checklist de remédiation

  • Recréez la protection de branche et les rulesets à partir d'une définition réputée saine (gardez les rulesets dans le code, ou exportez-les).
  • Rétablissez les règles de protection des environnements et renouvelez les secrets de l'environnement.
  • Passez en revue chaque commit, merge, force-push et déploiement effectué pendant le trou, puis annulez-les ou redéployez depuis un commit de confiance.
  • Retirez des rulesets les acteurs de contournement inattendus, ainsi que les administrateurs de dépôt inattendus.
  • Pour les contrôles les plus importants, privilégiez les rulesets d'organisation. Ils sont gérés au niveau de l'organisation, et non par les administrateurs de chaque dépôt.

À lire aussi

Articles liés

Prise de contrôle d'organisation GitHub dans le journal d'audit : nouveaux propriétaires, 2FA coupée, SSO SAML modifié, liste d'IP désactivée, streaming retiré.
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.
Ce que le journal d'audit GitHub n'enregistre pas : rétention de 180 et 7 jours, API et événements Git selon l'offre, IP masquées, ni contenus ni secrets lus.

Cet outil n'est ni affilié à GitHub, Inc. ou à Microsoft Corporation, ni approuvé ou sponsorisé par elles. GitHub et GitHub Actions sont des marques de GitHub, Inc. Les autres noms sont des marques de leurs propriétaires respectifs.