盘点JDK下的动态代理在反序列化链中的利用原理
LeeH 历史精选 281浏览 · 2025-02-11 13:06

CompositeInvocationHandlerImpl:jdk自带,能根据不同的方法所属的类,选择不同的InvocationHandler,可以看作InvocationHandler的代理。

AnnotationInvocationHandler:(指定一个方法返回指定的对象)同样jdk自带,也是反序列化gdaget的常客了,它在JSON1中承担的责任实际类似一个"垃圾桶",当调用CompositeData#getCompositeType()方法时可以返回一个CompositeType实例,避免报错抛异常。

JdkDynamicAopProxy:来自spring-aop依赖,反射调用方法。

AutowireUtils$ObjectFactoryDelegatingInvocationHandler:来自spring-beans依赖,反射调用方法,对象中this.objectFactory.getObject()中获取

JSONObject: 来自fastjson依赖,能够控制getter方法调用返回的对象

实例

JSON1链

image-20250115104836962.png


图源自:https://mp.weixin.qq.com/s/gl8lCAZq-8lMsMZ3_uWL2Q

简单分析一下JSON1的流程吧,更好的理解动态代理的用法

JSON1这条链子来源于ysoserial框架

https://github.com/frohoff/ysoserial

首先看一下这条链子的调用栈:

这条链子同样是因为json-lib这个json库,在反序列化过程中导致的getter方法调用导致的反序列化漏洞Gadgets,这里我们主要关注这条链子使用动态代理的方式解决问题的思想

JSON1:

这里在JSONObject中包裹的是cdsProxy,其为CompositeData对象,其通过调用Gadgets.createProxy创建的一个代理对象

image-20250115160538334.png


对应Proxy对象的InvocationHandler为CompositeInvocationHandlerImpl实现类,用来代理了多个接口,例如这里的CompositeData以及TemplateImpl

在进行反序列化调用时,该json组件将会触发所有在这两个代理的类中存在的getter方法

特别的,其中因为在CompositeData类中的getCompositeType这一个getter方法调用将会抛出异常,造成反序列化利用过程中退出,造成利用失败,这里通过Gadgets.createMemoizedInvocationHandler创建了一个InvocationHandler对象类将其保存在了CompositeInvocationHandlerImplclassToInvocationHandler属性中,用来处理在调用该getter调用返回我们精心构造的对象避免错误的发生,那么这一步是如何生效的呢?后面会介绍

同样,这里也创建了一个JdkDynamicAopProxy对象实例来代理除了CompositeData外的其他类的getter方法调用

一步一步来,首先看一看在getter方法反射调用过程中,第一个代理类CompositeInvocationHandlerImpl是如何处理的

image-20250115161432825.png


在进行反射调用时,将会调用CompositeInvocationHandlerImpl#invoke方法

1 首先会获取待调用方法所属的类,通过method.getDeclaringClass()

2 在获取了所属类之后从classToInvocationHandler中获取对应类是否代理了特殊处理的InvocationHandler对象

3 若存在特殊代理的InvocationHandler对象,则将方法调用传递给它的invoke方法进行处理

4 若不存在该对象,且defaultHandler这一默认的Handler也存在的话,则交由它进行处理

根据流程来看,CompositeInvocationHandlerImpl这一个Handler主要的作用是用来对Method方法调用进行分流处理

接下来看看代理CompositeData类的Handler,这是一个AnnotationInvocationHandler的实现类

image-20250115162140966.png


其invoke方法:

image-20250115162605991.png


其中var2为传入的待调用的getter方法,首先获取它的方法名为var4,在switch-case语句中的默认分支中,将会从memberValues属性中获取对应方法名返回的预定义的对象,之后直接进行返回,依靠着这样的方式能够避免getCompositeType调用导致的错误

根据流程来看,AnnotationInvocationHandler这一个Handler主要的作用是用来对特定Method方法的返回进行预定义,避免出现未知错误

接下来再瞅一瞅处理其他类所用到的JdkDynamicAopProxy

在创建该类对象是,传入的是一个AdvisedSupport对象参数,将其保存在advised属性中,特别的在该对象的targetSource属性中,存在着恶意的TemplateImpl对象

image-20250115163120197.png


在getter方法调用时,触发其invoke方法

image-20250115163545417.png


1 首先是从this.advised.targetSource属性中获取到TargetSource对象

2 之后调用getTarget方法后去具体的对象,这里是恶意的TemplateImpl对象

3 最后调用AopUtils.invokeJoinpointUsingReflection进行获取对象的getter方法的反射调用
image-20250115163837698.png


根据流程来看,JdkDynamicAopProxy这一个Handler主要的作用是用来进行方法的反射调用

以上大致分析了JSON1链中的动态代理使用的三个类,以及各个类所能够存在的作用

Spring1链

这条链子同样来自于ysoserial项目

其核心是利用了sun.reflect.annotation.AnnotationInvocationHandlerorg.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler这两个代理类进行结合利用

首先看一下这条链子的调用栈:

简单分析一下整个反序列化链的流程,核心分析如何使用动态代理实现我们的目的

这条反序列化链子的入口在org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider#readObject方法中

image-20250116111346742.png


其在readObject实现中,将会反射调用我们从provider中获取的方法

这里作者使用创建了一个sun.reflect.annotation.AnnotationInvocationHandler实例,用来代理org.springframework.core.SerializableTypeWrapper$TypeProvider类实现,将其作为provider属性值传入到SerializableTypeWrapper$MethodInvokeTypeProvider的构造方法中

当在反序列化过程中调用provider属性值的getType方法,将会触发AnnotationInvocationHandler#invoke调用,在这个过程中返回了我们预先定制的类对象,也即是另一个动态代理类AutowireUtils$ObjectFactoryDelegatingInvocationHandler

image-20250116112411685.png


而动态代理类AutowireUtils$ObjectFactoryDelegatingInvocationHandler代理的接口为Type.clas以及Templates.class,将其作为getType调用的返回

回到反序列化的流程,我们在readObject中的ReflectionUtils.findMethod调用中,getType返回的是AutowireUtils$ObjectFactoryDelegatingInvocationHandler代理对象,因为该对象同样代理了Template.class,则是能够反射查找到该Method,也即是newTransformer

接下来来到ReflectionUtils.invokeMethod方法的反射调用过程,在执行代理类的newTransformer调用时将会触发AutowireUtils$ObjectFactoryDelegatingInvocationHandler#invoke调用

image-20250116113726278.png


在这个方法中,同样存在有反射调用的实现,其object对象是从objectFactory.getObject获取来的

作者这里是通过再次创建一个AnnotationInvocationHandler实例,代理接口ObjectFactory.class,使得在调用getObject方法时,能够返回恶意的TemplateImpl对象,进行进行命令执行

根据流程来看,AutowireUtils$ObjectFactoryDelegatingInvocationHandler这一个Handler主要的作用是用来进行方法的反射调用

限制

AnnotationInvocationHandler

jdk8u71-b12之后,AnnotationInvocationHandler代码进行了修改,无法再代理非注解的接口方法,这就导致了AnnotationInvocationHandler无法再代理CompositeData接口,此时再调用getCompositeType方法就会报错。

要在高版本中稳定利用,需要替换掉AnnotationInvocationHandler。注意到JdkDynamicAopProxy的能力,容易想到再新建一个JdkDynamicAopProxy实例来代理CompositeData接口的子类(同时要求实现Serializable接口),并找到了一个jdk自带的CompositeData实现类:javax.management.openmbean.CompositeDataSupport

image-20250116112118861.png


参考

https://mp.weixin.qq.com/s/gl8lCAZq-8lMsMZ3_uWL2Q

https://github.com/frohoff/ysoserial

0 条评论
某人
表情
可输入 255