千家信息网

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

发表于:2025-01-20 作者:千家信息网编辑
千家信息网最后更新 2025年01月20日,java反序列化原理-Demo(二)0x00 测试代码以及运行结果测试代码:package test;import java.io.ByteArrayInputStream;import java.i
千家信息网最后更新 2025年01月20日java反序列化原理-Demo(二)

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

运行结果:

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

    在这里我们需要注重看
    在生成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;      }

    关键代码:

    由上面可知生成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);   通过类名调用该类的某一方法      }


    注意:
    通过以上分析可以了解到,进行反序列化的反射链为:
    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

0