
Proof-of-Concept-Exploit für CVE-2026-44590, eine Command Injection im GitHub-Actions-Workflow von Sherlock, die RCE und die Exfiltration von GITHUB_TOKEN über pull_request_target ermöglicht.
Entdeckt und gemeldet von: Astaruf
Vollständiger Bericht: https://nstsec.com/en/posts/sherlock-rce-pull-request-target-cve-2026-44590/
Upstream-Advisory: sherlock-project/sherlock GHSA advisory
NVD-Eintrag: https://nvd.nist.gov/vuln/detail/CVE-2026-44590
CVE-Datensatz: https://www.cve.org/CVERecord?id=CVE-2026-44590
Dieses Repository enthält den Proof-of-Concept für CVE-2026-44590, eine Command Injection im GitHub-Actions-Workflow validate_modified_targets.yml von sherlock-project/sherlock. Jeder GitHub-Benutzer kann einen Pull Request eröffnen, der die Ausführung beliebiger Befehle im privilegierten CI-Kontext auslöst, das GITHUB_TOKEN des Workflows exfiltriert und den bösartigen PR automatisch genehmigt – alles ohne menschliche Interaktion.
Für den vollständigen technischen Bericht (Root-Cause-Analyse, Exploitation-Walkthrough, Auswirkungsanalyse und ein Kapitel darüber, was ein Angreifer in realen Szenarien tun könnte) siehe den Blogbeitrag:
nstsec.com/en/posts/sherlock-rce-pull-request-target-cve-2026-44590
Diese README konzentriert sich ausschließlich auf das PoC-Skript: was es tut, wie man es ausführt und was man erwarten kann.
poc.pypoc.py ist ein einzelnes, eigenständiges Python-Skript (nur stdlib), das die gesamte Angriffskette Ende-zu-Ende automatisiert:
sherlock-project/sherlock, falls nötigmaster-Zweig des Forks auf den Pre-Fix-Commit zurück, sodass der Fehler auch nach dem Upstream-Fix reproduziert werden kanninteractsh-client)GITHUB_TOKEN aus dem OAST-Callback (im --mode exfil) und dekodiert es im KlartextVULNERABILITY CONFIRMED oder FIX VERIFIEDinteractsh-client)Es gibt genau einen manuellen Schritt (Klicken auf das „I understand my workflows"-Banner von GitHub beim ersten Mal pro Fork), da keine öffentliche API existiert, um es zu schließen. Das Skript erkennt diesen Fall und pausiert mit einer klaren Eingabeaufforderung.
python3 poc.py --fork-owner <dein-github-benutzername>
Forkt das Repository (falls nötig), synchronisiert mit Upstream (gepatchter master), eröffnet einen bösartigen PR, führt die Angriffskette aus und meldet FIX VERIFIED, weil der gepatchte Workflow die Payload blockiert, bevor irgendein Shell-Befehl ausgeführt wird.
python3 poc.py --fork-owner <dein-github-benutzername> --vulnerable
Wie oben, setzt aber zuerst den master-Zweig des Forks auf den Pre-Fix-Commit (271608fb) zurück. Erwartetes Urteil: VULNERABILITY CONFIRMED.
python3 poc.py --fork-owner <dein-github-benutzername> --vulnerable --mode exfil
Die Exfil-Payload gibt git config --list an den OAST aus und schläft 180 Sekunden, um den Workflow (und damit das GITHUB_TOKEN) am Leben zu halten. Während der Workflow schläft, extrahiert das Skript das Token aus dem OAST-Log, dekodiert es und ruft sofort die GitHub-API auf, um den PR zu genehmigen. Der PR wird am Ende von github-actions[bot] genehmigt.
gh (GitHub CLI), authentifiziert:
gh auth login
gitinteractsh-client (optional, aber empfohlen). Wenn installiert, startet das Skript ihn automatisch und verifiziert den Callback im Skript:
go install github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest
Wenn du lieber deinen eigenen OAST-Endpunkt verwenden möchtest (Burp Collaborator, oast.fun über Web-UI, requestbin usw.), übergib ihn mit --oast-url und der Schritt der automatischen Verifizierung wird übersprungen.Das Skript benötigt keinen bereits vorhandenen Fork, es erstellt automatisch einen.
--mode harmless (Standard)Die Payload ist ein einzelner curl-POST mit einer statischen Bestätigungszeichenfolge. Es werden keine Geheimnisse gelesen, keine API-Aufrufe getätigt, der einzige Nebeneffekt ist der OAST-Callback. Verwende dies, um zu bestätigen, dass die Schwachstelle existiert, ohne irgendeine Anmeldeinformation preiszugeben.
--mode exfilDie Payload gibt git config --list (das das base64-kodierte GITHUB_TOKEN unter http.https://github.com/.extraheader enthält) an den OAST aus und schläft dann 180 Sekunden. Das Skript führt dann Folgendes aus:
x-access-token:ghs_XXXXXXXX....x-access-token: und verwendet das rohe Token, um POST /repos/<fork>/pulls/<n>/reviews mit der Standard-Genehmigungs-Payload aufzurufen ({"event":"APPROVE","body":"All checks passed. LGTM!"}).github-actions[bot] genehmigt, nicht unterscheidbar von legitimer CI-Automatisierung.Sobald die Genehmigung aufgezeichnet ist, überspringt das Skript den Rest des 180-Sekunden-Schlafs des Workflows, da die Angriffskette abgeschlossen ist und das Warten auf den Timeout des Runners nichts hinzufügt.
| Flag | Beschreibung |
|---|---|
--fork-owner <user> | Erforderlich. GitHub-Benutzername, dem der Fork gehört (oder gehören wird) |
--fork-name <name> | Name des Fork-Repositorys (Standard: sherlock) |
--oast-url <url> | OAST-Endpunkt, der den Callback empfängt. Wenn weggelassen, startet das Skript automatisch interactsh-client und führt das Urteil im Skript aus |
--mode harmless|exfil | Payload-Typ (Standard: harmless) |
--vulnerable | Setzt den master-Zweig des Forks vor der Ausführung zwangsweise auf den Pre-Fix-Commit (271608fb) zurück. Impliziert --no-sync |
--no-sync | Überspringt die Synchronisierung des Forks mit Upstream (nützlich beim Testen eines festgepinnten Commits) |
--base-branch <name> | PR-Zielzweig auf dem Fork (Standard: master) |
--keep-branch | Löscht den PoC-Zweig nach Abschluss nicht |
--no-poll | Überspringt das Polling des Workflow-Runs und beendet nach der PR-Erstellung |
Absichtlich eröffnet der PoC den PR von einem Zweig auf dem Fork zum master desselben Forks. Er zielt nicht direkt auf sherlock-project/sherlock ab. Dafür gibt es zwei Gründe.