Fastjson 1.2.80 autoType Throwable 异常类绕过分析

发布时间:2026/8/7 9:25:17
Fastjson 1.2.80 autoType Throwable 异常类绕过分析 Fastjson 1.2.80 autoType Throwable 异常类绕过分析写在前面1.2.47 缓存绕过之后官方在 1.2.48 堵了缓存1.2.68 又祭出safeMode这把“终极武器”。但 safeMode 默认不开–只要用户没显式setSafeMode(true)autoType 这扇门就还留着缝。1.2.80 就是顺着这条缝用Throwable异常类配合expectClass机制完成了 autoType 在 safeMode 时代的最后一击。这篇拆expectClass这条常被忽略的路径看 Throwable 是怎么借它绕过黑名单的。纯源码分析无截图。一、漏洞简介漏洞组件Alibaba fastjson影响版本1.2.68 ≤ version ≤ 1.2.80safeMode 之后、1.2.83 修复前漏洞类型autoType 黑名单绕过Throwable expectClass导致反序列化 RCECVE无fastjson 后续绕过均未申请 CVE修复版本1.2.83收窄 expectClass 路径前提未开启 safeModesafeMode 默认 false根因checkAutoType 的expectClass分支在 autoTypeSupportfalse 时仍可加载 expectClass 的子类Throwable 的反序列化路径会把用户可控的类名以expectClassThrowable.class传入绕过黑名单二、1.2.68 safeMode开了才安全先澄清一个误区。1.2.68 引入的safeMode确实是 autoType 的终结技// ParserConfig.checkAutoType (1.2.68)publicClass?checkAutoType(StringtypeName,Class?expectClass){if(safeMode){thrownewJSONException(safeMode not support autoType: typeName);// 直接拒}...}开了 safeModetype一律不认什么绕过都没用。但它默认是 false要用户主动ParserConfig.getGlobalInstance().setSafeMode(true)。绝大多数项目没开–于是 autoType 仍在工作1.2.80 的绕过就是针对这种“没开 safeMode”的默认配置。三、expectClass被忽略的旁路checkAutoType 有个第二参数expectClass平时很少被注意publicClass?checkAutoType(StringtypeName,Class?expectClass)它的本意是当 fastjson 已经知道“这个字段期望的类型是 X”比如某个方法的参数类型是Throwable解析type时传入expectClassX允许加载 X 的子类。这是为正常多态反序列化设计的–比如字段声明的是Exception实际 JSON 里给个具体子类。关键源码1.2.68 ~ 1.2.80简化// checkAutoType (1.2.68核心分支)if(safeMode)throw...;// ① 黑名单仍查longhashfnv1a_64(className);for(inti0;idenyHashCodes.length;i){if(hashdenyHashCodes[i])throw...;}// ② expectClass 旁路★ 关键if(expectClass!null){if(expectClassObject.class||expectClass.isAssignableFrom(/* 待加载类 */)){// 走这条加载 typeName只要它是 expectClass 的子类就放行Class?clazzTypeUtils.loadClass(typeName,defaultClassLoader,false);if(clazz!nullexpectClass.isAssignableFrom(clazz)){returnclazz;// ★ 不强制 autoTypeSupport绕过}}}...注意第 ② 步只要expectClass不为 null且待加载类是它的子类就放行–这条路径不检查 autoTypeSupport。换句话说即使 autoType 关着只要能骗 fastjson 以一个非 null 的expectClass调 checkAutoType就能加载expectClass的任意子类只要不在黑名单。问题变成怎么让 fastjson 用expectClass某个宽泛类型去调 checkAutoType而且这个宽泛类型有“不在黑名单的危险子类”–Throwable 正好满足。四、Throwable 的反序列化路径fastjson 对Throwable/Exception有专门的处理ThrowableReader/ 异常类的 deserializer。当 JSON 里type是java.lang.Exception或 Throwablefastjson 走异常解析分支它会把 JSON 里下一个type具体异常类名以expectClass Throwable.class调 checkAutoType// ThrowableReader 解析异常对象简化StringexClassName/* 读到的第二个 type具体异常类名 */;Class?exClassparser.getConfig().checkAutoType(exClassName,Throwable.class);// ★ expectClassThrowable// - 命中第②步 expectClass 旁路加载 exClass只要它是 Throwable 子类且不在黑名单ObjectexexClass.newInstance();// 填字段异常类的 message、cause 等 setterpayload 雏形两段type{type:java.lang.Exception,type:com.xxx.MaliciousException,field:...}第一个typejava.lang.Exception进 Throwable 解析分支Throwable 本身不在黑名单。第二个typecom.xxx.MaliciousException被 ThrowableReader 以expectClassThrowable.class调 checkAutoType。命中 expectClass 旁路只要MaliciousException是 Throwable 子类且不在黑名单就放行加载。五、利用链需要一个 Throwable 子类 gadget绕过黑名单只是“能加载类”要 RCE 还得这个类有副作用。这里和前几篇不一样1.2.80 本身只提供“加载任意 Throwable 子类”的能力gadget 得自己找。常见的姿势找一个构造方法或 setter 里有 JNDI/反射/加载动作的 Throwable 子类通常在第三方库里。比如某些库的自定义异常在构造时会记录上下文、加载资源被挪用成触发点。典型 PoC 常结合 Groovy、Spring 等库里的特定异常类具体哪个类随依赖而变核心是“它是 Throwable 子类 有副作用”。typejava.lang.Exception - 进 Throwable 解析分支 └ 第二个 type某 Throwable 子类(不在黑名单) └ checkAutoType(name, Throwable.class) └ expectClass 旁路 - 加载该异常类(autoTypeSupport 不查) └ 实例化 setter/构造副作用 - RCE所以 1.2.80 的利用门槛比 1.2.24/1.2.47 高–它要目标 classpath 上有合适的 Throwable gadget。但“能绕过黑名单加载任意 Throwable 子类”这一步本身已是 autoType 防线的失守。六、修复1.2.83 收窄 expectClass1.2.83 的修复针对 expectClass 旁路对expectClass路径也加上更严格的校验收紧哪些 expectClass 允许走旁路或对 Throwable 这类宽泛基类的子类也过黑名单// 1.2.83 思路简化expectClass 旁路也要过黑名单if(expectClass!null){// 不再无条件放行子类先过黑名单longhashfnv1a_64(className);for(inti0;idenyHashCodes.length;i){if(hashdenyHashCodes[i])throw...;}// 且 expectClass 必须在允许的范围内如非 Object/Throwable 这种过宽基类...}同时官方再次强调真正可靠的修复是开启 safeMode。只要 safeMode 开着第 ② 步根本走不到checkAutoType 顶部就抛异常了expectClass 旁路无从发挥。七、小结1.2.80 这篇我们看到 autoType 防线在 safeMode 时代的最后一道裂痕旁路参数也是攻击面expectClass本是为多态设计的“信任通道”但只要它存在且不检查 autoTypeSupport就成了绕过黑名单的侧门。任何“为方便而开的信任通道”都要问它能不能被外部输入触发默认配置是安全水位线safeMode 默认 false意味着绝大多数用户其实没受保护。一个“要手动开才安全”的防御等于把安全责任推给用户–而用户多半不会开。gadget 可移植1.2.80 把“加载任意类”收窄到“加载任意 Throwable 子类”提高了门槛但没堵死。只要 classpath 上有合适的异常类 gadget仍可 RCE。至此 fastjson 的 autoType 绕过史–从 1.2.24 裸奔、1.2.41 前后缀、1.2.47 缓存、到 1.2.80 Throwable–就走完了。最后那篇防御演进我们看官方是怎么一步步把这些洞堵上、最终走向 safeMode 的。参考fastjson GitHubhttps://github.com/alibaba/fastjson系列前篇1.2.24 autoType 分析、1.2.41-43 黑名单绕过、1.2.47 缓存绕过