java-commons-collections-3.1反序列化漏洞学习
JAVA反序列化分析思路
从危险函数出发开始寻找不同类的同名函数实现任意方法调用(反射/动态加载字节码)
反射基础
r是一个Runtime实例,c是一个Runtime类,对c使用反射(getMethod)来获取exec方法,然后在r上调用这个方法
poc
1 |
|
漏洞利用分析
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题都没怎么做过,没啥基础,看了点反射跟基本语法就强行看完了,感觉看懂了但是让自己复现还是很困难(语法不清晰),过段时间自己再回来复习一下吧