安全攻防
evening
Fastjson 反序列化实战复盘:从黑盒 AutoType 探测到容器层面的利用边界
2026-11-13
1 阅读
约10分钟阅读
先知社区
字号:
背景: 1.2.24 之前 : autoType 默认开启, @type 可以直接指定任意类,利用门槛最低。 1.2.25 :引入 autoTypeSupport ,默认关闭;但黑名单覆盖不全,仍存在可绕过的期望类路径。 1.2.41、1.2.42、1.2.43、1.2.45、1.2.47 等版本 :陆续修复已知绕过,但每次修复后又会出现新的期望类路径,攻防进入拉锯。 1.2.68 :引入 safeMode ,开启后硬拒绝所有 @type 加载,比黑名单更彻底。 1.2.80 :将 AutoCloseable 加入黑名单,常见的探测类被收紧。 Fastjson2 :从架构上移除旧版 autoType 的默认行为,旧链的适用面进一步收窄。 一、 引言:触发点 在一次针对某金融企业的外网 SRC 打点中,我发现了一个返回数据极其复杂的业务接口。按照习惯,我向 JSON 参数中插入了一个畸形的闭合符,服务器直接返回了 500 错误,堆栈清晰可见: com.alibaba.fastjson.JSONException 。 很多人在这一步就直接上 ysoserial 或者现成的 Payload 了。但我要说的是:“存在 Fastjson”和“能够拿下服务器”之间,隔着一道巨大的鸿沟。Fastjson 的 autoType 状态、目标 JDK 版本、Classpath 下的第三方依赖,甚至传入参数的上下文,都会决定这条链能否走通。 先纠正一个高频混淆: Fastjson 的 @type 解析不经过 JEP 290。 JEP 290 的 ObjectInputFilter 作用于 ObjectInputStream 的类解析阶段,管的是 RMI 注册表、原生 Socket 反序列化这类入口。Fastjson 走的是自己的 ParserConfig 和 checkAutoType 。如果目标只接收 JSON 文本,要突破的是 safeMode 和黑名单;只有拿到 rO0AB 或 aced0005 开头的字节流,才轮到讨论 JEP 290。 这个区分不是文字游戏,它直接决定排查方向。如果入口只解析 JSON,却把失败归因于“JEP 290 拦截”,后续所有链的选择都会南辕北辙。Fastjson 侧真正要确认的是: autoTypeSupport 是否开启、 safeMode 是否启用、目标版本的黑名单覆盖了哪些类、业务代码是否开启了 SupportNonPublicField 、Classpath 中是否存在可被 @type 驱动的期望类。只有把这些条件逐项确认,才能判断一条链是“理论上可用”还是“在这个入口上可用”。 二、 黑盒探测:先确认 autoType 的状态 确认组件后,第一步不是盲目扔 TemplatesImpl ,而是判断 autoType 是否处于可被解析的状态。 用 {"@type":"java.lang.AutoCloseable"} 做无害探测。这个类的响应特征需要分版本看: 如果返回的是后者,说明该入口的 autoType 已被收紧,后续链的选择需要更加依赖目标组件本身的白名单。本次目标返回的是类加载相关的异常,并未明确拒绝,据此判断目标 autoType 处于可被解析的状态。 这里需要强调探测的局限性:不同 Fastjson 小版本、不同 ParserConfig 配置、不同业务封装下,响应可能不一致。 AutoCloseable 只是一个低成本的“探针”,它不能证明 autoType 完全开放,只能帮助排除“明确拒绝”的情况。真实测试中还应结合其他无害类做交叉验证,并观察异常堆栈中是否出现 checkAutoType 、 autoType is not support 、 ClassNotFoundException 等关键字符串。只有响应特征稳定指向“允许解析但后续失败”,才值得进入下一条链的尝试。 三、 TemplatesImpl 链的尝试与失败信号 autoType 可解析之后,首选不出网的 TemplatesImpl 。这条链的价值在于不需要目标出网,不依赖 LDAP/RMI,直接把恶意字节码放进 _bytecodes ,由 Fastjson 在本地完成 defineClass 。 但它有两个硬性前提: 必须开启 SupportNonPublicField : _bytecodes 是私有字段,Fastjson 默认只处理 public 字段和 setter,未开启该特性时不会被赋值。 _name 必须非空 : TemplatesImpl 在 _name == null 时会提前返回,不会进入 defineTransletClasses 。 EvilTranslet 需要实现 AbstractTranslet 的两个 transform 方法,否则编译不通过: import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; public class EvilTranslet extends AbstractTranslet { static { try { // Linux 下使用 touch 验证 Runtime.getRuntime().exec("touch /tmp/pwn_templates"); } 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 {} } 构造 Base64 编码的 _bytecodes ,连同 _name: "a" 发送。现象:目标无回显、无文件落地、无 DNS 请求。这个失败信号非常明确:该接口的 parseObject 没有开启 SupportNonPublicField ,导致 _bytecodes 根本未被赋值。 TemplatesImpl 链在此入口不可用,必须转向出网路径。 这里还要补一个版本边界: TemplatesImpl 在 Fastjson 1.2.24 到 1.2.47 等早期版本中较
这篇文章对您有帮助吗?
订阅66必读
每日精选科技资讯,直达你的邮箱