从反射链的构造看JAVA反序列化
http://www.freebuf.com/news/150872.html
构造反射链:Transformer类,源码解释为从一个类转化为另一个类。其中的transform()为执行转换方法。
ConstantTransformer,invokerTransformer,ChainedTransformer和TransformedMap继承了Transformer类。利用它们来构造反序列化payload,原理为:
ConstantTransformer类通过transform转换得到内部类的对象类型,如参数是Runtime.class时,经ConstantTransformer类执行后返回java.lang.Runtime
invokerTransformer类,通过反射创建新的对象实例。其中transform方法定义为:
这个transform(Object input) 中使用Java反射机制调用了input对象的一个方法,而该方法名是实例化InvokerTransformer类时传入的iMethodName成员变量:
也就是说这段反射代码中的调用的方法名和Class对象均可控。于是,我们可以构造一个恶意的Transformer链,借用InvokerTransformer.transform()执行任意命令。
method.invoke(input,iargs)意思是,执行input对象的method方法,参数是iargs。
举例:构造一个对象,并且调用transform对象,如下图所示:
查看method变量的值如下图所示:
cls变量获取到的是传递进来的input的对象值,此处input传递的是Runtime的对象,下面两行代码要反射Runtime的getRuntime方法,iMethodName表示要得到的方法名称,iParamTypes表示方法中所使用的参数类型的数组。
执行invoke方法,因为是反射getRuntime()方法,参数为空,所以iArgs的值可以为空。成功的反射出了Runtime.getRuntime()的方法,然而如果要执行任意代码的化,还需要有exec代码段,全部应该是Runtime.getRuntime().exec(“calc.exe”)。
构造payload
此时已经获得了GetRuntime()的Method对象,如果要执行exec(“calc.exe”),还需要进行一次invoke反射的过程,将GetRuntime()反射成对象,因为只有对象才能调用exec函数。因此我们根据上面构造出下面的代码段,如下图:
上图中,构造出tran2的方法,配置invoke的参数都为null,利用tran2.transform(run),反射invoke方法,过程与上文中一样,此处直接看输出了:
此处已经是Runtime类了,继续构造exec(“calc.exe”)代码段,如下图所示:
由此构造成功payload。
以上是构造反射链,执行反射链用到ChainedTransformer。
利用for循环,对传入的transformers[i]运行transform方法,就是把上文的步骤利用一个for循环整合在了一起。
现在我构造一个以数组为主的反射链进行弹窗,代码段如下图所示:
构造出了chain方法之后,还需要调用transform方法,至于传入的对象会被很快覆盖掉,所以input的类型可以任意。
TransformedMap类
这个类调用了ChainedTransformer类中的transform方法。transform函数调用在TransformedMap类的setvalue方法中。
只要我们控制valueTransformer的值为ChainTransformer对象就可以执行反射链了,找到他的赋值地点,如下图所示:
当TransformedMap内的key 或者value发生变化时,就会触发相应的Transformer的transform()方法。另外,还可以使用Transformer数组构造成ChainedTransformer。当触发时,ChainedTransformer可以按顺序调用一系列的变换。
payload测试代码:
InvokerTransformer.transform()执行任意命令,测试代码如下:
这样,这段恶意代码本质上就是利用反射调用Runtime() 执行了一段系统命令,作用等同于:
也就是说,一个精心构造的TransformedMap,在其任意键值被修改时,可以触发变换,从而执行任意命令。