您好,登錄后才能下訂單哦!
怎么解析Java反序列化漏洞,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
Java序列化:就是將內(nèi)存中的Java對象轉(zhuǎn)換為字節(jié)序列的過程,可以理解為對Java對象打個快照。通過序列化,可以方便將Java對象保存在內(nèi)存、文件、數(shù)據(jù)庫等媒介中,也便于在網(wǎng)絡(luò)中傳輸和共享Java對象。
Java反序列化:就是Java序列化的逆過程,將字節(jié)序列恢復(fù)為Java對象的過程。
序列化/反序列化并不是Java語言的獨有特性,像PHP、Python、Ruby等動態(tài)語言也有類似的特性。序列化/反序列化的主要目的是:
1、遠(yuǎn)程過程調(diào)用(RPC):為不同系統(tǒng)或不同進(jìn)程之間提供Java對象數(shù)據(jù)交互;
2、緩存/持久化存儲:可以將Java對象緩存或存儲到本地文件、磁盤、數(shù)據(jù)庫等媒介中;
3、會話tokens:用于HTTP cookies、HTML form表單參數(shù)、API 認(rèn)證tokens等場景的交互數(shù)據(jù)。
在Java中,序列化/反序列化操作主要由 java.io.ObjectOutputStream.writeObject(Object) 方法和 java.io.ObjectInputStream.readObject()方法實現(xiàn);在用戶代碼中,可以通過重寫上述方法實現(xiàn)自定義操作。
Java序列化數(shù)據(jù)格式
參考文檔:https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html
Java對象經(jīng)過序列化后得到的數(shù)據(jù)是個二進(jìn)制流,以固定的魔數(shù)(0xaced)和版本(0x0005)開始;在滲透測試過程中,可以以此來識別應(yīng)用系統(tǒng)中反序列化的入口點。(0xaced 0005的base64編碼以rO0AB開始,通常在Web應(yīng)用系統(tǒng)中傳輸?shù)腏ava序列化數(shù)據(jù)會經(jīng)過base64編碼)。
Java序列化數(shù)據(jù)示例:
00000000: aced 0005 7400 0d48 656c 6c6f 2c20 776f ....t..Hello, wo 00000010: 726c 6421 rld!
在Github上提供了Java對象序列化dump工具,可以對Java對象序列化后的數(shù)據(jù)進(jìn)行解析,具體請參考:https://github.com/NickstaDB/SerializationDumper。例如,將上述二進(jìn)制數(shù)據(jù)解析后的結(jié)果如下:
Java反序列化漏洞產(chǎn)生的原因在于Java應(yīng)用程序接收來自用戶的序列化數(shù)據(jù)并嘗試對其進(jìn)行反序列化;如果攻擊者通過構(gòu)造惡意輸入,讓反序列化過程產(chǎn)生非預(yù)期的對象,將可能導(dǎo)致各種后果,嚴(yán)重時可能造成遠(yuǎn)程代碼執(zhí)行。
Java序列化/反序列化代碼demo
下面代碼演示將一段字符串經(jīng)序列化保存到本地文件中,然后再從文件中恢復(fù)序列化的字符串。
package orz.vuln.poc; import java.io.FileOutputStream; import java.io.ObjectOutputStream; //將String對象序列化后保存到data.ser文件中 public class Serialization { public static void main(String[] args) throws Exception { String text = "Hello, world!"; FileOutputStream fos = new FileOutputStream("D:/data.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(text); oos.close(); fos.close(); } }
序列化后的數(shù)據(jù):
package orz.vuln.poc; import java.io.FileInputStream; import java.io.ObjectInputStream; //將data.ser文件中的數(shù)據(jù)反序列化為Java對象: public class Deserialization { public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("D:/data.ser"); ObjectInputStream ois = new ObjectInputStream(fis); String text = (String) ois.readObject(); fis.close(); ois.close(); System.out.println(text); } }
執(zhí)行結(jié)果:
在這里,可以通過修改本地文件數(shù)據(jù)控制反序列化后的字符串的值;例如,將data.ser修改如下:
00000000: aced 0005 7400 0845 7669 6c54 6578 74 ....t..EvilText
執(zhí)行反序列化代碼,結(jié)果:
更進(jìn)一步Java序列化/反序列化
在實際開發(fā)中,更多是通過實現(xiàn)Serializable接口并重寫readObject()方法對自定義類對象進(jìn)行反序列化,以完成更多操作。如下代碼示例,我們通過自定義Test類,實現(xiàn)了Serializable接口,并重寫readObject()方法,在readObject()方法中,我們自定義輸出字符串“Oops...”和彈出計算器操作。
package orz.vuln.poc; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class DemoCode { public static void main(String[] args) throws Exception { Test test = new Test("calc.exe"); FileOutputStream fos = new FileOutputStream("D:/object.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(test); oos.close(); fos.close(); FileInputStream fis = new FileInputStream("D:/object.ser"); ObjectInputStream ois = new ObjectInputStream(fis); Test test2 = (Test) ois.readObject(); ois.close(); } } class Test implements Serializable { private String cmd; public Test(String cmd) { this.cmd = cmd; } //重寫readObject()方法 private void readObject(java.io.ObjectInputStream in) throws Exception { in.defaultReadObject(); System.out.println("Oops..."); java.lang.Runtime.getRuntime().exec(cmd);//觸發(fā)代碼執(zhí)行,模擬調(diào)用鏈 } }
執(zhí)行結(jié)果:
調(diào)用堆棧如下:
DemoCode [Java Application] orz.vuln.poc.DemoCode at localhost:53445 Thread [main] (Suspended (entry into method exec in Runtime)) Runtime.exec(String) line: 345 Test.readObject(ObjectInputStream) line: 38 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 601 ObjectStreamClass.invokeReadObject(Object, ObjectInputStream) line: 1004 ObjectInputStream.readSerialData(Object, ObjectStreamClass) line: 1891 ObjectInputStream.readOrdinaryObject(boolean) line: 1796 ObjectInputStream.readObject0(boolean) line: 1348 ObjectInputStream.readObject() line: 370 DemoCode.main(String[]) line: 22 D:\Tools\Java\jdk1.7.0_21\jre\bin\javaw.exe (2021年3月17日 下午5:13:24)
從上述結(jié)果可以看出,反序列化將調(diào)用重寫的readObject()方法,執(zhí)行了自定義的字符串輸出和彈出計算器。如果重寫的readObject()方法中可以構(gòu)造出代碼執(zhí)行利用鏈,將存在遠(yuǎn)程代碼執(zhí)行漏洞。當(dāng)然,在實際開發(fā)過程中不可能像上述代碼一樣直接在readObject()方法內(nèi)部寫上java.lang.Runtime.getRuntime().exec(cmd)這種代碼;但是也差不太多,只是實際調(diào)用鏈比較復(fù)雜,通過控制反序列化的輸入數(shù)據(jù),結(jié)合Java反射調(diào)用機(jī)制,尋找可構(gòu)建遠(yuǎn)程代碼執(zhí)行的調(diào)用鏈,動態(tài)調(diào)用java.lang.Runtime.getRuntime().exec()完成代碼執(zhí)行。
下面可以放一個JDK7u21反序列化漏洞的調(diào)用堆棧做個對比,只是調(diào)用過程更加復(fù)雜化:
orz.vuln.poc.JDK7u21Exploit at localhost:53452 Thread [main] (Suspended (entry into method exec in Runtime)) owns: TemplatesImpl (id=46) Runtime.exec(String) line: 345 EvilCodes.<init>() line: 17 NativeConstructorAccessorImpl.newInstance0(Constructor, Object[]) line: not available [native method] NativeConstructorAccessorImpl.newInstance(Object[]) line: 57 DelegatingConstructorAccessorImpl.newInstance(Object[]) line: 45 Constructor<T>.newInstance(Object...) line: 525 Class<T>.newInstance0() line: 374 Class<T>.newInstance() line: 327 TemplatesImpl.getTransletInstance() line: 380 TemplatesImpl.newTransformer() line: 410 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 601 AnnotationInvocationHandler.equalsImpl(Object) line: 197 AnnotationInvocationHandler.invoke(Object, Method, Object[]) line: 59 $Proxy0.equals(Object) line: not available LinkedHashMap<K,V>(HashMap<K,V>).put(K, V) line: 475 LinkedHashSet<E>(HashSet<E>).readObject(ObjectInputStream) line: 309 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 Method.invoke(Object, Object...) line: 601 ObjectStreamClass.invokeReadObject(Object, ObjectInputStream) line: 1004 ObjectInputStream.readSerialData(Object, ObjectStreamClass) line: 1891 ObjectInputStream.readOrdinaryObject(boolean) line: 1796 ObjectInputStream.readObject0(boolean) line: 1348 ObjectInputStream.readObject() line: 370 JDK7u21Exploit.main(String[]) line: 91
Java反序列化漏洞的根源在于ObjectInputStream.readObject()方法在進(jìn)行反序列化時并沒有對生成的對象類型做檢測和限制,并且當(dāng)這種反序列化漏洞存在于一些公共類庫中時,將造成重大影響。例如Apache Commons Collections中實現(xiàn)的一些類可以被反序列化用來實現(xiàn)任意代碼執(zhí)行。而在WebLogic、WebSphere、JBoss、Jenkins、OpenNMS這些應(yīng)用的反序列化漏洞能夠得以利用,就是因為這些應(yīng)用中使用了Apache Commons Collections類庫。這就好像在開啟了ASLR地址隨機(jī)化防御的系統(tǒng)中,出現(xiàn)了一個加載地址固定的共享庫,或者類似于C語言中使用的鏈接庫,當(dāng)這些庫存在漏洞時,將對使用了這些庫的應(yīng)用造成重大影響。
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。
免責(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)容。