今天我想从构造反射链的从无到有到被利用来谈谈java的反序列化漏洞,从反射的最开始到执行payload,一个从无到有的过程,首先我们介绍一下Transformer类。
打开org.apache.commons.collections.Transformer类,可以看到源码中对该类的解释是从一个对象变为另一个对象,如下图所示:
从上图我们可以看到,里面有一个transform方法,通过描述我们理解为执行转变的方法,下面用一个简单的例子,解释一下这个类的作用,如下图所示:
当输入Runtime.class时,transform方法中输出了类的类型,如上图中红线处所示,当我需要转变对象时,相应的操作应该在transform方法当中。
我们查找有哪些类使用了Transformer接口,有如下几个类分别是ConstantTransformer,invokerTransformer,ChainedTransformer,TransformedMap。
下面我们利用以上的三个类一边构造出反序列漏洞的payload一边看他们的运作原理。
通过查看源码,我们看到该类使用了Transformer的接口,重写了transformer的方法,如下图所示:
上面两幅图,可以看出transform返回的是iConstant的变量,iConstant的变量必定在ConstantTransformer(Object)方法中被赋值。
下面举个例子详细看使用,根据上图中的代码,如下图所示:
此时根据源码,我需要查看返回的iConstant对象类型,在源码中设置断点,开启debug运行,运行结果如下图:
上图中可以看到,内部构造出Runtime的对象类型。
打开invokerTransformer查看源码的解释,可知是通过反射创建一个新的对象实例,如下图所示:
看到也使用了Transformer的接口,查看其transform方和和构造方法如下图所示:
上图中所示,构造函数会将iMethodName和iParamType的值传递进来,在transform方法中通过反射的方法,得到了这个方法的对象,最后返回的是Method对象。
使用举例,根据上述源码构造一个对象,并且调用transform对象,如下图所示:
在源码中设置断点,开启debug模式,进行分析,如下图所示:
参数传递进来,继续跟踪到transform函数当中,如下图所示:
继续跟踪查看method变量的值如下图所示:
这里解释一下下图中三行代码的意思:
cls变量获取到的是传递进来的input的对象值,此处input传递的是Runtime的对象,下面两行代码要反射Runtime的getRuntime方法,iMethodName表示要得到的方法名称,iParamTypes表示方法中所使用的参数类型的数组。
此处的iMethodName需要Mehtod对象,因此此处是getMethod,因此iParamTypes对应的是getMethod对象的参数类型集合,getMethod方法文档如下图所示:
通过查阅官方文档,我们知道了参数应该是String.class和Class[].class
继续往下执行invoke方法,因为是反射getRuntime()方法,参数为空,所以iArgs的值可以为空,回到主程序代码可以发现为null,如下图所示:
执行完毕之后,输出如下图所示:
成功的反射出了Runtime.getRuntime()的方法,然而如果要执行任意代码的化,还需要有exec代码段,全部应该是Runtime.getRuntime().exec(“calc.exe”)。
此时我们已经获得了GetRuntime()的Method对象,如果要执行exec(“calc.exe”),我们还需要进行一次invoke反射的过程,因此我们根据上面构造出下面的代码段,如下图:
上图中,构造出tran2的方法,配置invoke的参数都为null,利用tran2.transform(run),反射invoke方法,过程与上文中一样,此处直接看输出了:
此处已经是Runtime类了,继续构造exec(“calc.exe”)代码段,如下图所示:
重复上面的步骤,运行代码如下图所示:
成功弹窗,以上是构造反射链的过程,那么如何去让反射链执行呢,我们来看一下ChainedTransformer这个类,我觉得从名称上已经很能说明问题了,反射链,我们细细看一下这个类。
看一下这个类的源码解释,如下图所示:
没有很特别的地方,是Commons Collections中的类,继续往下看:
这里很有意思啊,和上文中的InvokerTransformer如出一辙,利用for循环,对传入的transformers[i]运行transform方法,这里无非就是把我们上文的步骤利用一个for循环整合在了一起,现在我构造一个以数组为主的反射链进行弹窗,代码段如下图所示:
构造出了chain方法之后,还需要调用transform方法,至于传入的对象会被很快覆盖掉,所以input的类型可以任意。
执行如下图所示:
执行成功。
下面就要去寻找类了,寻找到调用了ChainedTransformer类中的transform方法的类,这个类叫TransformedMap,从名称来看就非常的相似,找到他的setValue方法,如下图所示:
看到了吗,只要我们控制valueTransformer的值为ChainTransformer对象就可以执行反射链了,找到他的赋值地点,如下图所示:
从以上两幅图可以看出,valueTransformer变量是可控的,只要在decorate方法中赋值即可,我们给出下面的代码段:
利用decorate为valueTransformer赋值,然后在最后一行触发了setValue方法,其他的都是为了满足这两个条件形成,执行截图如下:
从以上分析我们可以得出,java反序列化漏洞,只要反射链构造合适,我们可以执行任意的java代码。