Java序列化反序列化原理及漏洞解決方案
Java序列化
Java 提供了一種對象序列化的機(jī)制,該機(jī)制中,一個(gè)對象可以被表示為一個(gè)字節(jié)序列,該字節(jié)序列包括該對象的數(shù)據(jù)、有關(guān)對象的類型的信息和存儲在對象中數(shù)據(jù)的類型。
Java反序列化
反序列化就是將字節(jié)序列恢復(fù)為Java對象的過程
整個(gè)過程都是 Java 虛擬機(jī)(JVM)獨(dú)立的,也就是說,在一個(gè)平臺上序列化的對象可以在另一個(gè)完全不同的平臺上反序列化該對象,因此可以實(shí)現(xiàn)多平臺之間的通信、對象持久化存儲,主要有如下幾個(gè)應(yīng)用場景。
HTTP:多平臺之間的通信,管理等
RMI:是 Java 的一組擁護(hù)開發(fā)分布式應(yīng)用程序的 API,實(shí)現(xiàn)了不同操作系統(tǒng)之間程序的方法調(diào)用。值得注意的是,RMI 的傳輸 100% 基于反序列化,Java RMI 的默認(rèn)端口是1099端口。
JMX:JMX 是一套標(biāo)準(zhǔn)的代理和服務(wù),用戶可以在任何 Java 應(yīng)用程序中使用這些代理和服務(wù)實(shí)現(xiàn)管理,中間件軟件 WebLogic 的管理頁面就是基于 JMX 開發(fā)的,而 JBoss 則整個(gè)系統(tǒng)都基于 JMX 構(gòu)架。
系列化反序列化基礎(chǔ)
序列化和反序列化本身并不存在問題。但當(dāng)輸入的反序列化的數(shù)據(jù)可被用戶控制,那么攻擊者即可通過構(gòu)造惡意輸入,讓反序列化產(chǎn)生非預(yù)期的對象,在此過程中執(zhí)行構(gòu)造的任意代碼。
一個(gè)類的對象能夠序列化的成功需要兩個(gè)條件
- 該類必須實(shí)現(xiàn) java.io.Serializable 接口
- 該類的所有屬性必須是可序列化的。如果有一個(gè)屬性不是可序列化的,則該屬性必須注明是短暫的。
漏洞基本原理
簡單的反序列化Demo
首先定義對象類Persion,包含兩個(gè)參數(shù)
public class implements java.io.Serializable{ public String name; public int age; public void info(){ System.out.println("Name:"+this.name+";nAge:"+this.age); } }
在主類中聲明對象,并且將對象序列化為二進(jìn)制文件,將其存儲到硬盤中
import java.io.*; public class Main{ public static void main(String [] args){ 將對象序列化為二進(jìn)制文件 Persion p = new Persion(); p.name = "Joner"; p.age = 18; try { //打開一個(gè)文件輸入流 FileOutputStream fileOut = new FileOutputStream("D:\test\test.db"); //建立對象輸入流 ObjectOutputStream out = new ObjectOutputStream(fileOut); //輸出反序列化對象 out.writeObject(p); out.close(); fileOut.close(); System.out.printf("保存成功"); }catch(IOException i){ i.printStackTrace(); } }
進(jìn)行反序列化
import java.io.*; public class Main{ public static void main(String [] args){ /*從二進(jìn)制文件中提取對象*/ Persion persion = null; try{ FileInputStream fileInputStream = new FileInputStream("D:\test\test.db"); //建立對象輸入流 ObjectInputStream inputStream = new ObjectInputStream(fileInputStream); persion = (Persion) inputStream.readObject(); inputStream.close(); fileInputStream.close(); }catch (ClassNotFoundException c){ System.out.println("對象未找到"); c.printStackTrace(); return; } catch (FileNotFoundException e) { e.printStackTrace(); return; } catch (IOException e) { e.printStackTrace(); return; } System.out.println("反序列化對象......."); System.out.println("Name:"+persion.name); System.out.println("Age:"+persion.age); } }
查看test.db文件的內(nèi)容可以看見如下內(nèi)容
其中 AC ED 00 05 是java 序列化內(nèi)容的特征,其中00 05 是版本信息,base64編碼后為ro0AB
反序列化漏洞Demo
在上面的Demo中可以看到,進(jìn)行反序列化時(shí)會(huì)調(diào)用readObject()方法,如果readObject方法書寫不當(dāng)就會(huì)引發(fā)漏洞。
import java.io.*; public class Main{ public static void main(String [] args)throws Exception{ Unsafeclass unsafeclass = new Unsafeclass(); unsafeclass.name = "hhhhh"; FileOutputStream fileOutputStream = new FileOutputStream("object"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); //將對象寫入object文件 objectOutputStream.writeObject(unsafeclass); objectOutputStream.close(); //從文件中反序列化對象 FileInputStream fileInputStream = new FileInputStream("object"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); //恢復(fù)對象 Unsafeclass objectFormDisk = (Unsafeclass)objectInputStream.readObject(); System.out.println(objectFormDisk.name); objectOutputStream.close(); } } class Unsafeclass implements Serializable{ public String name; //重寫readObject()方法 private void readObject(java.io.ObjectInputStream inputStream ) throws IOException , ClassNotFoundException{ //執(zhí)行默認(rèn)的readObdect()方法 inputStream.defaultReadObject(); //執(zhí)行打開計(jì)算器命令 Runtime.getRuntime().exec("calc.exe"); } }
程序運(yùn)行過程為:
- UnsafeClass類背序列化進(jìn)入object文件
- 從object文件中恢復(fù)對象
- 調(diào)用被恢復(fù)對象的readObject()方法
- 命令被執(zhí)行
這樣看感覺并不會(huì)有人會(huì)這樣寫readobject()這個(gè)方法,而且一些成熟的框架都會(huì)有防范反序列化的方法,但仍有很大比例的反序列化漏洞,這主要是使用了不安全的庫造成的。上面只是介紹了簡單的Java反序列化過程,接下來會(huì)有一篇文章介紹反序列化漏洞檢測方法以及復(fù)現(xiàn)一些經(jīng)典反序列化漏洞。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
基于Spring Cloud Zookeeper實(shí)現(xiàn)服務(wù)注冊與發(fā)現(xiàn)
這篇文章主要介紹了基于Spring Cloud Zookeeper實(shí)現(xiàn)服務(wù)注冊與發(fā)現(xiàn),幫助大家更好的理解和學(xué)習(xí)spring框架,感興趣的朋友可以了解下2020-11-11java實(shí)現(xiàn)輸出字符串中第一個(gè)出現(xiàn)不重復(fù)的字符詳解
這篇文章主要介紹了java實(shí)現(xiàn)輸出字符串中第一個(gè)出現(xiàn)不重復(fù)的字符詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04Java中的InputStreamReader和OutputStreamWriter源碼分析_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
本文通過示例代碼給大家解析了Java中的InputStreamReader和OutputStreamWriter知識,需要的的朋友參考下吧2017-05-05一篇文章帶你了解Maven的坐標(biāo)概念以及依賴管理
這篇文章主要為大家介紹了Maven的坐標(biāo)概念以及依賴管理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-01-01SpringBoot打jar包遇到的xml文件丟失的解決方案
這篇文章主要介紹了SpringBoot打jar包遇到的xml文件丟失的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09