blueredix logo
info vuln-class-deserialization

Unsichere Deserialisierung, verständlich erklärt

Liest eine Anwendung strukturierte Daten aus dem Netz und kann der Angreifer diese Daten formen, kann aus "Lesen" still und leise "fremden Code ausführen" werden. Die Bug-Klasse hinter Dutzenden RCEs.

Worum es geht

Viele Anwendungen schicken strukturierte Daten übers Netz und “lesen sie auf der Gegenseite zurück”. Das Fachwort dafür ist deserialisieren. Stammen die Daten aus dem internen Verkehr (Cache, Queue, Service-zu-Service-Aufruf), ist das unproblematisch. Kommen die Daten aus einer Quelle, die der Angreifer beeinflussen kann (Cookie, HTTP-Header, JSON Web Token, Request-Body), wird die Art, wie die Anwendung sie zurückliest, zur Sicherheitsfrage.

Der Kern: in vielen Sprachen ist “Deserialisieren” nicht bloß “Felder füllen”. Der Reader instanziiert Klassen, ruft Konstruktoren auf, führt Hooks wie readObject oder __wakeup aus. Kann der Angreifer wählen, welche Klassen instanziiert werden, sucht er solche, deren Konstruktor Seiteneffekte auslöst: Dateien öffnen, Shell-Befehle ausführen, Code nachladen. Eine Kette solcher Klassen heißt “Gadget Chain”, und solche Ketten existieren für nahezu jedes verbreitete Sprach-Runtime.

Wo das vorkommt

Die am häufigsten betroffenen Sprachen und Serialisierer:

  • Java. Java ist die Sprache mit der größten Anzahl dokumentierter Gadget-Chains. ObjectInputStream.readObject() plus Apache Commons Collections oder eines von Dutzenden anderen Bibliotheken hat Breaches bei PayPal, Cisco, Jenkins, WebLogic, Confluence und mehr verursacht.
  • Python pickle. Die Dokumentation sagt explizit “Daten aus nicht-vertrauenswürdigen Quellen niemals unpicklen”. Trotzdem taucht pickle in API-Endpoints, Caches und Message-Queues regelmäßig auf.
  • .NET BinaryFormatter, NetDataContractSerializer, LosFormatter. Microsoft hat alle drei aus Sicherheitsgründen formal deprecated. ASP.NET-ViewState ist die größte Install-Basis.
  • PHP unserialize(). Dieselbe Gadget-Chain-Dynamik, greift PHP-Frameworks (Laravel, Symfony, WordPress) über die Klassen an, die Composers Autoloader bereitstellt.
  • Ruby Marshal.load und YAML.load mit Defaults. Rails-CVEs alle paar Jahre gehen darauf zurück.
  • Node.js ist per Default meist sicher (JSON hat dieses Problem nicht), einige unglückliche Bibliotheken (node-serialize, ältere serialize-javascript-Versionen) reproduzieren es jedoch.

Was der Angreifer tut

Sobald ein Deserialisierungs-Bug erreichbar ist, ist der Rest kurz:

  1. Runtime identifizieren (oft in Fehlerseiten oder Response-Headern sichtbar).
  2. Eine veröffentlichte Gadget-Chain für die Runtime plus die im Classpath sichtbaren Bibliotheken auswählen. ysoserial (Java) und marshalsec (Java) sind die kanonischen Werkzeuge; Pendants gibt es für .NET (ysoserial.net) und PHP (PHPGGC).
  3. Payload mit einem Einzeiler erzeugen, der den gewünschten Befehl ausführt.
  4. Payload an der Deserialisierungsstelle einspielen (Cookie, JWT, HTTP-Body, RMI-Port, JMX-Endpoint).
  5. Reverse-Shell empfangen.

Schwere für deserialisierungs-getriebene Remote-Code-Execution liegt typischerweise bei 9,8 von 10. EPSS-Werte bekannter öffentlicher Chains bleiben jahrelang hoch, dasselbe Exploit-Binary funktioniert gegen jede verwundbare Instanz.

Wie das behoben wird

Empfohlen: nicht-vertrauenswürdige Daten gar nicht in Formaten deserialisieren, die beliebige Klassen erlauben.

Ersetzen Sie die gefährlichen Serialisierer durch sichere Alternativen:

Gefährlich Sichere Alternative
Java ObjectInputStream JSON (Jackson strikt), Protobuf, Avro
Python pickle JSON, msgpack mit strikten Typen
.NET BinaryFormatter System.Text.Json, MessagePack-CSharp
PHP unserialize JSON via json_decode()
Ruby YAML.load YAML.safe_load, JSON

JSON ist kein Allheilmittel: auch in Jackson und Gson gibt es “polymorphic deserialization”-Muster, die das Risiko der Klasseninstanziierung zurückbringen. Strikte Konfiguration verwenden (kein enableDefaultTyping, kein @JsonTypeInfo mit Klassennamen-Auflösung).

Lässt sich der Serialisierer wirklich nicht ersetzen (Legacy-Protokoll, das Sie nicht kontrollieren), helfen geschichtete Verteidigungen:

  • HMAC-Signatur des Blobs mit einem Server-Secret, und alles ablehnen, was nicht validiert. Der Angreifer kann die Bytes nicht mehr verändern.
  • Klassen-Instanziierung beschränken. Javas ObjectInputFilter (seit JDK 9) erlaubt eine exakte Allowlist akzeptierter Klassen.
  • Jede Library im Classpath patchen, die eine bekannte Gadget-Chain hat (apache-commons-collections, spring-core, snakeyaml, ehcache und so weiter).

Wie blueredix Deserialisierungs-Befunde meldet

Der Scanner meldet CVEs in Drittanbieter-Software, deren Beschreibung Deserialisierung nennt, und führt Templates aus, die bekannte verwundbare Endpoints (Spring Cloud Gateway, Atlassian, WebLogic) prüfen, sofern ein spezifischer Exploit gut verstanden ist. Befunde verlinken auf die jeweilige CVE und zurück auf Wie man eine CVE liest.

Wir versuchen nicht, Deserialisierungs-Payloads gegen Ihre Anwendung einzuspielen. Das ist Penetration-Testing-Territorium.

Mehr dazu