
CVE-2026-85706 के लिए रूट-कारण विश्लेषण, संवेदनशील Docker लैब, और PoC स्क्रिप्ट्स, जो GitLab में parser differential के माध्यम से एक unauthenticated arbitrary file read है।
CVSS 10.0 · अनधिकृत · सक्रिय रूप से शोषित (CISA KEV)
GitLab Workhorse (Go रिवर्स प्रॉक्सी) और Puma/Grape (Ruby) के बीच एक पार्सर अंतर एक अनधिकृत हमलावर को Workhorse के त्वरित-अपलोड हैंडऑफ़ को बायपास करने और हमलावर-नियंत्रित file.path के साथ तीन अपलोड एंडपॉइंट तक पहुँचने की अनुमति देता है, जिससे GitLab होस्ट पर मनमाना फ़ाइल पठन संभव हो जाता है।
files के एक अक्षर को %66iles के रूप में एन्कोड करने से Workhorse का अपलोड रूट चूक जाता है (यह एन्कोडेड पथ पर मेल खाता है) जबकि Rails इसे वापस डिकोड करके वास्तविक हैंडलर तक पहुँच जाता है (यह डिकोडेड पथ पर रूट करता है)। हैंडलर उस कच्चे file.path पैरामीटर पर भरोसा करता है जिसे Workhorse को ओवरराइट करना था — इसलिए file.path=/etc/passwd बिना किसी क्रेडेंशियल के डिस्क से पढ़ा जाता है।
POST /api/v4/projects/1/repository/%66iles/x?file=&file.path=/etc/passwd&file.size=1
^^^^^^ Workhorse misses -> raw file.path survives to Rails
| संस्करण | |
|---|---|
| प्रभावित | CE/EE 18.7 → 19.1.8, 19.2 → 19.2.6, 19.3 → 19.3.2 |
| ठीक किया गया | 19.1.8 / 19.2.6 / 19.3.2 (2026-09-10) |
| पथ | सामग्री |
|---|---|
docs/ANALYSIS.md | पूर्ण मूल-कारण विश्लेषण — पार्सर अंतर, दो Workhorse JWT, कच्चे-file.path ट्रस्ट बग, पैच डिफ, और परावर्तित-त्रुटि निष्कासन चैनल। विश्लेषण वास्तविक स्रोत (v19.3.1-ee बनाम v19.3.2-ee) के विरुद्ध किया गया। |
lab/ | Docker-आधारित भेद्य लैब (gitlab-ce:19.3.1-ce.0) + सेटअप निर्देश। |
poc/ | detect.sh (गैर-विनाशकारी अस्तित्व ओरेकल) और exploit.sh (परावर्तित-त्रुटि फ़ाइल पठन)। एकल-लक्ष्य, प्राधिकरण-नियंत्रित। |
GitLab की आंतरिक संरचना से परिचित नहीं हैं? यहाँ बताया गया है कि प्रत्येक परत क्या करती है — उस क्रम में जिसमें एक अनुरोध उनके माध्यम से गुजरता है। मुख्य अवधारणाओं के साथ एक गहरी शब्दावली docs/ANALYSIS.md §0 में है।
Internet
│
▼
┌────────┐ ┌───────────┐ ┌────────┐ ┌──────────────────────┐
│ NGINX │────▶│ Workhorse │────▶│ Puma │────▶│ Grape / Rails app │
│(proxy) │ │ (Go) │ │ (Ruby) │ │ (Ruby) │
└────────┘ └───────────┘ └────────┘ └──────────────────────┘
| घटक | यह क्या है |
|---|---|
| NGINX | सबसे बाहरी रिवर्स प्रॉक्सी। TLS समाप्त करता है, स्थिर फ़ाइलें परोसता है, बाकी सब कुछ अंदर की ओर अग्रेषित करता है। इस भेद्यता में सीधे तौर पर शामिल नहीं है। |
| Workhorse | GitLab के लिए विशिष्ट एक Go रिवर्स प्रॉक्सी। इसका मुख्य काम उस काम को ऑफ़लोड करना है जिसमें Ruby धीमा है — विशेष रूप से बड़े अपलोड को स्ट्रीम करना। अपलोड एंडपॉइंट के लिए, Workhorse बॉडी को एक अस्थायी फ़ाइल में बफ़र करता है, एक JWT पर हस्ताक्षर करता है, और file.path को फिर से लिखता है ताकि Ruby कभी कच्चे अपलोड बाइट्स न देखे। यह प्रत्येक अनुरोध पर जिसे वह अग्रेषित करता है, एक प्रति-अनुरोध Gitlab-Workhorse-Api-Request JWT भी लगाता है (यह साबित करते हुए कि "यह प्रॉक्सी के माध्यम से आया है," न कि "यह उपयोगकर्ता प्रमाणित है")। |
| Puma | Ruby एप्लिकेशन सर्वर जो Rails ऐप चलाता है। Workhorse से अनुरोध प्राप्त करता है, मिडलवेयर (Rack) चलाता है, और राउटर को डिस्पैच करता है। |
| Rack | Ruby वेब-सर्वर इंटरफ़ेस परत। Rack मिडलवेयर क्वेरी-स्ट्रिंग पार्सिंग, सत्र प्रबंधन, और — यहाँ महत्वपूर्ण रूप से — Workhorse के अपलोड JWT को सत्यापित करने और UploadedFile ऑब्जेक्ट बनाने को संभालता है। Rack::Utils.parse_nested_query वह फ़ंक्शन है जिसके त्रुटि संदेश इस एक्सप्लॉइट में फ़ाइल सामग्री लीक करते हैं। |
| Grape | GitLab द्वारा सभी /api/v4/* एंडपॉइंट के लिए उपयोग किया जाने वाला एक REST API फ्रेमवर्क। रूट परिभाषाएँ और require_gitlab_workhorse! (प्रॉक्सी जाँच) और authenticate! (उपयोगकर्ता पहचान जाँच) जैसे before-फ़िल्टर प्रदान करता है। Rails के अंदर, Puma पर, Workhorse के पीछे चलता है — इसलिए यह डिकोडेड URL पथ देखता है। |
| Rails | समग्र वेब फ्रेमवर्क (Ruby on Rails)। GitLab एक Rails मोनोलिथ है — मॉडल, सेवाएँ, और मिडलवेयर सभी यहाँ Puma के अंदर चलते हैं। |
भेद्यता Workhorse (जो एन्कोडेड पथ पर रूट मिलाता है) और Grape/Puma (जो डिकोडेड पथ पर रूट करते हैं) के बीच के अंतर में निहित है। नीचे देखें।
r.URL.EscapedPath() (एन्कोडेड) पर रूट मिलाता है; Puma/Grape डिकोडेड पथ पर रूट करते हैं। Workhorse के लिए %66iles ≠ regex files, लेकिन Rails के लिए यह files में डिकोड हो जाता है।file.path को हस्ताक्षरित अस्थायी पथ में फिर से नहीं लिखता, और कभी Gitlab-Workhorse-Multipart-Fields हेडर सेट नहीं करता — लेकिन यह फिर भी अनुरोध को प्रॉक्सी करता है (एक वैध Gitlab-Workhorse-Api-Request JWT के साथ)।authenticate! नहीं था, और हैंडलर ने Workhorse-सत्यापित, पथ-सीमित params[:file] UploadedFile के बजाय सीधे params['file.path'] (हमलावर की स्ट्रिंग) पढ़ा।Rack::Utils.parse_nested_query त्रुटि स्ट्रिंग्स ("invalid %-encoding (<file bytes>)") के माध्यम से लीक होती है — प्रतिक्रिया पहले अमान्य % तक फ़ाइल बाइट्स को परावर्तित करती है।पैच authenticate! जोड़ता है, सत्यापित UploadedFile पर स्विच करता है, और e.message को परावर्तित करना बंद कर देता है। docs/ANALYSIS.md §5 देखें।
# 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
यह रक्षात्मक और शैक्षिक उद्देश्यों के लिए प्रकाशित किया गया है: एक प्रकट किए गए, पैच किए गए CVE को समझना, पता लगाना और पैच करना जो CISA KEV सूची में है। यहाँ दी गई स्क्रिप्ट एकल-लक्ष्य हैं और आपको लक्ष्य को स्पष्ट रूप से पास करने की आवश्यकता होती है।
localhost पर चलने के लिए डिज़ाइन की गई है। इसे तृतीय-पक्ष होस्ट पर न लक्षित करें।यदि आप GitLab चलाते हैं, तो एक निश्चित संस्करण में अपग्रेड करें — यही एकमात्र वास्तविक उपचार है।
gitlab-org/gitlab @ v19.3.1-ee बनाम v19.3.2-eeMIT — केवल विश्लेषण और PoC कोड। GitLab, GitLab Inc. का ट्रेडमार्क है; यह रेपो GitLab Inc. से संबद्ध या उसके द्वारा समर्थित नहीं है।