hacktricks/src/pentesting-web/deserialization/basic-java-deserialization-objectinputstream-readobject.md

12 KiB
Raw Blame History

Basic Java Deserialization with ObjectInputStream readObject

{{#include ../../banners/hacktricks-training.md}}

Σε αυτή την ανάρτηση θα εξηγηθεί ένα παράδειγμα χρησιμοποιώντας java.io.Serializable και γιατί η υπερχείλιση του readObject() μπορεί να είναι εξαιρετικά επικίνδυνη αν το εισερχόμενο ρεύμα ελέγχεται από επιτιθέμενο.

Serializable

Η διεπαφή Java Serializable (java.io.Serializable) είναι μια διεπαφή σήμανσης που οι κλάσεις σας πρέπει να υλοποιούν αν θέλουν να είναι serializable και deserializable. Η σειριοποίηση αντικειμένων Java (γραφή) γίνεται με το ObjectOutputStream και η αποσειριοποίηση (ανάγνωση) γίνεται με το ObjectInputStream.

Υπενθύμιση: Ποιες μέθοδοι καλούνται έμμεσα κατά την αποσειριοποίηση;

  1. readObject() λογική ανάγνωσης συγκεκριμένης κλάσης (αν έχει υλοποιηθεί και είναι ιδιωτική).
  2. readResolve() μπορεί να αντικαταστήσει το αποσειριοποιημένο αντικείμενο με ένα άλλο.
  3. validateObject() μέσω callbacks ObjectInputValidation.
  4. readExternal() για κλάσεις που υλοποιούν Externalizable.
  5. Οι κατασκευαστές δεν εκτελούνται επομένως οι αλυσίδες gadget βασίζονται αποκλειστικά στους προηγούμενους callbacks.

Οποιαδήποτε μέθοδος σε αυτή την αλυσίδα που καταλήγει να καλεί δεδομένα ελεγχόμενα από επιτιθέμενο (εκτέλεση εντολών, αναζητήσεις JNDI, ανακλαστικότητα, κ.λπ.) μετατρέπει τη ρουτίνα αποσειριοποίησης σε gadget RCE.

Ας δούμε ένα παράδειγμα με μια κλάση Person που είναι serializable. Αυτή η κλάση υπερκαλύπτει τη συνάρτηση readObject, έτσι όταν οποιοδήποτε αντικείμενο αυτής της κλάσης είναι deserialized αυτή η συνάρτηση θα εκτελείται.
Στο παράδειγμα, η συνάρτηση readObject της κλάσης Person καλεί τη συνάρτηση eat() του κατοικίδιου του και η συνάρτηση eat() ενός Σκύλου (για κάποιο λόγο) καλεί ένα calc.exe. Θα δούμε πώς να σειριοποιήσουμε και να αποσειριοποιήσουμε ένα αντικείμενο Person για να εκτελέσουμε αυτόν τον υπολογιστή:

Το παρακάτω παράδειγμα είναι από https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649

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");
}
}

Συμπέρασμα (κλασικό σενάριο)

Όπως μπορείτε να δείτε σε αυτό το πολύ βασικό παράδειγμα, η “ευπάθεια” εδώ εμφανίζεται επειδή η μέθοδος readObject() καλεί άλλον κώδικα που ελέγχεται από τον επιτιθέμενο. Σε πραγματικές αλυσίδες gadget, χιλιάδες κλάσεις που περιέχονται σε εξωτερικές βιβλιοθήκες (Commons-Collections, Spring, Groovy, Rome, SnakeYAML, κ.λπ.) μπορούν να καταχραστούν ο επιτιθέμενος χρειάζεται μόνο μία προσβάσιμη gadget για να αποκτήσει εκτέλεση κώδικα.


2023-2025: Τι νέο υπάρχει στις επιθέσεις αποσυμπίεσης Java;

  • 2023 CVE-2023-34040: Η αποσυμπίεση κεφαλίδων σφαλμάτων του Spring-Kafka όταν είναι ενεργοποιημένες οι σημαίες checkDeserExWhen* επέτρεψε την αυθαίρετη κατασκευή gadget από θέματα που δημοσιεύθηκαν από τον επιτιθέμενο. Διορθώθηκε στην 3.0.10 / 2.9.11. ¹
  • 2023 CVE-2023-36480: Η υπόθεση αξιόπιστου διακομιστή του Aerospike Java client παραβιάστηκε οι κακόβουλες απαντήσεις του διακομιστή περιείχαν σειριακά payloads που αποσυμπιέστηκαν από τον client → RCE. ²
  • 2023 CVE-2023-25581: Η ανάλυση του χαρακτηριστικού προφίλ χρήστη του pac4j-core αποδέχθηκε blobs Base64 με πρόθεμα {#sb64} και τα αποσυμπίεσε παρά την ύπαρξη ενός RestrictedObjectInputStream. Αναβάθμιση ≥ 4.0.0.
  • 2023 CVE-2023-4528: Η υπηρεσία JSCAPE MFT Manager (θύρα 10880) αποδέχθηκε Java αντικείμενα κωδικοποιημένα σε XML που οδήγησαν σε RCE ως root/SYSTEM.
  • 2024 Προστέθηκαν πολλές νέες αλυσίδες gadget στο ysoserial-plus(mod) συμπεριλαμβανομένων των κλάσεων Hibernate5, TomcatEmbed και SnakeYAML 2.x που παρακάμπτουν ορισμένα παλιά φίλτρα.

Σύγχρονες μετρήσεις που πρέπει να εφαρμόσετε

  1. JEP 290 / Φιλτράρισμα Σειριοποίησης (Java 9+) Προσθέστε μια λίστα επιτρεπόμενων ή απαγορευμένων κλάσεων:
# Αποδεχθείτε μόνο τα DTO σας και το java.base, απορρίψτε τα πάντα τα άλλα
-Djdk.serialFilter="com.example.dto.*;java.base/*;!*"

Προγραμματιστικό παράδειγμα:

var filter = ObjectInputFilter.Config.createFilter("com.example.dto.*;java.base/*;!*" );
ObjectInputFilter.Config.setSerialFilter(filter);
  1. JEP 415 (Java 17+) Φίλτρα Ειδικών Συγκείμενων χρησιμοποιήστε έναν BinaryOperator<ObjectInputFilter> για να εφαρμόσετε διαφορετικά φίλτρα ανά εκτελεστικό συγκείμενο (π.χ., ανά κλήση RMI, ανά καταναλωτή ουράς μηνυμάτων).
  2. Μην εκθέτετε το raw ObjectInputStream μέσω του δικτύου προτιμήστε κωδικοποιήσεις JSON/Binary χωρίς σημασιολογία εκτέλεσης κώδικα (Jackson μετά την απενεργοποίηση του DefaultTyping, Protobuf, Avro, κ.λπ.).
  3. Περιορισμοί Άμυνας σε Βάθος Ορίστε μέγιστο μήκος πίνακα, βάθος, αναφορές:
-Djdk.serialFilter="maxbytes=16384;maxdepth=5;maxrefs=1000"
  1. Συνεχής σάρωση gadget εκτελέστε εργαλεία όπως gadget-inspector ή serialpwn-cli στο CI σας για να αποτύχει η κατασκευή αν γίνει προσβάσιμο ένα επικίνδυνο gadget.

Ενημερωμένο cheat-sheet εργαλείων (2024)

  • ysoserial-plus.jar κοινότητα fork με > 130 αλυσίδες gadget:
java -jar ysoserial-plus.jar CommonsCollections6 'calc' | base64 -w0
  • marshalsec παραμένει η αναφορά για τη δημιουργία gadget JNDI (LDAP/RMI).
  • gadget-probe γρήγορη ανακάλυψη gadget black-box κατά των δικτυακών υπηρεσιών.
  • SerialSniffer JVMTI agent που εκτυπώνει κάθε κλάση που διαβάζεται από το ObjectInputStream (χρήσιμο για τη δημιουργία φίλτρων).
  • Συμβουλή ανίχνευσης ενεργοποιήστε το -Djdk.serialDebug=true (JDK 22+) για να καταγράψετε τις αποφάσεις φίλτρου και τις απορριφθείσες κλάσεις.

Γρήγορη λίστα ελέγχου για ασφαλείς υλοποιήσεις readObject()

  1. Κάντε τη μέθοδο private και προσθέστε την αναγνώριση @Serial (βοηθά στην στατική ανάλυση).
  2. Ποτέ μην καλείτε μεθόδους που παρέχονται από τον χρήστη ή μην εκτελείτε I/O στη μέθοδο μόνο διαβάστε πεδία.
  3. Εάν απαιτείται επικύρωση, εκτελέστε την μετά την αποσυμπίεση, εκτός της readObject().
  4. Προτιμήστε να υλοποιήσετε το Externalizable και να κάνετε ρητές αναγνώσεις πεδίων αντί για προεπιλεγμένη σειριοποίηση.
  5. Εγγραφείτε σε ένα σκληρυμένο ObjectInputFilter ακόμη και για εσωτερικές υπηρεσίες (σχεδίαση ανθεκτική σε παραβιάσεις).

Αναφορές

  1. Spring Security Advisory CVE-2023-34040 Java Deserialization in Spring-Kafka (Αυγ 2023)
  2. GitHub Security Lab GHSL-2023-044: Unsafe Deserialization in Aerospike Java Client (Ιουλ 2023)

{{#include ../../banners/hacktricks-training.md}}