温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

java反序列化原理-Demo(二)

发布时间:2020-08-27 18:23:34 来源:网络 阅读:643 作者:wx5b0b88843cb2a 栏目:安全技术

java反序列化原理-Demo(二)

0x00 测试代码以及运行结果

测试代码:

package test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ReflectionPlay implements Serializable{

      public static void main(String[] args) throws Exception {
          new ReflectionPlay().run();
      }

      public void run() throws Exception {
          byte[] ObjectBytes=serialize(getObject());
          deserialize(ObjectBytes);
      }

      //在此方法中返回恶意对象
      public Object getObject() {
          String command = "calc.exe";
          Object firstObject = Runtime.class;
          ReflectionObject[] reflectionChains = {
                  /*
                   *            Object runtime = Class.forName("java.lang.Runtime").getMethod("getRuntime",
                    new Class[] {}).invoke(null);

             Class.forName("java.lang.Runtime")
             .getMethod("exec", String.class)
             .invoke(runtime,"calc.exe");
                   * 
                   * */

                  //调用 Runtime.class 的getMethod方法,寻找 getRuntime方法,得到一个Method对象(getRuntime方法)
                  //等同于 Runtime.class.getMethod("getRuntime",new Class[]{String.class,Class[].class})
                  new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                  //调用 Method 的 invoker 方法可以得到一个Runtime对象
                  // 等同于 method.invoke(null),静态方法不用传入对象
                  new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                  //调用RunTime对象的exec方法,并将 command作为参数执行命令
                  new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})
          };

          return new ReadObject(new ReflectionChains(firstObject, reflectionChains));
      }

      /*
       * 序列化对象到byte数组
       * */
      public byte[] serialize(final Object obj) throws IOException {
          ByteArrayOutputStream out = new ByteArrayOutputStream();
          ObjectOutputStream objOut = new ObjectOutputStream(out);
          objOut.writeObject(obj);
          return out.toByteArray();
      }

      /*
       * 从byte数组中反序列化对象
       * */
      public Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException {
          ByteArrayInputStream in = new ByteArrayInputStream(serialized);
          ObjectInputStream objIn = new ObjectInputStream(in);
          return objIn.readObject();
      }

      /*
      * 一个模拟拥有漏洞的类,主要提供的功能是根据自己的属性中的值来进行反射调用
      * */
      class ReflectionObject implements Serializable{
          private String methodName;
          private Class[] paramTypes;
          private Object[] args;

          public ReflectionObject(String methodName, Class[] paramTypes, Object[] args) {
              this.methodName = methodName;
              this.paramTypes = paramTypes;
              this.args = args;
          }

          //根据  methodName, paramTypes 来寻找对象的方法,利用 args作为参数进行调用
          public Object transform(Object input) throws Exception {
              Class inputClass = input.getClass();
              return inputClass.getMethod(methodName, paramTypes).invoke(input, args);
          }
      }

      /*
      * 一个用来模拟提供恶意代码的类,
      * 主要的功能是将 ReflectionObject进行串联调用,与ReflectionObject一起构成漏洞代码的一部分
      * */
      class ReflectionChains implements Serializable{

          private Object firstObject;
          private ReflectionObject[] reflectionObjects;

          public ReflectionChains(Object firstObject, ReflectionObject[] reflectionObjects) {//ReflectionChains构造方法
              this.firstObject = firstObject;
              this.reflectionObjects = reflectionObjects;
          }

          public Object execute() throws Exception {
              Object concurrentObject = firstObject;
              for (ReflectionObject reflectionObject : reflectionObjects) {
                  concurrentObject = reflectionObject.transform(concurrentObject);
                  System.out.println(concurrentObject);
              }
              return concurrentObject;
          }
      }

      /**
       * 一个等待序列化的类,拥有一个属性和一个重写了的readObject方法
       * 并且在readObject方法中执行了该属性的一个方法
       * */
      class ReadObject implements Serializable {

          private ReflectionChains reflectionChains;

          public ReadObject(ReflectionChains reflectionChains) {
              this.reflectionChains = reflectionChains;
          }
          //当反序列化的时候,这个代码会被调用
          //该方法被调用的时候其属性都是空
          private void readObject(java.io.ObjectInputStream stream)
                  throws IOException, ClassNotFoundException {
              try {
                  //用来模拟当readObject的时候,对自身的属性进行了一些额外的操作
                  reflectionChains= (ReflectionChains) stream.readFields().get("reflectionChains",null);
                  reflectionChains.execute();
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
    }

运行结果:
java反序列化原理-Demo(二)

0x01 测试代码分析

涉及到的类:

类名 是否继承 Serializable 方法名
ReflectionObject 继承 transform
ReflectionChains 继承 execute
ReadObject 继承 readObject(重写)

注:要想产生反序列漏洞,需要重写readObjec方法

代码流程:

  1. serialize(getObject());将一个对象序列化
  2. getObject(); 生成一个ReflectionObject数组,包含三个ReflectionObject对象;然后使用ReflectionObject数组生成一个ReflectionChains对象,继续使用ReflectionChains对象生成一个ReadObject对象

    new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                  //调用 Method 的 invoker 方法可以得到一个Runtime对象
                  // 等同于 method.invoke(null),静态方法不用传入对象
    new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                  //调用RunTime对象的exec方法,并将 command作为参数执行命令
    new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})
    };
    
          return new ReadObject(new ReflectionChains(firstObject, reflectionChains));
  3. deserialize(ObjectBytes);将序列化后的字节反序列化
    将传入的字节进行反序列化,因为传入的字节是一个ReadObjec对象序列化后的,因此在反序列化时需要调用ReadObjec的readObjec方法(该方法已经被重写)
    private void readObject(java.io.ObjectInputStream stream)
                  throws IOException, ClassNotFoundException {
              try {
                  //用来模拟当readObject的时候,对自身的属性进行了一些额外的操作
                  reflectionChains= (ReflectionChains) stream.readFields().get("reflectionChains",null);
                  reflectionChains.execute();
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }

    在这里我们需要注重看java反序列化原理-Demo(二)
    在生成ReadObject对象时传入的参数是ReflectionChains对象,所以这里调用的execute()方法就是ReflectionChains的execute()方法。
    接着分析ReflectionChains的execute()方法:

    public Object execute() throws Exception {
              Object concurrentObject = firstObject;
              for (ReflectionObject reflectionObject : reflectionObjects) {
                  concurrentObject = reflectionObject.transform(concurrentObject);
                  System.out.println(concurrentObject);
              }
              return concurrentObject;
          }

    关键代码:
    java反序列化原理-Demo(二)
    由上面可知生成ReflectionChains对象时传入的参数是ReflectionObject数组,这段代码的含义便是:遍历ReflectionObject数组,每一个元素(ReflectionObject)执行transform方法
    继续分析ReflectionObject的transform方法:

    public Object transform(Object input) throws Exception {
              Class inputClass = input.getClass();  得到input对象是那个类的类名
              return inputClass.getMethod(methodName, paramTypes).invoke(input, args);   通过类名调用该类的某一方法
          }

    java反序列化原理-Demo(二)
    注意:
    通过以上分析可以了解到,进行反序列化的反射链为:
    deserialize(ObjectBytes); 调用ReadObject类的readObject方法;接下来调用ReflectionChains类的execute方法;接下来通过遍历ReflectionObject数组调用ReflectionObject类的transform方法

0x02 核心分析

通过以上代码分析看,最终需要执行的便是以下代码:

 new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                  //调用 Method 的 invoker 方法可以得到一个Runtime对象
                  // 等同于 method.invoke(null),静态方法不用传入对象
new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                  //调用RunTime对象的exec方法,并将 command作为参数执行命令
 new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})
new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),    
等同于执行了:Runtime.class.getMethod("getRuntime",new Class[]{String.class,Class[].class})
new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
等同于执行了:method.invoke(null),静态方法不用传入对象
new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})
RunTime对象的exec方法,并将 command作为参数执行命令

第一个对象执行完成:
Runtime.class.getMethod("getRuntime", new Class[0]);
输出的结果:java.lang.Runtime.getRuntime() 输出的是一个类
第二个对象执行完成:
Runtime.class.getMethod("getRuntime", new Class[0]).invoke(null, new Object[0]);
输出结果:java.lang.Runtime@74a14482 输出的是一个Runtime的对象
第二个对象执行完成:
Runtime t = (Runtime) Runtime.class.getMethod("getRuntime", new Class[0]).invoke(null, new Object[0]);
t.exec("calc.exe")

0x03补充

package test;

import java.lang.reflect.InvocationTargetException;

public class test5 {
    public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
        //Class inputClass = input.getClass();
        //inputClass.getMethod(methodName, paramTypes).invoke(input, args)
        //new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]})

        Class  inputClass1 = Runtime.class.getClass();
        Object concurrentObject1 =  inputClass1.getMethod("getMethod",new Class[]{String.class, Class[].class}).invoke(Runtime.class, new Object[]{"getRuntime", new Class[0]});
        System.out.println(inputClass1.getMethod("getMethod",new Class[]{String.class, Class[].class}).invoke(Runtime.class, new Object[]{"getRuntime", new Class[0]}));
        //public static java.lang.Runtime java.lang.Runtime.getRuntime()

        //new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]})
        Class  inputClass2 =  concurrentObject1.getClass();
        Object concurrentObject2 =  inputClass2.getMethod("invoke",new Class[]{Object.class, Object[].class}).invoke(concurrentObject1, new Object[]{null, new Object[0]});
        System.out.println(concurrentObject2);
        //java.lang.Runtime@3d4eac69

        //new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})
        Class  inputClass3 =  concurrentObject2.getClass();
        Object concurrentObject3 =  inputClass3.getMethod("exec",new Class[]{String.class}).invoke(concurrentObject2,new Object[]{"calc.exe"});
        System.out.println(concurrentObject3);

        /*
         * 对比user类:
         * inputClass.getMethod(methodName, paramTypes).invoke(input, args)
         * 对参数说明:
         * inputClass   user.getClass()
         * methodName 调用的方法名称
         * paramTypes    调用方法的参数类型
         * input    是  inputClass的一个对象  
         * args  调用方法的参数
         * 
         * 函数返回结果:
         * input对象调用methodName的方法(传入的参数是args)返回的结果:比如  void setName(String name){} 返回的是null
         * */

    }
}

参考链接:http://www.freebuf.com/vuls/170344.html

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI