mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
155 lines
8.5 KiB
Markdown
155 lines
8.5 KiB
Markdown
# 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 <https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649> 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<ObjectInputFilter>` 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}}
|