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 — Root-cause analysis, vulnerable Docker lab, and PoC scripts for CVE-2026-85706, an unauthenticated arbitrary file read in GitLab via parser differential. | Kitploit
Tools/GitHubGitHub/0xenesbayram/cve-2026-85706
Vulnerability AnalysisExploitationWeb Application ExploitationWeb SecurityPenetration TestingLearning & EducationLabs & Practice
GitHub0xenesbayram/cve-2026-85706

cve-2026-85706

Root-cause analysis, vulnerable Docker lab, and PoC scripts for CVE-2026-85706, an unauthenticated arbitrary file read in GitLab via parser differential.

View Repository
9h 26m 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 — GitLab Unauthenticated Arbitrary File Read

CVSS 10.0 · Unauthenticated · Actively exploited (CISA KEV)

A parser differential between GitLab Workhorse (Go reverse proxy) and Puma/Grape (Ruby) lets an unauthenticated attacker bypass Workhorse's accelerated-upload handoff and reach three upload endpoints with an attacker-controlled file.path, yielding arbitrary file read on the GitLab host.

Encoding one character of files as %66iles makes Workhorse's upload route miss (it matches on the encoded path) while Rails decodes it back and hits the real handler (it routes on the decoded path). The handler trusts the raw file.path param that Workhorse was supposed to overwrite — so file.path=/etc/passwd is read from disk with no credentials.

root@kitploit:~
POST /api/v4/projects/1/repository/%66iles/x?file=&file.path=/etc/passwd&file.size=1
                                   ^^^^^^  Workhorse misses -> raw file.path survives to Rails

Affected / fixed

Version
AffectedCE/EE 18.7 → 19.1.8, 19.2 → 19.2.6, 19.3 → 19.3.2
Fixed19.1.8 / 19.2.6 / 19.3.2 (2026-09-10)

What's in this repo

PathContents
docs/ANALYSIS.mdFull root-cause analysis — parser differential, the two Workhorse JWTs, the raw-file.path trust bug, the patch diff, and the reflected-error exfiltration channel. Analysis done against real source (v19.3.1-ee vs v19.3.2-ee).
lab/Docker-based vulnerable lab (gitlab-ce:19.3.1-ce.0) + bring-up instructions.
poc/detect.sh (non-destructive existence oracle) and exploit.sh (reflected-error file read). Single-target, authorization-gated.

Background — GitLab's request pipeline

Not familiar with GitLab internals? Here's what each layer does — in the order a request passes through them. A deeper glossary with key concepts is in docs/ANALYSIS.md §0.

root@kitploit:~
  Internet
     │
     ▼
 ┌────────┐     ┌───────────┐     ┌────────┐     ┌──────────────────────┐
 │ NGINX  │────▶│ Workhorse │────▶│  Puma  │────▶│  Grape / Rails app  │
 │(proxy) │     │   (Go)    │     │ (Ruby) │     │      (Ruby)         │
 └────────┘     └───────────┘     └────────┘     └──────────────────────┘
ComponentWhat it is
NGINXOutermost reverse proxy. Terminates TLS, serves static files, forwards everything else inward. Not directly involved in this vulnerability.
WorkhorseA Go reverse proxy specific to GitLab. Its main job is offloading work Ruby is slow at — especially streaming large uploads. For upload endpoints, Workhorse buffers the body to a temp file, signs a JWT, and rewrites file.path so Ruby never sees raw upload bytes. It also stamps a per-request Gitlab-Workhorse-Api-Request JWT on every request it forwards (proving "this came through the proxy," not "this user is authenticated").
PumaThe Ruby application server that runs the Rails app. Receives requests from Workhorse, runs middleware (Rack), and dispatches to the router.
RackThe Ruby web-server interface layer. Rack middleware handles query-string parsing, session management, and — critically here — verifying Workhorse's upload JWT and building UploadedFile objects. Rack::Utils.parse_nested_query is the function whose error messages leak file content in this exploit.
GrapeA REST API framework used by GitLab for all /api/v4/* endpoints. Provides route definitions and before-filters like require_gitlab_workhorse! (proxy check) and authenticate! (user identity check). Runs inside Rails, on Puma, behind Workhorse — so it sees the decoded URL path.
RailsThe overall web framework (Ruby on Rails). GitLab is a Rails monolith — models, services, and middleware all run here inside Puma.

The vulnerability lives in the gap between Workhorse (which matches routes on the encoded path) and Grape/Puma (which route on the decoded path). See below.

TL;DR of the mechanism

  1. Parser differential. Workhorse matches routes on r.URL.EscapedPath() (encoded); Puma/Grape route on the decoded path. %66iles ≠ regex files for Workhorse, but decodes to files for Rails.
  2. Handoff skipped. Because the upload route missed, Workhorse never buffers the body to a temp file, never rewrites file.path to a signed temp path, and never sets the Gitlab-Workhorse-Multipart-Fields header — but it still proxies the request (with a valid Gitlab-Workhorse-Api-Request JWT).
  3. Missing auth + raw-param trust. The endpoint had no authenticate!, and the handler read params['file.path'] directly (the attacker's string) instead of the Workhorse-verified, path-confined params[:file] UploadedFile.
  4. Exfil via error messages. Content leaks through Rack::Utils.parse_nested_query error strings ("invalid %-encoding (<file bytes>)") — the response reflects file bytes up to the first invalid %.

The patch adds authenticate!, switches to the verified UploadedFile, and stops reflecting e.message. See docs/ANALYSIS.md §5.

Quick start

root@kitploit:~
# 1. Stand up the vulnerable lab (see lab/README.md for details)
cd lab && docker compose up -d      # wait ~5 min for GitLab to become healthy

# 2. Non-destructive detection
../poc/detect.sh http://localhost:8929

# 3. File-read PoC against a file you're authorized to read on your own lab
../poc/exploit.sh http://localhost:8929 /var/opt/gitlab/gitlab-rails/etc/gitlab.yml

⚠️ Responsible use

This is published for defensive and educational purposes: understanding, detecting, and patching a disclosed, patched CVE that is on the CISA KEV list. The scripts here are single-target and require you to pass the target explicitly.

  • Only run these against systems you own or are explicitly authorized to test.
  • The lab is designed to run on localhost. Do not point it at third-party hosts.
  • Unauthorized access to computer systems is illegal in most jurisdictions.

If you run GitLab, upgrade to a fixed version — that is the only real remediation.

References

  • watchTowr — Rapid Reaction: GitLab Path Traversal (CVE-2026-85706)
  • Nuclei Templates — PR #17231
  • Primary source diff: gitlab-org/gitlab @ v19.3.1-ee vs v19.3.2-ee

License

MIT — analysis and PoC code only. GitLab is a trademark of GitLab Inc.; this repository is not affiliated with or endorsed by GitLab Inc.

Download Tool