
Prueba de concepto del exploit para CVE-2026-44590, una inyección de comandos en el flujo de trabajo de GitHub Actions de Sherlock que permite RCE y la exfiltración de GITHUB_TOKEN a través de pull_request_target.
Descubierto y reportado por: Astaruf
Informe completo: https://nstsec.com/en/posts/sherlock-rce-pull-request-target-cve-2026-44590/
Aviso upstream: Aviso GHSA de sherlock-project/sherlock
Entrada NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-44590
Registro CVE: https://www.cve.org/CVERecord?id=CVE-2026-44590
Este repositorio contiene la prueba de concepto para CVE-2026-44590, una inyección de comandos en el flujo de trabajo validate_modified_targets.yml de GitHub Actions de sherlock-project/sherlock. Cualquier usuario de GitHub puede abrir una pull request que desencadena la ejecución arbitraria de comandos en el contexto privilegiado del CI, exfiltrar el GITHUB_TOKEN del flujo de trabajo y auto-aprobar la PR maliciosa, todo sin ninguna interacción humana.
Para el informe técnico completo (análisis de causa raíz, recorrido de explotación, discusión de impacto y un capítulo sobre lo que un atacante podría hacer en escenarios del mundo real), consulta la publicación del blog:
nstsec.com/en/posts/sherlock-rce-pull-request-target-cve-2026-44590
Este README se centra exclusivamente en el script PoC: qué hace, cómo ejecutarlo y qué esperar.
poc.pypoc.py es un script de Python autónomo (solo stdlib) que automatiza toda la cadena de ataque de principio a fin:
sherlock-project/sherlock si es necesariomaster del fork al commit anterior al fix para que el bug pueda reproducirse incluso después del fix upstreaminteractsh-client)GITHUB_TOKEN de la devolución de llamada OAST (en --mode exfil) y lo decodifica en texto claroVULNERABILITY CONFIRMED o FIX VERIFIEDinteractsh-client)Hay exactamente un paso manual requerido (hacer clic en el banner "I understand my workflows" de GitHub la primera vez por fork), porque no existe una API pública para descartarlo. El script detecta este caso y se pausa con un aviso claro.
python3 poc.py --fork-owner <tu-nombre-de-usuario-de-github>
Bifurca el repositorio (si es necesario), sincroniza con upstream (master parcheado), abre una PR maliciosa, ejecuta la cadena de ataque y reporta FIX VERIFIED porque el flujo de trabajo parcheado bloquea el payload antes de que se ejecute cualquier comando de shell.
python3 poc.py --fork-owner <tu-nombre-de-usuario-de-github> --vulnerable
Igual que arriba, pero primero revierte el master del fork al commit anterior al fix (271608fb). Veredicto esperado: VULNERABILITY CONFIRMED.
python3 poc.py --fork-owner <tu-nombre-de-usuario-de-github> --vulnerable --mode exfil
El payload de exfiltración vuelca git config --list al OAST y duerme durante 180 segundos para mantener vivo el flujo de trabajo (y por tanto el GITHUB_TOKEN). Mientras el flujo de trabajo duerme, el script extrae el token del registro OAST, lo decodifica e inmediatamente llama a la API de GitHub para aprobar la PR. La PR termina aprobada por github-actions[bot].
gh (GitHub CLI), autenticado:
gh auth login
gitinteractsh-client (opcional pero recomendado). Cuando está instalado, el script lo inicia automáticamente y verifica la devolución de llamada dentro del script:
go install github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest
Si prefieres usar tu propio endpoint OAST (Burp Collaborator, oast.fun mediante interfaz web, requestbin, etc.), pásalo con --oast-url y se omitirá el paso de verificación automática.El script no requiere que exista un fork de antemano, lo crea automáticamente.
--mode harmless (predeterminado)El payload es un único curl POST con una cadena de confirmación estática. No se leen secretos, no se realizan llamadas a la API, el único efecto secundario es la devolución de llamada OAST. Úsalo para confirmar que la vulnerabilidad existe sin exponer ninguna credencial.
--mode exfilEl payload vuelca git config --list (que contiene el GITHUB_TOKEN codificado en base64 bajo http.https://github.com/.extraheader) al OAST, y luego duerme durante 180 segundos. El script entonces:
x-access-token:ghs_XXXXXXXX....x-access-token: y usa el token crudo para llamar a POST /repos/<fork>/pulls/<n>/reviews con el payload de aprobación estándar ({"event":"APPROVE","body":"All checks passed. LGTM!"}).github-actions[bot], indistinguible de la automatización legítima del CI.Una vez registrada la aprobación, el script omite el resto de los 180 segundos de sueño del flujo de trabajo, ya que la cadena de ataque está completa y esperar a que el runner agote el tiempo no añade nada.
| Flag | Descripción |
|---|---|
--fork-owner <usuario> | Requerido. Nombre de usuario de GitHub que posee (o poseerá) el fork |
--fork-name <nombre> | Nombre del repositorio del fork (predeterminado: sherlock) |
--oast-url <url> | Endpoint OAST que recibe la devolución de llamada. Si se omite, el script inicia automáticamente interactsh-client y ejecuta el veredicto dentro del script |
--mode harmless|exfil | Tipo de payload (predeterminado: harmless) |
--vulnerable | Forzar el restablecimiento del master del fork al commit anterior al fix (271608fb) antes de ejecutar. Implica --no-sync |
--no-sync | Omitir la sincronización del fork con upstream (útil al probar un commit fijado) |
--base-branch <nombre> | Rama objetivo de la PR en el fork (predeterminado: master) |
--keep-branch | No eliminar la rama PoC después de completarse |
--no-poll | Omitir la consulta de ejecuciones del flujo de trabajo y salir después de la creación de la PR |
Por diseño, el PoC abre la PR desde una rama del fork hacia el master del mismo fork. No apunta directamente a sherlock-project/sherlock. Hay dos razones para esto.