- 类别:
javanative别名:— 优先级:15 - 作者:Y4tacker、1ue
- 依赖:
com.alibaba:fastjson:1.x(目标 classpath 存在 fastjson 1.x 即可;无需开启 AutoType)
在 Java 原生反序列化 入口下,借助目标环境已存在的 fastjson 1.x,把内层「带危险 getter 的对象」放进 com.alibaba.fastjson.JSONArray,利用 JSONArray.toString() 会对元素执行 JSON 序列化、从而遍历调用其所有 getter 的特性,把 Java 反序列化的 toString 触发点转换成「任意 getter 调用」跳板。是一条不依赖 CommonsCollections / BeanUtils、仅需 fastjson 在 classpath 上的 Getter 型跳板。
其定位与 CommonsBeanutils1 类似(都把入口能力收敛为「调用内层对象的 getter」),区别在于触发媒介是 JSONArray.toString() 而非 BeanComparator.compare()。
- 入口
tags:JavaNativeDeserialize—— 承接 Java 原生反序列化入口(ObjectInputStream.readObject)。 - 衔接
nextTags:TemplatesImplChain、TemplatesImplWrapperChain、LdapAttributeChain、SignedObjectChain、MapMessageChain、C3p0JndiChain、DataSourceChains、DataSourceWrapperChain、hutoolJndiDSFactory、hutoolPooledDSFactory、hutoolSimpleDSFactory—— 之后可接任意「通过 getter 触发」的 sink 链:字节码型(TemplatesImpl 系)走getOutputProperties,JNDI 型(LdapAttribute、C3p0、各类 DataSource/DBCP、hutool 连接池)走 getter 中的 JNDI lookup。 excludes:NotForFastjson—— 标记了该 tag 的 gadget(自身即依赖 fastjson 处理逻辑、或与本链的引用绕过冲突者)不能与本链共用。
全部逻辑集中在 getObject 与 invoke 两个方法,toString 字段(@Param)在两条构造路径之间切换。
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
Object object = chain.doCreate(context); // 取链中下一环(内层 getter 载体)
context.put(ContextTag.FASTJSON_HANDLE_BYPASS_KEY, object); // 把该对象登记到上下文
return this.getObject(object);
}- 按「由内向外」构建:
chain.doCreate(context)先产出内层对象object(即下游 sink 链的最外层产物,一个「访问某 getter 即触发」的对象,如 TemplatesImpl、JdbcRowSetImpl、各 DataSource)。 context.put(ContextTag.FASTJSON_HANDLE_BYPASS_KEY, object):把该 getter 载体登记进GadgetContext。同一 key 被大量equals/toString型 gadget 读取(grep可见HashMapEquals、HashTableEquals、Rdn$RdnEntryEquals、BadAttributeValueExpExceptionToString等均context.get(FASTJSON_HANDLE_BYPASS_KEY)),用于跨 gadget 复用「同一个 getter 触发对象」,避免重复构造并保证引用一致。- 返回
getObject(object)得到最终交给上层 Payload 序列化的外层对象。
public Object getObject(Object obj) throws Exception {
JSONArray jsonArray = new JSONArray();
jsonArray.add(obj); // 关键:JSONArray 元素 = 内层 getter 载体
// ... 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); // 外层 HashMap,key=obj,value=bave
return map;
}逐段解释(触发链 BadAttributeValueExpException.readObject() => JSONArray.toString() => getter):
new JSONArray(); jsonArray.add(obj):把内层 getter 载体作为JSONArray唯一元素。JSONArray.toString()内部走JSON.toJSONString(this),fastjson 序列化器会对元素的每个属性调用其getXxx()(ASM/反射 getter 枚举)——这正是「任意 getter」触发点。- 通过反射把
jsonArray塞进javax.management.BadAttributeValueExpException的私有字段val。JDK 中BadAttributeValueExpException.readObject()在System.getSecurityManager() == null(绝大多数部署默认)时会执行valObj.toString(),从而在原生反序列化阶段调用jsonArray.toString(),把控制流交给上一步的 fastjson getter 枚举。 - 清空
stackTrace/suppressedExceptions/cause:使反序列化过程更干净、缩小 payload 并规避某些字段写入异常。 - 最外层再包一个
HashMap,map.put(obj, bave):以内层对象obj为 key、BadAttributeValueExpException为 value。外层 HashMap 承担description所称「Hashmap 的引用机制绕过 Fastjson 反序列化的检测」——将obj同时作为 key 与JSONArray元素两处引用出现,使 fastjson 在序列化枚举时对obj的 getter 真正求值(而非被循环引用$ref/去重逻辑短路),保证 sink getter 被执行。
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]);
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); // 哈希碰撞双键 HashMap
ArrayList<Object> arrayList = new ArrayList<>();
arrayList.add(obj);
arrayList.add(map);
return arrayList;
}- 用
XStringForChars(com.sun.org.apache.xpath.internal.objects.XString家族)替代BadAttributeValueExpException作为toString触发媒介。createWithoutConstructor免构造实例化、m_obj置空字符数组,避免构造/序列化副作用。 "zZ"与"yy"是一对 fastjson/JDK 场景常用的同哈希桶碰撞键名;两个 HashMap 中xString与jsonArray的键位互换,再由PayloadHelper.makeMap(hashMap1, hashMap2)组合成一个「readObject时会强制两键equals比较」的 HashMap(与本项目XString1中makeMap(h1, h2)同一套哈希碰撞原语)。反序列化HashMap.readObject → putVal时触发XString.equals(jsonArray),XString#equals会对参数取toString,从而落到JSONArray.toString => getter。- 选择 xString 的动机见
@Param.description:xString 在高版本 JDK 上 serialVersionUID 一致,跨 JDK 兼容性优于BadAttributeValueExpException(后者在部分高 JDK 上 SerId/字段有差异,易InvalidClassException)。最外层用ArrayList[obj, map],同样让obj与内部jsonArray中的obj形成两处引用,配合上文「引用机制」保证 getter 求值。
注:
PayloadHelper.makeMap/Reflections.*属工具类(打包在依赖 jar 中,未随本类反编译出体),其语义依据同项目内XString1等对makeMap(h1, h2)的一致用法确定——即经典的「两元素哈希碰撞 HashMap,readObject阶段触发 key1.equals(key2)」原语(与 ysoserial URLDNS 同源)。
| 字段 | 名称 | 说明 | 类型 | 可选值 | 默认 |
|---|---|---|---|---|---|
toString |
选用toString进行触发 | 选择 badAttributeValueExpException 或 xString 作为 toString() 触发媒介;xString 在高 JDK 上 serialVersionUID 一致、兼容性更好 |
ParamType.Choice |
badAttributeValueExpException、xString |
badAttributeValueExpException |
- 依赖:目标 classpath 存在
com.alibaba:fastjson:1.x。本链利用的是JSONArray.toString()(序列化侧枚举 getter)而非 fastjson 的反序列化 AutoType,因此不要求开启 AutoType、不受 AutoType 黑白名单约束——只要 fastjson 类在类路径即可。 - 入口:Java 原生反序列化(
readObject)。链尾 getter 载体决定最终效果:TemplatesImpl 系 → 加载字节码 RCE(要求目标 JDK 能加载TemplatesImpl,通常 JDK < 17);LdapAttribute / C3p0 / DataSource / hutool 连接池 → JNDI 注入或 JDBC 二次利用。 - 两种
toString媒介:badAttributeValueExpException依赖System.getSecurityManager()==null(默认成立)且 JDK 版本与BadAttributeValueExpException序列化兼容;xString走HashMap#readObject → XString#equals碰撞路径,高 JDK 兼容性更好,是高版本环境的推荐选项。 - 引用绕过要点:外层容器(默认
HashMap,xString 下ArrayList+ 碰撞HashMap)刻意让内层obj与JSONArray内元素形成多处引用,规避 fastjson 序列化去重 / 循环引用短路,确保 sink getter 实际被求值——即description的「Hashmap 引用机制绕过检测」。
- Fastjson链
- Fastjson JNDI链
- Fastjson C3p0 Jdbc h2链
- Hessian Fastjson 链(Hessian 桥接场景中作为 Java 反序列化/toString 段)
- 下游(
nextTags):TemplatesImpl(getOutputProperties字节码 sink)、LdapAttribute、C3p0 / 各 DataSource / hutool 连接池(JNDI getter sink)、SignedObject / MapMessage 等二次反序列化 getter。 - 同族:Fastjson2(同一
getObject思路,依赖收敛为com.alibaba:fastjson <= 2.0.26)。 - 共享上下文机制:与
equals/tostring型 gadget 共用ContextTag.FASTJSON_HANDLE_BYPASS_KEY(如HashMapEquals、BadAttributeValueExpExceptionToString)。 - 同类思路对照:CommonsBeanutils1(Getter 跳板,媒介为
BeanComparator.compare)。
- 检测特征:
- 序列化流中同时出现
javax.management.BadAttributeValueExpException+com.alibaba.fastjson.JSONArray(默认路径),或com.sun.org.apache.xpath.internal.objects.XStringForChars+ 碰撞键名zZ/yy+java.util.HashMap(xString 路径)。 JSONArray出现在readObject可达的字段(BadAttributeValueExpException.val)中,是强烈信号。- 反序列化异常栈中出现
JSONArray.toString → JSON.toJSONString → ... getXxx、或HashMap.readObject → XString.equals帧。
- 序列化流中同时出现
- 加固建议:
- 反序列化入口启用类白名单(
ObjectInputFilter/ValidatingObjectInputStream),拒绝BadAttributeValueExpException、XStringForChars、JSONArray等非业务类。 - 生产环境尽量移除不必要的
fastjson 1.x依赖;确需保留时升级并配合安全模式(本链虽不走 AutoType,但移除依赖可根除该跳板)。 - 高 JDK(≥ 17)默认无法加载
TemplatesImpl字节码路径,可显著削弱 RCE sink;同时对 JNDI 相关(com.sun.jndi.ldap.object.trustURLCodebase等)做限制以阻断 JNDI sink。
- 反序列化入口启用类白名单(