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-85706 — Python PoC exploit for CVE-2026-85706, an unauthenticated arbitrary file read in GitLab CE/EE via Workhorse path-encoding bypass, with writeup and bypass variants. | Kitploit
Tools/GitHubGitHub/guneykabel/cve-2026-85706
Vulnerability AnalysisExploitationWeb Application ExploitationInformation GatheringSecurity VirtualizationWeb SecurityPenetration Testing
GitHubguneykabel/cve-2026-85706

cve-2026-85706

Python PoC exploit for CVE-2026-85706, an unauthenticated arbitrary file read in GitLab CE/EE via Workhorse path-encoding bypass, with writeup and bypass variants.

View Repository
412h 9m 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

cve-2026-85706

Unauthenticated arbitrary local file read in GitLab CE/EE. Affects 18.7–19.1.7, 19.2.0–19.2.5, 19.3.0–19.3.1. Fixed in 19.1.8 / 19.2.6 / 19.3.2 (2026-09-10). CVSS 10.0, reportedly exploited in the wild. Original report by s3ntago and this repo is just my writeup + PoC.

disclaimer

This PoC is published for educational and defensive research purposes only, to help admins and researchers understand and test for the vulnerability. Run it exclusively against systems you own or have explicit written authorization to test. If your instance falls in the affected range, stop reading and patch to 19.1.8 / 19.2.6 / 19.3.2 first.

how it works

Three repository endpoints (POST :id/repository/commits, POST/PUT :id/repository/files/:file_path) sit behind Workhorse's requestBodyUploader. The Rails handler reads the on-disk path straight from the raw file.path request field and File.opens it before any authentication. does not serve as an auth because Workhorse's signing round-tripper attaches a valid JWT to every request it proxies, so anything that falls through to the generic API proxy passes that check.

require_gitlab_workhorse!
Gitlab-Workhorse-Api-Request

The only reason this isn't instant LFI for everyone is that Workhorse is supposed to rewrite the request first. But its route regex matches against the escaped path (EscapedPath() plus a path.Clean clone that never decodes %XX), while Puma decodes %XX before Grape routing. So percent-encode any character of a static segment (%63ommits, %72epository, %66iles), add a trailing slash, or tack on .json Workhorse's regex misses, Rails still routes to the vulnerable handler. That encoding mismatch is where lies the bypass. (//, /./, %2F, ; variants like this don't work cause path.Clean normalizes the first two and Puma refuses %2F.)

Then just send forged unsigned upload metadata as query params:

root@kitploit:~
POST /api/v4/projects/1/repository/%63ommits?file=&file.path=<ABSOLUTE_PATH>&file.size=1&Content-Type=application/x-www-form-urlencoded

file= blank satisfies the requires :file, WorkhorseFile validation (blank coerces to nil). The read fires pre-auth. Getting the bytes back is the fun part: on the urlencoded branch the helper runs Rack::Utils.parse_nested_query(File.read(path)) and interpolates parser errors into the 400 body. Any % not followed by two hex digits raises InvalidParameterError: invalid %-encoding (<raw file bytes>) file contents comes back inside the error message. The JSON branch (Oj) leaks nothing, which is why the urlencoded content type matters here.

The fix (master 0d9ce3e7, backports 1fe30154 / b43c8b26 / 0ff7b6b2) adds authenticate! to all three endpoints plus the /authorize pre-step, trusts only the middleware-produced UploadedFile for path/size, and stops echoing parser errors. Bug was introduced in Dec 2025, which is why the affected range starts at 18.7.

usage

root@kitploit:~
./exploit.py --url http://localhost:8080 --file /etc/hostname
./exploit.py --url http://localhost:8080 --file /opt/gitlab/embedded/service/gitlab-rails/config/gitlab.yml

The script cycles through all verified bypass forms (encoded segments, trailing slash, .json; commits + files endpoints, POST and PUT) and classifies each response so you can tell where in the chain a probe died. Authorized targets only, obviously.

flaws / limitations / pre-conditions

  • The echo only fires if the file contains a % not followed by two hex chars.
  • The leak regex is greedy ((.*) up to the last ) in the body). Fine against the stock JSON error, will over-capture if something in front wraps the response in HTML. Proper fix is parsing the JSON message field.
  • Commits API needs an anonymously readable project id (the before block 404s otherwise).

references

  • fix commit (master): https://gitlab.com/gitlab-org/gitlab/-/commit/0d9ce3e758a85f0690be751e213625f7902c0361
  • patch release notes: https://docs.gitlab.com/releases/patches/patch-release-gitlab-19-3-2-released/
  • writeup: https://securityonline.info/gitlab-vulnerabilities-cve-2026-85706-cvss-10/
Download Tool