
مختبر قابل لإعادة الإنتاج لـ CVE-2026-10053 (اجتياز المسار في سجل حزم npm في GitLab -> كتابة ملف عشوائي بصلاحيات git). الإصدار الضعيف 19.2.1 مقارنةً بالإصدار المصحح 19.2.2، أوراكل حتمي.
مختبر مكتفٍ ذاتيًا يُثبت ثغرة اجتياز المسار في سجل حزم npm التابع لـ GitLab (CVE-2026-10053) ويُظهر إصلاحها، باستخدام مُحكِّم (oracle) حتمي على القرص — وليس حالة HTTP.
ما هو مُثبَت وما ليس مُثبَتًا. يُثبت هذا المختبر الكتابة التعسفية للملفات بعد المصادقة بصفة مستخدم نظام التشغيل
gitعلى GitLab الضعيف، وأن الإصدار المُصحَّح يمنعها. وهو ليس إثبات مفهوم (PoC) مستقلًا لتنفيذ التعليمات البرمجية عن بُعد — انظر النطاق. لا تستشهد به كـ RCE.
| الثغرة | CWE-22 اجتياز المسار في سجل حزم npm (TOCTOU: الفحص نُفِّذ في before :cache وليس في before :store) |
| CVSS | 8.5 عالية — AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H |
| المتأثر | GitLab CE/EE 18.8 → <19.0.6، 19.1 → <19.1.4، 19.2 → <19.2.2 |
| المُصحَّح | 19.0.6 / 19.1.4 / 19.2.2 — الالتزام 435cf863 "إعادة التحقق من اجتياز مسار الرفع قبل التخزين" |
python3 وgit وcurl على المضيف../run.sh # up + provision + exploit + verify BOTH; exits 0 iff vuln writes AND patched blocks
النتيجة المتوقعة:
================ CVE-2026-10053 verification ================
INSTANCE EXPECT RESULT STATUS
gitlab-vuln (19.2.1) written written PASS
gitlab-patched (19.2.2) blocked blocked PASS
------------------------------------------------------------
PROVEN: authenticated arbitrary file write as git on 19.2.1.
NOT a standalone RCE — see README.md 'Scope' and ./run.sh rce-gate.
============================================================
أوامر فرعية أخرى:
./run.sh up # just start + wait until healthy
./run.sh rce-gate # honest exec-bit-gate demo (below)
./run.sh down # docker compose down -v
يستجيب كل من الخادمين الضعيف والمُصحَّح للنشر الخبيث بـ HTTP 200 {"status":"processing"} — إذ يُكتب الملف لاحقًا، في عامل (worker) الإنهاء (finalize). لذلك فإن حالة HTTP ليست مُحكِّمًا. بدلًا من ذلك، يستقصي exploit/poc.py --verify-docker <container> داخل الحاوية عن الملف المتحكَّم فيه في المسار المُجتاز ويقارن SHA-1 الخاص به:
/var/tmp/CVE_2026_10053_PROOF-1.0.0.tgz (المالك git:git، بايتاتنا) → exit 0.Gitlab::PathTraversal::PathTraversalAttackError: Invalid path → exit 2.الاستغلال نفسه ليس سوى طلب PUT …/packages/npm/:pkg الموثَّق؛ إذ يكمن الاجتياز بالكامل في حقل name داخل جسم JSON (file_name = "#{name}-#{version}.tgz"، لا يُفحص فيه سوى كونه غير فارغ). استدعاءات docker exec هي تحقُّق وإعداد، وليست جزءًا أبدًا من قدرة المهاجم.
git. تُحفظ الملفات بصلاحيات 0644.-<semver>.tgz (تُرفض NUL بواسطة النواة، ولا يقصّ سطر جديد (newline) الاسم)، لذا لا يمكن استبدال أي هدف محدد الاسم (secrets.yml، authorized_keys، ملف .rb، ثنائيات gitaly). المنفِّذ الوحيد المستقل عن اسم الملف، custom_hooks/<hook>.d/، يشغّل أي اسم ملف لكن فقط إذا كان قابلًا للتنفيذ — وهذه الكتابة بصلاحيات 0644. استبدال ملف موجود مسبقًا بصلاحيات 0755 لا يساعد: فالتخزين يستبدل inode معيدًا ضبطه إلى 0644. لذا يتطلب RCE أولية تنفيذ منفصلة (executable-bit / execution primitive) لا يوفرها هذا الـ PoC.يُظهر ./run.sh rce-gate هذا بالضبط وبأمانة: يزرع خطاف (hook) pre-receive.d عبر الاجتياز، ثم يدفع (push)، ويُظهر أن الخطاف بصلاحيات 0644 لا يُنفَّذ. ويضبط ./rce_gate_demo.sh --illustrate-gate أيضًا البت +x عبر docker exec chmod (إجراء جذر docker خارج النطاق (out-of-band)، وليس قدرة مهاجم) فقط لإظهار أن gitaly كان سيشغّله — بما يؤكد أن العتلة المفقودة هي بت التنفيذ.
انظر ANALYSIS.md للتحليل الكامل. باختصار: تحقَّق app/uploaders/gitlab_uploader.rb من مسار التخزين في before :cache فقط؛ وفي before :store كان يُكتب file_name المشتق من النموذج (الذي يتحكم فيه المهاجم وغير المُتحقَّق منه) حرفيًا. يضيف الإصلاح before :store, :protect_from_path_traversal!.
docker-compose.yml GitLab CE — الإصدار الضعيف (19.2.1) + المُصحَّح (19.2.2)
run.sh أداة تشغيل بأمر واحد مع مُحكِّم ملفات حتمي + PASS/FAIL
provision.rb إعداد المختبر: إنشاء PAT جذري + إنشاء مشروع (ليس جزءًا من الاستغلال)
exploit/poc.py مُرسِل الـ PoC + مُحكِّم --verify-docker
rce_gate_demo.sh عرض صادق لبوابة بت التنفيذ