© Enkel/Shutterstock.com
Deserialisierungsschwachstellen im Java-Programmcode

Exploiting in Java


Der Stack Overflow ist das am meisten anzutreffende Sicherheitsproblem in der Softwareentwicklung. Bei einem solchen kann ein Angreifer mittels einer zu langen Eingabe einen Programmspeicher zum Überlaufen bringen, sprich über die Grenzen eines Arrays hinausschreiben und so Programme zum Absturz bringen, interne Daten verfälschen und den Programmfluss ändern. In Java gibt es zwar keinen klassischen Stack Overflow, aber ähnliche Deserialisierungsschwachstellen.

(Benutzer-)Daten können Programmcode enthalten, die ein Angreifer beim Stack Overflow zur Codeausführung missbraucht. In der Programmiersprache Java jedoch begegnet uns dieses Problem aufgrund der virtuellen Machinenarchitektur nicht, was allerdings nicht bedeutet, dass man Deserialisierungsschwachstellen nicht mit einem Stack Overflow vergleichen kann. In Analogie dazu kann man setzen, dass hier ebenfalls Daten und Programmcode nicht voneinander separiert werden und ein Teil des weiteren Programmflusses von den zu deserialisieren Daten abhängt. Da Deserialisierung oft auch in Webanwendungen bei der Verarbeitung von Benutzereingaben und Cookies Verwendung findet, stellt sie für Hacker eine interessante Angriffsoberfläche dar.

Wer Deserialisierung sagt, muss auch Serialisierung sagen!

Unter der Serialisierung versteht man die Umwandlung eines Objekts in einen Bytestrom. Ein Java-Objekt und dessen Zustand kann so effizient beispielsweise in einer Datei oder Datenbank gespeichert oder über ein Netzwerk zu einem anderen Rechner übertragen werden. Das Gegenstück zur Serialisierung ist die Deserialisierung, die den Prozess auf der Empfängerseite umkehrt und das ursprüngliche Objekt anhand des Bytestroms, des serialisierten Objekts, rekonstruiert (Abb. 1).

rengstorf_deserialisierung_1.tif_fmt1.jpgAbb. 1: Serialisierungs- und Deserialisierungsprozess

Ein Onlineshop verwendet die Serialisierung zur Sicherung des aktuellen Warenkorbzustands in einer Datei, die vom Benutzer heruntergeladen werden kann, und die Deserialisierung, um anhand einer hochgeladenen Datei den Zustand des Warenkorbs wieder herzustellen.

Vor der Serialisierung muss zunächst die Klasse des Objekts definiert werden. Ein Objekt kann im java.io-Serialisierungs-API [1] nur serialisiert werden, wenn die Klasse das Interface Serializable implementiert (Listing 1, Kommentar 4). In Listing 1 wird die Klasse ShoppingBasket definiert, die aus den Variablen artikel (Listing 1, Kommentar 1), die für den Namen des Artikels im Warenkorb steht, anzahl (Listing 1, Kommentar 2) für die Artikelmenge und preis (Listing 1, Kommentar 3) für den Artikelpreis besteht und den Zustand des Warenkorbs im Onlineshop repräsentiert.

Listing 1: ShoppingBasket.java

import java.io.*; class ShoppingBasket implements Serializable { // (4) String artikel; // (1) int anzahl; // (2) int preis; // (3) // Konstruktor public ShoppingBasket(String artikel, int anzahl, int preis) { this.artikel = artikel; this.preis = preis; this.anzahl = anzahl; System.out.println("ShoppingBasket() Konstruktor."); } public String toString() { return "Warenkorb :"+anzahl+"x"+artikel+" Preis:"+preis+" EUR" ; } }

Serialisierung und Download des Warenkorbs

Der Onlineshop verwendet Serialisierung, um den Zustand des Warenkorbobjekts basket der Klasse ShoppingBasket in einen Bytestrom umzuwandeln, den der Benutzer als Datei herunterladen kann. Das zeigt vereinfacht das Programm DownloadShoppingBasket. Dieses Programm stellt vereinfacht den Download dar, indem es einen Beispielwarenkorb in einer auf der Kommandozeile angegeben Datei speichert.

Listing 2: DownloadShoppingBasket.java

import java.io.*; public class DownloadShoppingBasket { public static void main(String[] args) { ShoppingBasket basket = new ShoppingBasket("iPhone SE", 100, 1000); // (1) try { FileOutputStream file = new FileOutputStream (args[0]); // Erzeugen eines OutputStreams // public ObjectOutputStream(OutputStream out); ObjectOutputStream out = new ObjectOutputStream (file); // Serialisierung des Objekts und speichern in OutputStream bzw. Datei out.writeObject(basket); // (3) // Schließen der Filehandler out.close(); file.close(); System.out.println(basket.toString()); // Ausgabe Warenkorb System.out.println("Shopping Basket downloaded to "+filename); } catch (IOException ex) { System.out.println("Error IOEXception"); } } }

Das Programm erzeugt zunächst einen Beispielwarenkorb in der Variable basket (Listing 2, Kommentar 1). Der Warenkorb enthält als Artikel iPhone SE: 100 Stück zum Stückpreis von 1000 EUR. Zur eigentlichen Serialisierung wird basket der writeObject()-Methode (Listing 1, Kommentar 3) der Klasse java.io.ObjectOutputStream übergeben. Der Konstruktor dieser Klasse verlangt ein Objekt vom Typ OutputStream (Listing 1, Kommentar 2), das angibt, wohin der serialisierte Bytestrom geschrieben wird – in diesem Fall eine Datei, deren Dateiname auf der Kommandozeile bestimmt wird. Bei der Ausführung des Programms wird Folgendes ausgegeben und die Datei mitarbeiter.ser erstellt, die das serialisierte Objekt basket enthält:

$ java DownloadShoppingBasket shopping_basket.ser ShoppingBasket() Konstruktor. Warenkorb :100xiPhone SE Preis:1000 EUR Shopping Basket downloaded to shopping_basket.ser

Zur Analyse der entstandenen Datei gibt das Unix-Tool file den ersten Hinweis darauf, dass das Java API zur Serialisierung verwendet wurde:

$ file shopping_basket.ser shopping_basket.ser: Java serialization data, version 5

Mittels xxd kann man sich die Datei wie mit einem Hexeditor genauer anschauen (Listing 3).

Listing 3

$ xxd shopping_basker.ser 00000000: aced 0005 7372 000e 5368 6f70 7069 6e67 ....sr..Shopping 00000010: 4261 736b 6574 a061 7c96 fd20 4090 0200 Basket.a|.. @... 00000020: 0349 0006 616e 7a61 686c 4900 0570 7265 .I..anzahlI..pre 00000030: 6973 4c00 0761 7274 696b 656c 7400 124c isL..artikelt..L 00000040: 6a61 7661 2f6c 616e 672f 5374 7269 6e67 java/lang/String 00000050: 3b78 7000 0000 6...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang