# Basic Java Deserialization with ObjectInputStream readObject {{#include ../../banners/hacktricks-training.md}} Bu POST'ta `java.io.Serializable` kullanarak bir örnek açıklanacak **ve neden `readObject()`'ın geçerli bir akış saldırgan kontrolündeyse son derece tehlikeli olabileceği** anlatılacak. ## Serializable Java `Serializable` arayüzü (`java.io.Serializable`), sınıflarınızın **serileştirilmesi** ve **serileştirilmesinin geri alınması** için uygulaması gereken bir işaretçi arayüzüdür. Java nesne serileştirmesi (yazma) [`ObjectOutputStream`](http://tutorials.jenkov.com/java-io/objectoutputstream.html) ile yapılır ve serileştirilmenin geri alınması (okuma) [`ObjectInputStream`](http://tutorials.jenkov.com/java-io/objectinputstream.html) ile yapılır. ### Hatırlatma: Serileştirilmenin geri alınması sırasında hangi yöntemler örtük olarak çağrılır? 1. `readObject()` – sınıfa özgü okuma mantığı (eğer uygulanmışsa ve *özel* ise). 2. `readResolve()` – serileştirilen nesneyi başka bir nesne ile değiştirebilir. 3. `validateObject()` – `ObjectInputValidation` geri çağırmaları aracılığıyla. 4. `readExternal()` – `Externalizable` uygulayan sınıflar için. 5. Yapıcılar **çalıştırılmaz** – bu nedenle gadget zincirleri yalnızca önceki geri çağırmalara dayanır. O zincirdeki herhangi bir yöntem, saldırgan kontrolündeki verileri (komut yürütme, JNDI aramaları, yansıma vb.) çağırırsa, serileştirme rutini bir RCE gadget'ına dönüşür. **Serileştirilebilir** olan bir **Person** sınıfı ile bir örneğe bakalım. Bu sınıf **readObject** fonksiyonunu **aşırı yükler**, böylece bu **sınıfın** **herhangi bir nesnesi** **serileştirildiğinde** bu **fonksiyon** **çalıştırılacaktır**.\ Örnekte, Person sınıfının **readObject** fonksiyonu, evcil hayvanının `eat()` fonksiyonunu çağırır ve bir Köpek'in `eat()` fonksiyonu (bir sebepten dolayı) **calc.exe**'yi çağırır. **Bu hesap makinesini çalıştırmak için bir Person nesnesini nasıl serileştirip serileştireceğimizi göreceğiz:** **Aşağıdaki örnek adresinden alınmıştır.** ```java import java.io.Serializable; import java.io.*; public class TestDeserialization { interface Animal { public void eat(); } //Class must implements Serializable to be serializable public static class Cat implements Animal,Serializable { @Override public void eat() { System.out.println("cat eat fish"); } } //Class must implements Serializable to be serializable public static class Dog implements Animal,Serializable { @Override public void eat() { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { e.printStackTrace(); } System.out.println("dog eat bone"); } } //Class must implements Serializable to be serializable public static class Person implements Serializable { private Animal pet; public Person(Animal pet){ this.pet = pet; } //readObject implementation, will call the readObject from ObjectInputStream and then call pet.eat() private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { pet = (Animal) stream.readObject(); pet.eat(); } } public static void GeneratePayload(Object instance, String file) throws Exception { //Serialize the constructed payload and write it to the file File f = new File(file); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f)); out.writeObject(instance); out.flush(); out.close(); } public static void payloadTest(String file) throws Exception { //Read the written payload and deserialize it ObjectInputStream in = new ObjectInputStream(new FileInputStream(file)); Object obj = in.readObject(); System.out.println(obj); in.close(); } public static void main(String[] args) throws Exception { // Example to call Person with a Dog Animal animal = new Dog(); Person person = new Person(animal); GeneratePayload(person,"test.ser"); payloadTest("test.ser"); // Example to call Person with a Cat //Animal animal = new Cat(); //Person person = new Person(animal); //GeneratePayload(person,"test.ser"); //payloadTest("test.ser"); } } ``` ### Sonuç (klasik senaryo) Bu çok temel örnekte görüldüğü gibi, buradaki “zayıflık” **readObject()** metodunun **başka bir saldırgan kontrolündeki kodu çağırmasından** kaynaklanmaktadır. Gerçek dünyadaki gadget zincirlerinde, dış kütüphanelerde (Commons-Collections, Spring, Groovy, Rome, SnakeYAML, vb.) bulunan binlerce sınıf kötüye kullanılabilir – saldırganın yalnızca *bir* erişilebilir gadget'a ihtiyacı vardır. --- ## 2023-2025: Java deserialization saldırılarında neler yeni? * 2023 – CVE-2023-34040: `checkDeserExWhen*` bayrakları etkinleştirildiğinde Spring-Kafka hata kayıt başlıklarının deserialization'ı, saldırgan tarafından yayımlanan konulardan keyfi gadget inşasına izin verdi. 3.0.10 / 2.9.11'de düzeltildi. ¹ * 2023 – CVE-2023-36480: Aerospike Java istemcisi güvenilir sunucu varsayımı kırıldı – kötü niyetli sunucu yanıtları, istemci tarafından deserialized edilen serileştirilmiş yükler içeriyordu → RCE. ² * 2023 – CVE-2023-25581: `pac4j-core` kullanıcı profili öznitelik ayrıştırması `{#sb64}` ile başlayan Base64 blob'larını kabul etti ve `RestrictedObjectInputStream` olmasına rağmen bunları deserialized etti. 4.0.0 veya üstü sürüme yükseltin. * 2023 – CVE-2023-4528: JSCAPE MFT Manager Service (port 10880) XML kodlu Java nesnelerini kabul etti ve bu da root/SYSTEM olarak RCE'ye yol açtı. * 2024 – Eski filtreleri atlayan Hibernate5, TomcatEmbed ve SnakeYAML 2.x sınıflarını içeren birden fazla yeni gadget zinciri ysoserial-plus(mod) eklendi. ## Uygulamanız gereken modern önlemler 1. **JEP 290 / Serileştirme Filtreleme (Java 9+)** *Sınıfların bir izin listesi veya yasak listesi ekleyin:* ```bash # Sadece DTO'larınızı ve java.base'ı kabul edin, diğer her şeyi reddedin -Djdk.serialFilter="com.example.dto.*;java.base/*;!*" ``` Programatik örnek: ```java var filter = ObjectInputFilter.Config.createFilter("com.example.dto.*;java.base/*;!*" ); ObjectInputFilter.Config.setSerialFilter(filter); ``` 2. **JEP 415 (Java 17+) Bağlama Özel Filtre Fabrikaları** – her yürütme bağlamı için (örneğin, her RMI çağrısı, her mesaj kuyruğu tüketicisi) farklı filtreler uygulamak için bir `BinaryOperator` kullanın. 3. **Ham `ObjectInputStream`'i ağda açmayın** – kod yürütme anlamı taşımayan JSON/Binary kodlamalarını tercih edin (Jackson `DefaultTyping` devre dışı bırakıldıktan sonra, Protobuf, Avro, vb.). 4. **Derinlikte Savunma sınırları** – maksimum dizi uzunluğu, derinlik, referanslar ayarlayın: ```bash -Djdk.serialFilter="maxbytes=16384;maxdepth=5;maxrefs=1000" ``` 5. **Sürekli gadget taraması** – tehlikeli bir gadget erişilebilir hale gelirse inşaatı başarısız kılmak için CI'nizde `gadget-inspector` veya `serialpwn-cli` gibi araçları çalıştırın. ## Güncellenmiş araçlar kılavuzu (2024) * `ysoserial-plus.jar` – > 130 gadget zinciri içeren topluluk çatallaması: ```bash java -jar ysoserial-plus.jar CommonsCollections6 'calc' | base64 -w0 ``` * `marshalsec` – JNDI gadget üretimi için hala referans. * `gadget-probe` – ağ hizmetlerine karşı hızlı siyah kutu gadget keşfi. * `SerialSniffer` – `ObjectInputStream` tarafından okunan her sınıfı yazdıran JVMTI ajanı (filtreler oluşturmak için yararlıdır). * **Tespit ipucu** – filtre kararlarını ve reddedilen sınıfları günlüğe kaydetmek için `-Djdk.serialDebug=true` (JDK 22+) etkinleştirin. ## Güvenli `readObject()` uygulamaları için hızlı kontrol listesi 1. Metodu `private` yapın ve `@Serial` anotasyonunu ekleyin (statik analize yardımcı olur). 2. Kullanıcı tarafından sağlanan metodları asla çağırmayın veya metod içinde I/O gerçekleştirmeyin – yalnızca alanları okuyun. 3. Doğrulama gerekiyorsa, deserialization'dan **sonra**, `readObject()` dışında gerçekleştirin. 4. Varsayılan serileştirme yerine `Externalizable` uygulamayı tercih edin ve açık alan okumaları yapın. 5. İç hizmetler için bile sertleştirilmiş bir `ObjectInputFilter` kaydedin (uzlaşma dayanıklı tasarım). ## Referanslar 1. Spring Security Advisory – CVE-2023-34040 Java Deserialization in Spring-Kafka (Ağu 2023) 2. GitHub Security Lab – GHSL-2023-044: Unsafe Deserialization in Aerospike Java Client (Tem 2023) {{#include ../../banners/hacktricks-training.md}}