溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

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

發(fā)布時間:2020-08-27 18:23:34 來源:網(wǎng)絡(luò) 閱讀:666 作者:wx5b0b88843cb2a 欄目:安全技術(shù)

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

0x00 測試代碼以及運行結(jié)果

測試代碼:

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");
                   * 
                   * */

                  //調(diào)用 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]}),
                  //調(diào)用 Method 的 invoker 方法可以得到一個Runtime對象
                  // 等同于 method.invoke(null),靜態(tài)方法不用傳入對象
                  new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                  //調(diào)用RunTime對象的exec方法,并將 command作為參數(shù)執(zhí)行命令
                  new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})
          };

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

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

      /*
       * 從byte數(shù)組中反序列化對象
       * */
      public Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException {
          ByteArrayInputStream in = new ByteArrayInputStream(serialized);
          ObjectInputStream objIn = new ObjectInputStream(in);
          return objIn.readObject();
      }

      /*
      * 一個模擬擁有漏洞的類,主要提供的功能是根據(jù)自己的屬性中的值來進(jìn)行反射調(diào)用
      * */
      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;
          }

          //根據(jù)  methodName, paramTypes 來尋找對象的方法,利用 args作為參數(shù)進(jìn)行調(diào)用
          public Object transform(Object input) throws Exception {
              Class inputClass = input.getClass();
              return inputClass.getMethod(methodName, paramTypes).invoke(input, args);
          }
      }

      /*
      * 一個用來模擬提供惡意代碼的類,
      * 主要的功能是將 ReflectionObject進(jìn)行串聯(lián)調(diào)用,與ReflectionObject一起構(gòu)成漏洞代碼的一部分
      * */
      class ReflectionChains implements Serializable{

          private Object firstObject;
          private ReflectionObject[] reflectionObjects;

          public ReflectionChains(Object firstObject, ReflectionObject[] reflectionObjects) {//ReflectionChains構(gòu)造方法
              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方法中執(zhí)行了該屬性的一個方法
       * */
      class ReadObject implements Serializable {

          private ReflectionChains reflectionChains;

          public ReadObject(ReflectionChains reflectionChains) {
              this.reflectionChains = reflectionChains;
          }
          //當(dāng)反序列化的時候,這個代碼會被調(diào)用
          //該方法被調(diào)用的時候其屬性都是空
          private void readObject(java.io.ObjectInputStream stream)
                  throws IOException, ClassNotFoundException {
              try {
                  //用來模擬當(dāng)readObject的時候,對自身的屬性進(jìn)行了一些額外的操作
                  reflectionChains= (ReflectionChains) stream.readFields().get("reflectionChains",null);
                  reflectionChains.execute();
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
    }

運行結(jié)果:
java反序列化原理-Demo(二)

0x01 測試代碼分析

涉及到的類:

類名 是否繼承 Serializable 方法名
ReflectionObject 繼承 transform
ReflectionChains 繼承 execute
ReadObject 繼承 readObject(重寫)

注:要想產(chǎn)生反序列漏洞,需要重寫readObjec方法

代碼流程:

  1. serialize(getObject());將一個對象序列化
  2. getObject(); 生成一個ReflectionObject數(shù)組,包含三個ReflectionObject對象;然后使用ReflectionObject數(shù)組生成一個ReflectionChains對象,繼續(xù)使用ReflectionChains對象生成一個ReadObject對象

    new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                  //調(diào)用 Method 的 invoker 方法可以得到一個Runtime對象
                  // 等同于 method.invoke(null),靜態(tài)方法不用傳入對象
    new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                  //調(diào)用RunTime對象的exec方法,并將 command作為參數(shù)執(zhí)行命令
    new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})
    };
    
          return new ReadObject(new ReflectionChains(firstObject, reflectionChains));
  3. deserialize(ObjectBytes);將序列化后的字節(jié)反序列化
    將傳入的字節(jié)進(jìn)行反序列化,因為傳入的字節(jié)是一個ReadObjec對象序列化后的,因此在反序列化時需要調(diào)用ReadObjec的readObjec方法(該方法已經(jīng)被重寫)
    private void readObject(java.io.ObjectInputStream stream)
                  throws IOException, ClassNotFoundException {
              try {
                  //用來模擬當(dāng)readObject的時候,對自身的屬性進(jìn)行了一些額外的操作
                  reflectionChains= (ReflectionChains) stream.readFields().get("reflectionChains",null);
                  reflectionChains.execute();
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }

    在這里我們需要注重看java反序列化原理-Demo(二)
    在生成ReadObject對象時傳入的參數(shù)是ReflectionChains對象,所以這里調(diào)用的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;
          }

    關(guān)鍵代碼:
    java反序列化原理-Demo(二)
    由上面可知生成ReflectionChains對象時傳入的參數(shù)是ReflectionObject數(shù)組,這段代碼的含義便是:遍歷ReflectionObject數(shù)組,每一個元素(ReflectionObject)執(zhí)行transform方法
    繼續(xù)分析ReflectionObject的transform方法:

    public Object transform(Object input) throws Exception {
              Class inputClass = input.getClass();  得到input對象是那個類的類名
              return inputClass.getMethod(methodName, paramTypes).invoke(input, args);   通過類名調(diào)用該類的某一方法
          }

    java反序列化原理-Demo(二)
    注意:
    通過以上分析可以了解到,進(jìn)行反序列化的反射鏈為:
    deserialize(ObjectBytes); 調(diào)用ReadObject類的readObject方法;接下來調(diào)用ReflectionChains類的execute方法;接下來通過遍歷ReflectionObject數(shù)組調(diào)用ReflectionObject類的transform方法

0x02 核心分析

通過以上代碼分析看,最終需要執(zhí)行的便是以下代碼:

 new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                  //調(diào)用 Method 的 invoker 方法可以得到一個Runtime對象
                  // 等同于 method.invoke(null),靜態(tài)方法不用傳入對象
new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                  //調(diào)用RunTime對象的exec方法,并將 command作為參數(shù)執(zhí)行命令
 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]}),    
等同于執(zhí)行了: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]}),
等同于執(zhí)行了:method.invoke(null),靜態(tài)方法不用傳入對象
new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})
RunTime對象的exec方法,并將 command作為參數(shù)執(zhí)行命令

第一個對象執(zhí)行完成:
Runtime.class.getMethod("getRuntime", new Class[0]);
輸出的結(jié)果:java.lang.Runtime.getRuntime() 輸出的是一個類
第二個對象執(zhí)行完成:
Runtime.class.getMethod("getRuntime", new Class[0]).invoke(null, new Object[0]);
輸出結(jié)果:java.lang.Runtime@74a14482 輸出的是一個Runtime的對象
第二個對象執(zhí)行完成:
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)
         * 對參數(shù)說明:
         * inputClass   user.getClass()
         * methodName 調(diào)用的方法名稱
         * paramTypes    調(diào)用方法的參數(shù)類型
         * input    是  inputClass的一個對象  
         * args  調(diào)用方法的參數(shù)
         * 
         * 函數(shù)返回結(jié)果:
         * input對象調(diào)用methodName的方法(傳入的參數(shù)是args)返回的結(jié)果:比如  void setName(String name){} 返回的是null
         * */

    }
}

參考鏈接:http://www.freebuf.com/vuls/170344.html

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI