Branch Protection deaktiviert? GitHub-Rulesets prüfen
Manipulierte Branch Protection oder Rulesets in GitHub untersuchen: protected_branch.destroy, Ruleset-Änderungen, Admin-Overrides, Umgebungen und neue Commits.
Kurz gesagt. Branch Protection und Rulesets verhindern, dass eine einzige gestohlene Zugangsberechtigung main umschreiben kann. Angreifer entfernen sie (protected_branch.destroy, repository_ruleset.destroy), schwächen sie ab (protected_branch.update_*, repository_ruleset.update) oder umgehen sie (protected_branch.policy_override). Mit Deployment-Umgebungen verfahren sie genauso (environment.remove_protection_rule). Das Audit-Log zeigt Ihnen, wer welche Regel wann geändert hat. Die Git-Historie und die Git-Events zeigen Ihnen, was in der Zwischenzeit eingespielt wurde. Stellen Sie die Regel wieder her und prüfen Sie dann jeden Push, jeden Force-Push und jedes Deployment in diesem Zeitfenster.
Code-Review, verpflichtende Statuschecks und signierte Commits sind Kontrollen für die Supply Chain. Sie wirken nur, solange sie durchgesetzt werden. Ein Angreifer mit Admin-Rechten oder dem Token eines Owners kann sie für die paar Minuten abschalten, die er braucht. Manche schalten sie danach wieder ein, um die Lücke zu verschleiern. Das Audit-Log behält beide Ereignisse.
Was manipuliert werden kann
| Kontrolle | Entfernt / gelöscht | Abgeschwächt | Umgangen |
|---|---|---|---|
| Klassische Branch Protection | protected_branch.destroy | protected_branch.update_* (Anzahl der Reviews, Statuschecks, Force-Pushes, Löschungen, Durchsetzung für Admins …) | protected_branch.policy_override |
| Repository-Rulesets | repository_ruleset.destroy | repository_ruleset.update | im Ruleset konfigurierte Bypass-Akteure |
| Deployment-Umgebungen | environment.delete | environment.remove_protection_rule, environment.update_protection_rule | — |
GitHubs Dokumentation zu geschützten Branches und zu Rulesets beschreibt jede Einstellung. Der Glossareintrag zu Repository-Rulesets erklärt, wie Rulesets und klassischer Branch-Schutz zusammenwirken.
Wie der Analyzer das erkennt
| Regel | Aktionen | Schweregrad |
|---|---|---|
branch-protection-removed | protected_branch.destroy, repository_ruleset.destroy | hoch |
branch-protection-override | protected_branch.policy_override | mittel |
environment-protection-removed | environment.remove_protection_rule, environment.delete | hoch |
Befunde werden nach Repository gruppiert. Die abschwächenden Aktionen (protected_branch.update_*, repository_ruleset.update) sind keine Regeln, weil sie zur normalen Verwaltung eines Repositories gehören. Sie erscheinen in der Zeitleiste, weil dort jede Aktion mit protected_branch. und repository_ruleset. erhalten bleibt. Schlägt ein Befund an, öffnen Sie die Zeitleiste für dieses Repository und lesen Sie die Änderungen rund um den Befund.
Eine Entfernung lesen
Ein Ereignis protected_branch.destroy enthält das Repository, das Branch-Muster (name) und die Einstellungen der Regel zum Zeitpunkt der Löschung, etwa required_approving_review_count und admin_enforced. Daran sehen Sie, was verloren ging. Beantworten Sie dann vier Fragen:
- Wer und womit? Akteur, IP-Adresse, User-Agent und
hashed_token. Eine Browser-Sitzung aus dem Büro des Owners ist etwas anderes alscurl/8.5.0mit einem Token von einem VPS. - Wurde die Regel wiederhergestellt, und von wem? Ein
protected_branch.createfür denselben Branch Stunden später, durch einen anderen Akteur, bedeutet meist, dass es jemandem aufgefallen ist. Legt derselbe Akteur die Regel Minuten später neu an, deutet das auf Verschleierung hin. - Was wurde dazwischen gepusht? Filtern Sie die Git-Events nach
git.pushauf dieses Repository im Zeitfenster und vergleichen Sie mit der Commit-Historie des Branches. Git-Events enthalten keine Pushes über die Weboberfläche oder die API, prüfen Sie also zusätzlich die Aktivitätsansicht des Repositories. - Wurde etwas deployt? Workflow-Läufe auf dem Standard-Branch im Zeitfenster (
workflows.created_workflow_runmithead_branch: main) könnten die Änderung ausgeliefert haben.
Overrides sind etwas anderes
protected_branch.policy_override bedeutet, dass ein Repository-Administrator gepusht oder gemergt hat, während die Regel in Kraft blieb, indem er sein Bypass-Recht genutzt hat. Das ist oft legitim: Hotfixes oder Release-Manager mit dokumentierter Ausnahme. Deshalb ist es mit mittel bewertet. Interessant wird es, wenn das Admin-Konto zugleich Befunde zu Tokens oder Standorten aufweist oder wenn sich Overrides auf sensiblen Pfaden wie .github/workflows/ häufen.
Umgebungen: die Secrets hinter der Schranke
Schutzregeln für Umgebungen (erforderliche Reviewer, Wartezeiten, Richtlinien für Deployment-Branches) sind oft das Einzige, was zwischen einem Workflow auf einem beliebigen Branch und den Produktions-Zugangsdaten für die Cloud steht, die als Umgebungs-Secrets gespeichert sind. Wird eine Regel entfernt oder die Umgebung gelöscht und neu angelegt, sind diese Secrets für jeden Workflow-Lauf erreichbar, der auf die Umgebung verweist. Nach einem Befund environment-protection-removed listen Sie die Läufe auf, die diese Umgebung im Zeitfenster verwendet haben, und behandeln ihre Secrets als offengelegt. Der Artikel zu Actions-Secrets beschreibt, wie Sie den Umfang bestimmen.
Force-Pushes und umgeschriebene Historie
Regeln blockieren oft Force-Pushes. Ist der Schutz aus, kann ein Angreifer die Historie umschreiben, um einen bösartigen Commit einzuschleusen oder die Spuren eines solchen zu beseitigen. Das Audit-Log zeichnet keine Commit-Inhalte auf, aber Sie können trotzdem mit Folgendem arbeiten:
git.push-Ereignisse (sofern Git-Events vorliegen) für den Zeitpunkt und die verwendete Zugangsberechtigung.protected_branch.update_allow_force_pushes_enforcement_level, falls der Angreifer nur diese Einstellung gelockert hat.- Die Aktivitätsansicht des Repositories sowie Ihre Mirrors oder CI-Caches. Sie enthalten womöglich noch die Commits vor dem Umschreiben.
Die Lücke von Hand vermessen
Wenn Sie die Auswertung des Analyzers überprüfen wollen oder nur den Rohexport haben, ist die Abfrage kurz. Extrahieren Sie jedes Schutz- und Ruleset-Ereignis des Repositories in zeitlicher Reihenfolge, mit Akteur und IP-Adresse:
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
Lesen Sie die Ausgabe paarweise. Auf jedes destroy oder update, das eine Regel schwächt, sollte ein create oder update folgen, das sie wiederherstellt. Die Zeit dazwischen ist Ihr Expositionsfenster. Notieren Sie es in UTC, denn alles Weitere bei der Bestimmung des Umfangs (Pushes, Workflow-Läufe, Deployments) wird nach diesem Fenster gefiltert. Fehlt die passende Wiederherstellung, ist das Fenster noch offen. Beheben Sie das, bevor Sie weiter ermitteln. Prüfen Sie außerdem, ob Abschwächung und Wiederherstellung vom selben Konto kamen. Wenn eine Person den Schutz rund um einen einzigen Push ab- und wieder einschaltet, müssen Sie mit dieser Person sprechen, ganz gleich, welcher Grund sich am Ende herausstellt.
Checkliste zur Bereinigung
- Legen Sie Branch Protection und Rulesets aus einer bekannt guten Definition neu an (Rulesets als Code pflegen oder exportieren).
- Stellen Sie die Schutzregeln der Umgebungen wieder her und rotieren Sie die Secrets der Umgebung.
- Prüfen Sie jeden Commit, Merge, Force-Push und jedes Deployment aus der Zeit der Lücke, und setzen Sie zurück oder deployen Sie neu von einem vertrauenswürdigen Commit.
- Entfernen Sie unerwartete Bypass-Akteure aus Rulesets und unerwartete Repository-Admins.
- Bevorzugen Sie für die wichtigsten Kontrollen Rulesets auf Organisationsebene. Sie werden auf Ebene der Organisation verwaltet, nicht von den Admins des jeweiligen Repositories.
Verwandte Artikel
- Übernahme einer GitHub-Organisation: Owner, 2FA, SSO und IP-Zulassungsliste
- GitHub-Actions-Secrets geleakt: Exfiltration untersuchen
- Das fiktive Szenario northwind-labs: Branch Protection um 03:05 UTC entfernt, um 07:48 wiederhergestellt
- MITRE ATT&CK T1562 Impair Defenses