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 只能获取到 TemplatesImplgetOutputProperties 方法,避免因方法顺序不确定导致的失败。

5.2 模块访问权限解决

  • 问题:直接传入 TemplatesImpl 对象会因模块访问限制而失败
  • 原因com.sun.org.apache.xalan.internal.xsltc.trax 包没有导出给外部模块
  • 解决方案:通过 AOP 代理后,对外暴露的接口变为 javax.xml.transform.Templates
  • 优势javax.xml.transform.Templatesjava.xml 模块中是公开导出的,可以正常反序列化

6. 最终触发点

6.1 序列化字段处理

BeanPropertyWriter#serializeAsField 方法中:

  • _accessorMethod 对应属性中的 getOutputProperties 方法
  • bean 参数对应 POJONode_value 属性(即通过反射修改的恶意类)

6.2 最终执行

通过上述调用链,最终进入 TemplatesImplgetOutputProperties 方法,实现代码执行。

7. 序列化处理注意事项

7.1 writeReplace 方法问题

  • 问题:在 writeObject0 序列化过程中会检查 writeReplace 方法
  • 解决方案:需要删除或绕过 writeReplace 方法

7.2 处理建议

通过反射修改或删除相关方法,确保序列化流程能够正常执行到恶意代码触发点。

8. 总结

本教学文档详细分析了在 JDK 17 + SpringBoot 环境下的原生反序列化利用链,重点涵盖了:

  1. JPMS 模块系统的限制与绕过技术
  2. 基于 EventListenerList 的 toString 触发链
  3. Jackson 反序列化的不稳定因素及稳定化方案
  4. JdkDynamicAopProxy 在模块访问和方法排序中的关键作用
  5. 完整的利用链构建和注意事项

该技术链结合了模块系统特性、反射技术和序列化机制,是在高版本 JDK 环境下实现反序列化利用的重要研究案例。

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 文件进行声明,例如: 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 技术实现 3. JDK 17 反序列化链分析 3.1 限制与突破 限制 :在 JDK 17 中, BadAttributeValueExpException 无法直接调用任意类的 toString() 方法 替代方案 :使用 EventListenerList 触发任意类的 toString() 方法调用 3.2 完整调用链 4. Jackson 反序列化利用 4.1 POJONode 利用 4.2 Jackson 序列化流程 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 环境下实现反序列化利用的重要研究案例。