java反序列化之yso中的spring链子分析及利用
Sherlock 发表于 浙江 漏洞分析 538浏览 · 2025-05-01 13:34

前言

最近开始学习spring链子,看网上分析spring链子的文章不多,讲的也比较简单,还都是yso中的spring1的链子分析,特此出一篇文章来详细分析spring1和spring2链子以及自己的关于它的其他利用姿势

下面所用的java版本都是8u65

Spring1

spring版本范围在3.0.0到4.1.4

依赖

链子分析

链子如下所示:

该链子运用到了三层动态代理,因此要求我们对动态代理的原理和利用要有一个较为清晰的认识,关于动态代理的相关知识可详见一篇文章:https://cina666.github.io/2024/11/06/JDK%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/

好了接下来我们开始正式分析

首先我们先看入口,也就是SerializableTypeWrapper.MethodInvokeTypeProvider.readObject()

image-20250501103612885.png
图片加载失败


这里主要就是通过反射完成方法调用,按照我们之前所学的肯定就要想到调用TemplatesImpl.newTransformer()来实现任意类加载从而可以执行任意命令

因此我们正常思路下我需要让this.provider.getType()返回TemplatesImpl对象,this.methodName为newTransformer()方法,mehodName可直接通过反射赋值

看眼getType()方法

image-20250501104538347.png
图片加载失败


返回的是this.result,而在构造函数里面result是transient,不可被序列化的,相当于这个方法对我们来说是无用的,这个时候我们就要用上动态代理了,当调用getType()方法的时候通过InvocationHandler的处理让其返回的是TemplatesImpl对象,而在学cc链的过程中我们有接触到这么一个类AnnotationInvocationHandler,它的invoke方法可以通过控制memberValues的属性来让其返回特定的对象

究其原因在于 memberValues是一个Map<String,Object>类型的对象,我们可以设置键为getType,值为我们的TemplatesImpl对象便可以返回我们想要的,链子不应该就这么结束了吗

其实不然,回看getType()方法,它要求的是要返回一个Type类型的数据,而我们返回TemplatesImpl对象的话便会产生类型上的冲突,强转不了导致报错

所以我们通过getType()还要返回一个动态代理对象,其必须实现Type和Templates接口

那么要选择哪一个InvocationHandler,才能够让我们在调用该动态代理对象的newTransformer方法的时候调用到我们TemplatesImpl对象的newTransfoemer方法呢

注意一下,上面讲的调用该动态代理对象的newTransformer方法是在ReflectionUtils.invokeMethod()里调用的

yso的作者替我们找到了答案,即AutowireUtils.ObjectFactoryDelegatingInvocationHandler

看下它的invoke方法

image-20250501110556777.png
图片加载失败


重点关注return method.invoke(this.objectFactory.getObject(), args);,通过这个我们可以知道只要我们控制this.objectFactory.getObject()的返回值为我们的TemplatesImpl对象便可以了

又提到了控制,那么自然而然地就又想到了通过前面提到的动态代理,也就是让this.objectFactory为一个代理对象,其InvocationHandler自然就还是AnnotationInvocationHandler,但是这一次的memberValues的键就要换成getObject,值还是我们亲爱的TemplatesImpl对象

在这里我们便可以实现反射调用TemplatesImpl的newTransformer方法,从而实现任意类加载

调试

综上所述,我们的poc如下

其中的Test.class文件的具体内容如下

那ok,我们进行调试

image-20250501112528030.png
图片加载失败


跟进getType方法,按照动态代理的原理,就会直接跳到InvocationHandler中的invoke方法处,如下:

image-20250501112633447.png
图片加载失败


从merberValues中读到了getType的值,也就是同时实现Type和Templates接口的代理对象,后返回result

通过ReflectionUtils.findMethod方法成功获取到了我们想要的newTransformer方法

image-20250501113214125.png
图片加载失败


往下走,跟进invokeMethod方法(该行中的this.provider.getType()返回的依然是同时实现Type和Templates接口的代理对象)

image-20250501151513931.png
图片加载失败


再跟进invoke方法

image-20250501151611442.png
图片加载失败


便走到了AutowireUtils.ObjectFactoryDelegatingInvocationHandler.invoke()方法中,method是newTrnasFormer方法所以都不会走进if里面

继续往下走

image-20250501151959481.png
图片加载失败


根据poc我们可以知道该处的this.objectFactory是一个代理对象,调用getObject()方法便会走到其InvocationHandler的invoke方法中

跟进

image-20250501152123464.png
图片加载失败


我们可以看到返回的result便是我们心心念念的TemplatesImpl对象

往下走,返回后,return method.invoke(this.objectFactory.getObject(), args);代码其实就是return method.invoke(templates, args);

成功调用TemplatesImpl的newTransformer方法,实现任意类加载

image-20250501152547027.png
图片加载失败


弹出计算器

image-20250501152646186.png
图片加载失败


Spring2

依赖

链子分析

链子如下:

该条链子与Spring1的前半条链子是一样的,入口都是SerializableTypeWrapper$MethodInvokeTypeProvider.readObject

这条链子主要是为了说明ObjectFactoryDelegatingInvocationHandler并不是关键所在,即使它被禁了我们依然有其他的方法可以成功实现任意类加载

这里所用的关键InvocationHandler除了AnnotationInvocationHandler之外就是JdkDynamicAopProxy

我们进该类的invoke方法看一手

从提供的链子可知首先我们要关注的是retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);

跟进invokeJoinpointUsingReflection方法

image-20250501172822787.png
图片加载失败


里面就是简简单单对方法进行了反射调用

所以重点看参数target:target = targetSource.getTarget();

再看targetSource:TargetSource targetSource = this.advised.targetSource;

再跟进targetSource我们可以发现是在AdvisedSupport类里面调用了setTarget方法来进行赋值的

image-20250501173128166.png
图片加载失败


因此我们只需要在调用setTarget的时候将其参数target设为我们的TemplatesImpl对象就可以了,那么最后在invokeJoinpointUsingReflection方法里反射调用的就是我们的TemplatesImpl对象的newTransformer方法了

调试

综上所述,我们的poc如下:

前面的调试都是一样的,这边直接省略,直接跳到调用invokeMethod方法,跟进去

image-20250501173818759.png
图片加载失败


我们便来到了JdkDynamicAopProxy类的invoke方法里

image-20250501174013870.png
图片加载失败


继续往下走

image-20250501174208761.png
图片加载失败


target获取到了我们的TemplatesImpl对象

继续往下走

image-20250501174340265.png
图片加载失败


跟进

image-20250501174410177.png
图片加载失败


方法反射调用,成功弹出计算器

攻击面拓展

学习完这两条链子之后我就思考着要是TemplatesImpl类被禁掉了要怎么办,在网上搜寻了一番后发现没有现成的文章来讲述相关的解决办法,所以打算自己尝试一手

我想的是将该链子与jndi注入联系起来,从而实现远程类加载

思路有了那么接下来就是要找到合适的实现类了,最开始我想到的是JDBcRowSetImpl类,利用它的getDatabaseMetaData()方法

image-20250507191954022.png
图片加载失败


但是嘞由于我们链子前面都是用到了代理,所以也就说明我们所找的这个方法必须是位于接口中的,很遗憾该方法就只位于JDBcRowSetImpl类里面,所以用不了

接下来又到处找实现类,全局搜索lookup方法等等,都没有找到我所需要的

直到突然之间想起了之前看过的一篇文章:http://blog.potatowo.top/2025/03/23/%E8%BD%AF%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%AE%89%E5%85%A8%E8%B5%9B2025%E5%8D%8E%E4%B8%9C%E8%B5%9B%E5%8C%BA%E5%8D%8A%E5%86%B3%E8%B5%9Bwp-web/

在这篇文章里面提到了一个类com.sun.jndi.ldap.LdapAttribute,其有个getAttributeDefinition()方法,其中调用了lookup()

image-20250507192707681.png
图片加载失败


该类继承了BasicAttribute类,跟过去

BasicAttribute类实现了Attribute接口,在这个接口中定义了getAttributeDefinition()方法

image-20250507193003926.png
图片加载失败


很好,一切都对上了,这就是我找了许久的类

重新改一下我们的poc,如下

进行测试,成功弹出计算器

image-20250507193534087.png
图片加载失败


然后后面进行一个调试,发现其实进行了一个远程类加载的相关代码并不是getAttributeDefinition()方法中最明显的lookup函数

这边笔者就不进行调试了,想调试的可以自己去试试,调用栈如下:

总结

spring1和spring2的链子都与动态代理密切相关,建议是先熟练掌握了动态代理之后再来学习这两条链子会比较轻松

对于这两条链的利用肯定不止笔者这里提到的这些,大家有兴趣的也可以自己去找找看

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