java-commons-collections-3.1反序列化漏洞学习

JAVA反序列化分析思路

从危险函数出发开始寻找不同类的同名函数实现任意方法调用(反射/动态加载字节码)

反射基础

截图

r是一个Runtime实例,c是一个Runtime类,对c使用反射(getMethod)来获取exec方法,然后在r上调用这个方法

poc

1
2
3
4
5
6
7
8
9
10
11

public class ApacheSerialize {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }),
new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc.exe"})
}; //将transformers数组存入ChaniedTransformer这个继承类
Transformer transformerChain = new ChainedTransformer(transformers);

截图


漏洞利用分析

InvokerTransformer(反射利用点)

首先是由InvokerTransformer类中的transform实现了任意方法调用的同时还在构造函数中导致参数可控截图

上面的java反射就相当于

截图

(实际上自己构造的时候在IDE中直接看需要填入的变量还是挺方便的,这里不太好看懂)

如以下语句就可以实现命令执行

截图

将Runtime实例传入transfrom,cls就获取了Runtime类,method获取了Runtime类中的exec方法,然后对Runtime实例使用传入的传输calc

poc如下截图效果是

截图

最后的目标是回到readObject,所以此时要寻找不同的方法来调用transform(视频原话)


MAP

在TransformedMap类里面可以找到调用了transfrom方法的位置

截图

而valueTransformer是TransformedMap构造函数中的一部分,也就是可控截图

如下构造就可以对invokerTransformer调用tansform方法(由于构造函数是保护的,这里用到decorate装饰器)

截图

继续向上寻找发现是TransformedMap的父类的MapEntry类重写了setValue方法

截图

则可以如下,构造一个TransformedMap并且调用setValue即可(让TransformedMap的value为构造的invokertransformer然后通过setValue调用transform)截图

现在则需要一个可以遍历数组的地方并且需要value可控或者不同名的调用了setValue


AnnotationInvocationHandler

pop链的最后一步截图

满足两个if条件后即可调用setvalue

但是这个类是一个default类,需要用反射创建,并且可以看见他的构造函数中的第一个参数是一个注解类(如Override)截图

此时有三个问题:

1.如何通过if判断来进入这个setvalue中

2.setValue貌似不可控

3.Runtime对象本身是不可序列化的,需要通过反射


Runtime实现反射调用

解决Runtime不可序列化的问题
正常情况,由于Runtime对象构造函数是私有的,需要通过getRuntime方法来获取这个对象

截图

上图,通过.class获取了Runtime类,然后getMethod反射获取getRuntime方法,然后调用这个方法,获取r这个Runtime实例化对象,最后反射获取exec方法并且在r上调用

转换为InvokerTransform类中则是

截图

chainedtransformer

截图

这时候通过chainedtransform实现循环调用来使得调用更加简洁

截图


进入if语句

截图

1.通过getKey来获取传入的memberValue中的键是否存在于menberTypes中

截图

如上,反序列化入口获取了type参数也就是下面的Override.class截图

接着获取了这个类中的成员方法,检查poc中传入的键在这个类中的成员方法终会是否存在

截图

则传入一个有成员方法的类并且将key改成他的一个方法名

如Target的value方法截图

截图如上修改即可

2.截图

即检查是否可以强转,这里不需要绕过


实现可控SetValue

正常而言,这里会调用

截图

也就是这里的transform的value不可控截图

也就相当于要把下面的语句换成上面的语句(实现value可控)

截图

ConstantTransformer

截图

其transformer方法直接返回自身的一个变量同时可控
就可以利用它通过Runtime.getRuntime()创建一个Runtime实例

此时即可转换成poc中的


截图


pop链回溯

通过反射实例化了AnnotationInvocationHandler类—>对他的value遍历并且调用setvalue,最终来到transform方法(这一步为了实现可控SetValue借助到ConstantTransformer类)—>在value中,我们恶意构造的chainTransformer类实现命令执行


总结

第一次看java的链子,之前连ctf的java题都没怎么做过,没啥基础,看了点反射跟基本语法就强行看完了,感觉看懂了但是让自己复现还是很困难(语法不清晰),过段时间自己再回来复习一下吧


java-commons-collections-3.1反序列化漏洞学习
http://example.com/2023/09/24/java-commons-collections-3-1反序列化漏洞学习/
作者
z2zQAQ
发布于
2023年9月24日
许可协议