早就想學JAVA安全,但一直無從下手,今天下定決心好好學習,當然以下內容可能會有些許錯誤,小白的煩惱。
序列化與反序列化
Java序列化是指把Java對象轉換為字節序列的過程;而Java反序列化是指把字節序列恢復為Java對象的過程。
序列化分為兩大部分:序列化和反序列化。序列化是這個過程的第一部分,將數據分解成字節流,以便存儲在文件中或在網絡上傳輸。反序列化就是打開字節流并重構對象。對象序列化不僅要將基本數據類型轉換成字節表示,有時還要恢復數據。恢復數據要求有恢復數據的對象實例。
為什么需要序列化與反序列化
我們知道,當兩個進程進行遠程通信時,可以相互發送各種類型的數據,包括文本、圖片、音頻、視頻等, 而這些數據都會以二進制序列的形式在網絡上傳送。那么當兩個Java進程進行通信時,能否實現進程間的對象傳送呢?答案是可以的。如何做到呢?這就需要Java序列化與反序列化了。換句話說,一方面,發送方需要把這個Java對象轉換為字節序列,然后在網絡上傳送;另一方面,接收方需要從字節序列中恢復出Java對象。
當我們明晰了為什么需要Java序列化和反序列化后,我們很自然地會想Java序列化的好處。其好處一是實現了數據的持久化,通過序列化可以把數據永久地保存到硬盤上(通常存放在文件里),二是,利用序列化實現遠程通信,即在網絡上傳送對象的字節序列。
① 想把內存中的對象保存到一個文件中或者數據庫中時候;② 想用套接字在網絡上傳送對象的時候;③ 想通過RMI傳輸對象的時候
為什么會產生安全問題?
只要服務端反序列化數據,客戶端傳遞類的readObject中代碼會自動執行,給予攻擊者在服務器上運行代碼的能力。
幾種常見的序列化和反序列化協議
XML&SOAP
JSON
Protobuf
理解
類比快遞、打包和拆包
有些快遞打包和拆包時有獨特需求、比如易碎朝上,類比重寫writeObject和readObject
實例
Java反序列化的操作,很多是需要開發者深入參與的,所以你會發現大量的庫會實現readObject 、 writeObject 方法,這和php中__wakeup 、__sleep 很少使用是存在鮮明對比的。我在《Java安全漫談 - 06.RMI篇(3)》的最后一部分,講到了classAnnotations ,這次再來說說 objectAnnotation 。Java在序列化時一個對象,將會調用這個對象中的writeObject 方法,參數類型是 ObjectOutputStream ,開發者可以將任何內容寫入這個stream中;反序列化時,會調用 readObject ,開發者也可以從中讀取出前面寫入的內容,并進行處理。
創建一個person類
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
publicclass Person implementsSerializable {
privateString name;
privateint age;
publicPerson(){}
publicPerson(String name,int age){
this.name=name;
this.age=age;
}
@Override
publicString toString() {
return"Person{"+
"name='" + name + "'" +
",'age=" + age +
'}';
}
}
創建一個序列化類
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializationTest {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void main(String[] args) throws Exception{
Person person=new Person("xinyuan",22);
serialize(person);
System.out.println(person);
}
}
創建一個反序列化類
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class UnserializationTest {
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("ser.bin"));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception{
Person person=(Person) unserialize("ser.bin");
System.out.println(person);
}
}
可能的形式
1.入口類的readObject直接調用危險方法
在person類,重寫readObject方法,序列化后,反序列化
2.入口類參數中包含可控類,該類有危險方法,readObject時調用。
3.入口類參數包含可控類,該類又調用其他有危險方法的類,readObject時調用
條件
共同條件 繼承Serializable
入口類 source(重寫readObject 參數類型寬泛 最好jdk自帶) Map
調用鏈 gadget chain 相同名稱,相同類型
執行類 sink(rce ssrf 寫文件等等)
https://github.com/frohoff/ysoserial/