jdk17-springboot原生链分析
字数 2035 2025-11-07 08:41:54
JDK 17 + SpringBoot 原生反序列化链分析教学文档
1. JPMS(Java 平台模块系统)概述
1.1 基本概念
- JPMS(Java Platform Module System,又称 Jigsaw)是 Java 9 引入的模块系统
- 核心目标:为 Java 平台提供真正的运行时模块化能力
- 主要功能:
- 将 JDK 和应用程序按模块(module)进行划分
- 明确模块间的依赖关系与可见性
- 减少类冲突问题
- 提升代码封装性
- 支持通过 jlink 工具创建小型化镜像
1.2 模块声明
模块通过 module-info.java 文件进行声明,例如:
module com.example.myapp {
requires java.base;
exports com.example.package;
}
1.3 访问限制实例
在 JDK 17 中,直接导入 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 会报错,原因是该包没有在 java.xml 模块中导出。即使使用 setAccessible(true) 也会失败,需要通过 --add-opens 参数显式开放访问权限。
2. 模块边界绕过技术
2.1 核心思路
通过反射获取 sun.misc.Unsafe 实例,利用 Unsafe 直接修改 java.lang.Class 的私有 module 字段,将当前类所属的 Module 替换为其他类的 Module。
2.2 技术实现
// 示例代码框架
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
// 获取目标类的 module 字段并修改
Class<?> targetClass = ...;
Object newModule = ...;
unsafe.putObject(targetClass, moduleFieldOffset, newModule);
3. JDK 17 反序列化链分析
3.1 限制与突破
- 限制:在 JDK 17 中,
BadAttributeValueExpException无法直接调用任意类的toString()方法 - 替代方案:使用
EventListenerList触发任意类的toString()方法调用
3.2 完整调用链
EventListenerList#readObject
→ add方法(类名拼接)
→ UndoManager的父类CompoundEdit的toString方法
→ Vector的toString方法
→ append方法
→ valueOf方法
→ 调用任意类的toString方法
4. Jackson 反序列化利用
4.1 POJONode 利用
new POJONode(恶意类) // 通过反射修改 _value 字段为恶意类
4.2 Jackson 序列化流程
EventListenerList调用POJONode的toString方法
→ 超类BaseJsonNode的toString
→ InternalNodeMapper#nodeToString
→ ObjectWriter#writeValueAsString
→ ObjectWriter#_writeValueAndClose
→ ObjectWriter#_serialize
→ DefaultSerializerProvider#serializeValue
→ DefaultSerializerProvider#_serialize
→ SerializableSerializer#serialize
→ InternalNodeMapper#serialize
→ InternalNodeMapper#serializeNonRecursive
→ POJONode#serialize
→ SerializerProvider#defaultSerializeValue
→ BeanSerializer#serialize
→ BeanSerializerBase#serializeFields
4.3 Jackson 不稳定性问题
- 根本原因:Jackson 在触发 getter 方法时,使用
getDeclaredMethods()获取所有方法 - 问题:根据 Java 官方文档,
getDeclaredMethods()返回的方法顺序是不确定的 - 后果:如果获取到非预期的 getter 方法,会导致序列化过程报错退出
5. JdkDynamicAopProxy 的关键作用
5.1 保证 getter 顺序稳定性
通过使用 JdkDynamicAopProxy 创建代理类,可以确保 Jackson 只能获取到 TemplatesImpl 的 getOutputProperties 方法,避免因方法顺序不确定导致的失败。
5.2 模块访问权限解决
- 问题:直接传入
TemplatesImpl对象会因模块访问限制而失败 - 原因:
com.sun.org.apache.xalan.internal.xsltc.trax包没有导出给外部模块 - 解决方案:通过 AOP 代理后,对外暴露的接口变为
javax.xml.transform.Templates - 优势:
javax.xml.transform.Templates在java.xml模块中是公开导出的,可以正常反序列化
6. 最终触发点
6.1 序列化字段处理
在 BeanPropertyWriter#serializeAsField 方法中:
_accessorMethod对应属性中的getOutputProperties方法bean参数对应POJONode的_value属性(即通过反射修改的恶意类)
6.2 最终执行
通过上述调用链,最终进入 TemplatesImpl 的 getOutputProperties 方法,实现代码执行。
7. 序列化处理注意事项
7.1 writeReplace 方法问题
- 问题:在
writeObject0序列化过程中会检查writeReplace方法 - 解决方案:需要删除或绕过
writeReplace方法
7.2 处理建议
通过反射修改或删除相关方法,确保序列化流程能够正常执行到恶意代码触发点。
8. 总结
本教学文档详细分析了在 JDK 17 + SpringBoot 环境下的原生反序列化利用链,重点涵盖了:
- JPMS 模块系统的限制与绕过技术
- 基于 EventListenerList 的 toString 触发链
- Jackson 反序列化的不稳定因素及稳定化方案
- JdkDynamicAopProxy 在模块访问和方法排序中的关键作用
- 完整的利用链构建和注意事项
该技术链结合了模块系统特性、反射技术和序列化机制,是在高版本 JDK 环境下实现反序列化利用的重要研究案例。