Anwendungen – Apps, Fachartikel

Java Serialization

Java Serialization

Serialisierung (engl. Serialization) ist eine insbesondere bei Java verwendete Technik, mit der Objekte in eine Byte-Sequenz umgewandelt werden, um diese persistent auf eine Festplatte oder in eine Datenbank zu schreiben bzw. für den Datenaustausch über Netzwerke zu verwenden. Ein Angreifer kann eine Applikation, die ungeprüfte und entsprechend präparierte serialisierte Objekte akzeptiert, dazu bringen, beliebigen Code mit den Rechten der Anwendung auszuführen. Zu solchen Angriffen existieren mittlerweile bekannte Exploits und es ist davon auszugehen, dass sich diverse Akteure nun darauf konzentrieren werden, diese auszunutzen. Der Artikel beschreibt die Angriffstechnik und zeigt Gegenmaßnahmen auf.

Die Serialisierungs-Funktionalität wird von Java-Softwareentwickeln häufig verwendet, um Daten zwischen Java-Programmen (z.B. auf ThickClients und Java Servern) oder Webapplikationen (z.B. ViewState) zu übertragen. Der umgekehrte Prozess, also das Auslesen der Byte-Sequenz und das Umwandeln in ein lauffähiges Objekt nennt man Deserialisierung engl. deserialization. Eine Klasse, die serialisierbar sein soll, muss das Java Interface java.io.serializable implementieren. Hierbei handelt es sich um ein Marker Interface, das der Klasse, die dieses implementiert, das Serialisierungs-Verhalten übermittelt. Java bietet eine große API unter der java.io Package an, die (De)Serialisierung behandelt.

•  java.io.serializable
•  java.io.Externalizable
•  ObjectInputStream
•  and ObjectOutputStream etc.

Die in der Vergangenheit häufig gefundenen Schwachstellen in serialisierter Kommunikation werden verursacht, wenn Entwickler annehmen, dass die serialisierten Objekte von einem vertrauensvollen Client bzw. Server kommen. Besonders häufig ist das der Fall, wenn beide Programme von demselben Entwickler oder derselben Firma entwickelt wurden.

Als Beispiel hierfür soll ein in Java implementiertes „Client-Server“-Programm dienen. Hier sendet der Client ein serialisiertes Payload-Objekt über ein TCP-Stream zum Server, dieser deserialisiert das Objekt und gibt die Attribute des Objektes aus.

TcpServer.java

Der Server öffnet den TCP-Port 9999 und erwartet, dass ein Client sich mit ihm verbindet und ein serialisiertes Objekt sendet. Dieses wird per Type-Cast auf ein Object gecastet und dessen Attribute ausgegeben.

TcpClient.java

Hier wird ein Objekt vom Typ TcpPayload instanziiert und dem TcpServer.java per TCP gesendet.

Problembeschreibung

Häufig gehen Entwickler davon aus, dass die serialisierten Objekte Attribute enthalten, denen sie beim deserialisieren vertrauen können, da sie (angeblich) von einem bekannten Client entstammen. Es ist jedoch nicht nur möglich als Angreifer einen eigenen Client zu programmieren, auch ist die Manipulation der Datenübertragung durch einen „Man-in-the-Middle“-Angriff denkbar. Hierzu muss lediglich eine Klassendefinition des übertragenen serialisierten Objektes vorliegen, um den übertragenen serialisierten Bytestrom als „Man-in-the-Middle“ zu deserialisieren, die Attribute zu manipulieren und wieder serialisiert an den eigentlichen Empfänger zu leiten. Denkbar wäre etwa, dass ein String, der ein Attribut eines übertragenen Objektes ist, serverseitig verwendet wird, um SQL-Befehle auszuführen. Um hier eine mögliche SQL-Injection Schwachstelle anzugreifen, muss dieser String von Angreifer manipuliert werden können.

<next>

Manipulation von Objekten

Das Tool (tcpproxy)  erlaubt den Penetrationstestern der GAI NetConsult die Manipulation von serialisierten Objekten während der Datenübertragung. Hierfür wird ein simpler TCP-Proxy verwendet, der anhand der Java X-Stream Bibliothek die übertragenen Objekte als XML-Struktur darstellt.

Über die Verbindung von zwei Instanzen des tcpproxys ist es möglich, die XML-Struktur durch andere Tools wie etwa BurpSuite zu schleifen. Hier wird die XML-Repräsentation des Objektes als menschenlesbar dargestellt. Die Attribute können nach Belieben manipuliert und anschließend wieder durch den tcpproxy geschleift und zu serialisierten Objekten umgewandelt werden.

Da tcpproxy auf Python2 basiert, für die XML-Darstellung aber die Java-Bibliothek X-Stream verwendet werden soll, muss tcpproxy mit jython gestartet werden. Diese auf der JVM basierte Python-Runtime erlaubt es, innerhalb von üblichem Python-Programmcode auch Java-Objekte zu verwenden.

Tcpproxy wandelt die XML-Struktur mit X-Stream um und sendet das serialisierte Objekt weiter an den TcpServer:

Mögliche Angriffsszenarien

Hinsichtlich der potentiellen Angriffsszenarien verhalten sich übertragene Attribute wie üblicher Nutzerinput. Dieser muss in allen Fällen validiert und als potentiell nicht vertrauenswürdig behandelt werden. Sollten diese Attribute also in SQL-Statements oder z.B. bei einer Kommandoausführung verwendet werden, müssen Prepared Statements oder Escaping verwendet werden, um eventuelle SQL-Injection oder Command-Injection-Angriffe zu verhindern.

Ende letzten Jahres entpuppte sich die Technologie der netzwerkseitigen Java Serialisierung als hochgefährliche generische Schwachstelle. Des Weiteren entpuppte sich die Java Serialisierung als hochgefährliche Technologie, die generisch zur Remote Code Execution missbraucht  werden kann. Diese Deserialisierungsschwachstelle betrifft nicht nur selbst entwickelte Java Programme die Deserialisierung verwenden, sondern auch Standardprogramme wie WebLogic, WebSphere, Jenkins oder OpenNMS waren davon betroffen.

Problematisch ist hier, dass Java alle Klassen deserialisieren kann, die sich im Klassenpfad befinden. Die Klassen müssen also nicht in das Programm importiert sein. Das bedeutet beim oben aufgeführten Programmcode, dass wenn der ObjectInputStream ein anderes serialisiertes Object wie TcpPayload enthält, wird die Java Runtime trotzdem versuchen, diesen Bytestrom mit der Funktion readObject() zu deserialisieren und zu einem lauffähigen Objekt umzuwandeln. Anzumerken ist hier, dass der anschließende Type Cast auf TcpPayload nicht ausgeführt wird, wenn das Objekt nicht vom Typ TcpPayload oder einer Subklasse ist. Um den Angriff durchzuführen, ist der Type Cast aber nicht notwendig.

 

Die Schwachstelle ergibt sich aus der Funktion readObject() und der Möglichkeit alle im Java Classpath enthaltenen Klassen einzuschleusen. Es gibt Klassen die in weit verbreiteten Javaprojekten und Bibliotheken enthalten sind, die sehr häufig im Classpath abgelegt sind. Einige dieser Klassen können so missbraucht werden, dass auf dem betroffenen System beliebige Systemkommandos ausgeführt werden können. Hier handelt es sich z.B. um:

• BeanShell1 [org.beanshell:bsh:2.0b5]
• CommonsBeanutilsCollectionsLogging1 [commons-beanutils:commons-beanutils:1.9.2, commons-collections:commons-collections:3.1, commons-logging:commons-logging:1.2
• CommonsCollections1 [commons-collections:commons-collections:3.1]
• CommonsCollections2 [org.apache.commons:commons-collections4:4.0]
• CommonsCollections3 [commons-collections:commons-collections:3.1]
• CommonsCollections4 [org.apache.commons:commons-collections4:4.0]
• Groovy1 [org.codehaus.groovy:groovy:2.3.9]
• Spring1 [org.springframework:spring-core:4.1.4.RELEASE, org.springframework:spring-beans:4.1.4.RELEASE]

All diese Klassen haben eines gemeinsam: Sie haben die Standardfunktion readObject() mit einer eigenen überschrieben. Problematisch ist der Umstand, dass man diese modifizierte Funktion durch InvocationHandler  Systemkommandos zur Ausführung bringen kann. Anbei ist der Beispielcode [2], der verwendet werden kann, wenn er in serialisierter Form an den Server gesendet zur Ausführung kommt.

Für diesen Angriff wurde das Tool ysoserial entwickelt. Mithilfe dieses Programms kann ein Angreifer eine der Klassen, die missbraucht werden kann, mit einem Kommando versehen, instanziieren und serialisiert an den verwundbaren Server senden.

Schutzmaßnahmen

Dieses Sicherheitsproblem ist leider ein altbekanntes, da viele Softwareentwickler noch immer davon ausgehen, dass es sich bei serialisierten Objekten um eine vertrauenswürdige Datenquelle handelt. Zwar werden die oben aufgeführten Klassen als sogenannte Gadgets für die Code-Ausführung missbraucht, das Problem ist jedoch, dass nicht validierter Input in die JVM geladen wird. Auch das Löschen der betroffenen Bibliotheken, wenn diese nicht verwendet werden, ist keine gute Lösung, da man damit rechnen muss, dass sich in anderen Klassen weitere Möglichkeiten für Gadgets verbergen, die eventuell noch nicht veröffentlich wurden. Zwar gibt es Möglichkeiten mit JVM-Agenten die Deserialisierung von Objekten zu „whitelisten “, jedoch sollte dies nur als Notlösung verstanden werden.

Wenn möglich, sollte von der Technologie der serialisierten Java Objekte gänzlich abgesehen werden. Sollten Sie auf ihren Systemen ein JMX Servlet, OpenNMS, WebSphere betreiben, sollte sichergestellt werden, dass die jeweiligen Ports für serialisierte Kommunikation deaktiviert wurden.
 

Autor: Jon Barg, GAI NetConsult GmbH

 

Referenzen

foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/
github.com/ickerwx/tcpproxy
github.com/kantega/notsoserial
www.studytonight.com/java/serialization-and-deserialization.php
github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java