
إعادة إنتاج وتحليل مستقلان باستخدام Docker لـ CVE-2024-23897، وهو قراءة ملفات عشوائية عبر واجهة Jenkins CLI باستخدام توسعة الوسيطات @-syntax في args4j.
إعادة إنتاج محلية ومُكتفية ذاتياً بالكامل لـ CVE-2024-23897، وهي ثغرة حرجة (درجة أساسية CVSS 3.1 تبلغ 9.8) لقراءة ملف عشوائي في واجهة سطر الأوامر (CLI) الخاصة بـ Jenkins. يقوم المشروع بتشغيل مجموعتين من Docker تختلفان فقط في الإصدار الثانوي لـ Jenkins، ثم يُنفذ نفس إثبات المفهوم ضد كليهما، ويُظهر الثغرة وهي تعمل على الخادم غير المُصحح وتصمت على الخادم المُصحح.
كل شيء مُدار بواسطة برنامج Python واحد، poc.py. الإطار يُنسق البيئة فقط (Docker Compose، عميل jenkins-cli.jar الرسمي، والنسخ الحرفي للمخرجات). الثغرة نفسها موجودة في كود Java الخاص بـ Jenkins ولم يتم إعادة تنفيذها هنا.
يوجد تحليل كامل مكتوب، بما في ذلك مراجعة التصحيح وتفكيك CVSS، في report/report.pdf.
يبني Jenkins CLI محلل وسائطه باستخدام مكتبة args4j. تحتوي args4j على ميزة تسمى expandAtFiles، يتحكم فيها العلم atSyntax ومُفعّلة افتراضيًا، والتي تعيد كتابة أي وسيط من الشكل @/path/to/file إلى محتويات ذلك الملف قبل تشغيل الأمر. يُفتح الملف بصلاحيات عملية خادم Jenkins. نظرًا لأن جميع طبقات نقل CLI (HTTP وWebSocket وSSH) تتجمع في نفس المحلل، يمكن لأي عميل يمكنه إرسال أي أمر CLI قراءة ملفات عشوائية من الخادم. التصحيح (الالتزام 554f0378) يضيف ثابتًا ALLOW_AT_SYNTAX افتراضيًا على false، مما يعطل التوسيع.
هذا ليس اجتياز مسار تقليدي: لا يوجد ../ ولا دليل أساسي للهروب. المسار يُفتح مباشرة. على محور النتيجة هو قراءة ملف عشوائي؛ على محور الآلية هو توسيع وسيط.
cve-2024-23897-jenkins-poc/
README.md # هذا الملف
LICENSE
poc.py # إطار إعادة الإنتاج بلغة Python (جميع الأوامر الفرعية)
Dockerfile.vuln # jenkins/jenkins:2.426.2-lts + matrix-auth
Dockerfile.fix # jenkins/jenkins:2.426.3-lts + matrix-auth
docker-compose.vuln.yml # jenkins-vuln + attacker-vuln
docker-compose.fix.yml # jenkins-fix + attacker-fix
init.groovy.d/
01-create-users.groovy # يُنشئ مسؤول + مستخدم قراءة عبر matrix-auth
evidence/
docker-versions.txt # إصدارات Docker وCompose على المضيف
output-vulnerable.txt # تم التقاطه أثناء `poc.py exploit`
output-fixed.txt # تم التقاطه أثناء `poc.py verify-fix`
report/
report.pdf # تحليل كامل مكتوب
report.tex # مصدر LaTeX (مُكتفي ذاتيًا، بدون أشكال خارجية)
| المتطلب | ملاحظات |
|---|
لا حاجة إلى JDK على المضيف. يعمل Java داخل حاوية المهاجم. ملفات compose تحدد platform: linux/amd64 حتى تعمل الصور بشكل متطابق على Apple Silicon؛ هذا اختيار تشغيلي ولا يمس الثغرة، وهي مستقلة عن النظام الأساسي.
من داخل المستودع:
python3 poc.py up-vuln # بناء وبدء الحزمة غير المُصححة، جلب ملف CLI jar
python3 poc.py place-proof # كتابة ملف العلامة غير الضار داخل الخادم
python3 poc.py exploit # تشغيل PoC، يكتب evidence/output-vulnerable.txt
python3 poc.py up-fix # إيقاف الحزمة غير المُصححة، بناء وبدء الحزمة المُصححة
python3 poc.py verify-fix # تشغيل نفس PoC، يكتب evidence/output-fixed.txt
python3 poc.py teardown # إيقاف وإزالة كلتا الحزمتين
التشغيل الأول يستغرق بضع دقائق (سحب الصور بالإضافة إلى تثبيت الإضافة). التشغيلات اللاحقة أسرع بكثير.
poc.py exploit يمر عندما يظهر سلسلة العلامة POC-PROOF-LINE في المخرجات الملتقطة (حدث التسريب). poc.py verify-fix يمر بالشرط المعاكس: يجب أن تكون العلامة غائبة. كلا الأمرين الفرعيين يخرجان برمز غير صفري عند الفشل، لذا فإن ملفي الأدلة مع حالة خروجهما هما أنفسهما نتيجة الاختبار.
يعمل ملفا compose على تشغيل نفس الخدمتين على شبكة Docker واحدة: خادم Jenkins (الضحية) وحاوية مهاجم صغيرة eclipse-temurin:17-jre. يعمل poc.py على المضيف ويقود Docker عبر subprocess، لكن التشغيل الفعلي java -jar jenkins-cli.jar ... يعمل داخل حاوية المهاجم. لا يملك المهاجم وصولاً إلى وحدة تخزين بيانات Jenkins؛ يصل إلى الخادم فقط عبر الشبكة، كما يفعل مهاجم عن بُعد.
يستخدم init.groovy.d/01-create-users.groovy إضافة matrix-auth لإنشاء حسابين بصلاحيات مختلفة عمدًا:
| الحساب | الصلاحيات |
|---|
هذا يُعيد إنتاج الفصل الدقيق في الإعلان الرسمي، Overall/Read مقابل مجهول، بدلاً من الفصل الأوسع "أي مستخدم مسجل دخول مقابل مجهول" الذي كان لينتجه الإعداد الافتراضي لـ Jenkins Core. وبالتالي فإن تسريب الملف الكامل يُعزى إلى CVE وحدها، وليس إلى نطاق المسؤول.
يدير PoC أربعة سياقات ضد كل خادم. الجدول أدناه يُلخص المقارنة A/B؛ النسخ الكاملة موجودة في evidence/.
المتغير الوحيد الذي يتغير بين العمودين هو الإصدار الثانوي لـ Jenkins، ومن خلاله القيمة الافتراضية بعد التصحيح لـ atSyntax. النتائج المتعاكسة تُعزو تغيير السلوك إلى إصلاح المحلل في الالتزام 554f0378.
تشغيل Jenkins في حاوية ليس تخفيفًا. يقرأ المحلل الملفات بصلاحيات JVM الخاصة بـ Jenkins، وتلك الملفات موجودة داخل نظام ملفات الحاوية نفسه الذي يخزن مخزن بيانات الاعتماد. حدود الحاوية تحمي المضيف من عملية Jenkins، وليس عملية Jenkins من نفسها. إعداد Docker هنا هو مجرد صندوق توضيحي، لا أكثر.
الترقية إلى إصدار مُصحح: 2.442 (أسبوعي)، أو 2.426.3 أو 2.440.1 (LTS)، وكلها نُشرت في 24 يناير 2024. إذا كانت الترقية الفورية غير ممكنة، اترك خاصية النظام hudson.cli.CLICommand.allowAtSyntax غير مُعينة (قيمتها الافتراضية)، مما يبقي ALLOW_AT_SYNTAX على false ويعطل التوسيع. تعطيل طبقات نقل CLI الفردية هو إجراء جزئي فقط، لأن الثلاثة تتقارب على نفس المحلل.
جميع الأعمال تجري في بيئة Docker محلية ضد ملف علامة غير ضار من ثلاثة أسطر (/tmp/poc-proof.txt) يُنشئه الإطار بنفسه. لا يتم فحص أو الاتصال بأي مثيل Jenkins عام، ولا تتم قراءة أي سر حقيقي مثل secrets/master.key أو credentials.xml.
554f0378 على jenkinsci/jenkins: https://github.com/jenkinsci/jenkins/commit/554f03782057c499c49bbb06575f0d28b5200edbParserProperties.withAtSyntax): https://github.com/kohsuke/args4j| Docker Engine الإصدار 24 أو أحدث | الإصدار المُستخدم مُسجل في evidence/docker-versions.txt |
| Docker Compose v2 | مُدمج كملحق docker compose المدمج |
| Python 3.8 أو أحدث | المكتبة القياسية فقط، لا حاجة إلى pip install |
| مساحة قرص | حوالي 1.5 جيجابايت لصورتي Jenkins، وصورة temurin، وإضافة matrix-auth |
| الدور في PoC |
|---|
admin | Jenkins.ADMINISTER | موجود فقط لتلبية "على الأقل مسؤول واحد"، لا يُستخدم أبدًا للهجوم |
readuser | Jenkins.READ فقط (Overall/Read) | المهاجم المُوثق |
| مجهول | لا شيء | المهاجم غير المُوثق |
| الملاحظة | غير مُصحح 2.426.2 | مُصحح 2.426.3 |
|---|
معالجة رمز @ | توسع إلى محتويات الملف | تُعامل كسلسلة نصية حرفية |
readuser + connect-node | كشف كامل للملف (3 من 3 أسطر) | لا كشف |
مجهول + who-am-i / help | تسريب جزئي (السطر الأول) عبر خطأ المحلل قبل بوابة المصادقة | لا كشف |
العلامة POC-PROOF-LINE في المخرجات | موجودة | غائبة |