Dieser Beitrag aus unserer Reihe zur OWASP Top 10, den zehn häufigsten Sicherheitslücken in Webapplikationen, beschäftigt sich mit dem Thema der unsicheren Deserialisierung.

In der Informatik wird unter Deserialisierung die Umwandlung einer persistierbaren Byte-Folge (Zusammenstellung von Nullen und Einsen) in den ursprünglichen Zustand eines programmierbaren Objektes im Arbeitsspeicher verstanden. Persistierbare Daten sind Daten in einem nichtflüchtigen Speicher, so dass sie auch über einen Programm- oder Rechnerneustart hinweg noch unverändert vorhanden bleiben. Eine Serialisierung ist demzufolge der umgekehrte Vorgang. Eine (De-)Serialisierung geschieht zum Beispiel, wenn ein Objekt als Byte-Folge über ein Netzwerk übertagen und danach in der ursprünglichen Objektstruktur wieder in eine Datenbank gespeichert werden soll. Unzureichend geprüfte Deserialisierungen können zu Schwachstellen führen, die Remote-Code-Executions ermöglichen, d. h. Angreifern wird hierüber die Möglichkeit gegeben aus der Ferne Änderungen an Systemen durchzuführen und Schadcode auszuführen. Darüber hinaus können Deserialisierungsfehler Angriffsverfahren wie Replay-Angriffe (Senden zuvor aufgezeichneter Daten, um bspw. eine andere Identität vorzutäuschen oder auch manipulierte Objekte mit Schadcode einzuschleusen), Injections oder auch die Ausweitung von Zugriffsberechtigungen zur Folge haben.

Beispielszenarien

Anwendungen oder APIs können verwundbar sein, wenn sie bösartige oder manipulierte Objekte in ihrer Anwendungslogik ohne Prüfung deserialisieren. Dabei werden grundsätzlich zwei Hauptangriffsarten unterschieden:

  • Angreifer versuchen durch Objekt- und Datenstrukturen die Anwendungslogik zu beeinflussen oder Schadcode auszuführen. Betroffene Anwendungen müssen hierfür auf Klassen zugreifen können, deren Verhalten während oder nach der Deserialisierung manipuliert werden kann.

Als Beispiel kann eine fiktive Anwendung basierend auf mehreren Java-Microservices herangezogen werden, die eine Serialisierung des Benutzerstatus vornimmt und diesen Status bei jeder Anfrage hin und her transferiert. Die Java-Standardbibliotheken sind dabei ggf. anfällig für Deserialisierungsangriffe (vgl. CVE-2015-8103 und CVE-2015-4852) und lassen sich mit dem simplen Tool „Java Serial Killer“ leicht manipulieren.

  • Ein weiteres Beispiel für die Ausführung von Schadcode durch die Deserialisierung von nicht vertrauenswürdigem Binärcode, kann sehr einfach mit Hilfe des Python-Moduls „pickle“ nachgestellt werden. Mit dem Aufruf „pickle.loads()“ lässt sich eine unsichere Deserialisierung von Benutzereingaben (hier bspw. aus einer Datei) durchführen:

import pickle

 with open(‘serial’, ‘r’) as f:

    pickle.loads(f.read())

Wenn nun das folgende Objekt in serialisierter Form aus einer Datei eingelesen wird, kann z. B. der Aufruf des Befehls „id“ (hier könnte auch Schadcode stehen) ausgeführt werden:

class VulnPickle(object):

    def __reduce__(self):

        import os

        return (os.system,(“id”,))

  • Existierende Datenstrukturen werden genutzt und deren Inhalt manipuliert, u. a. werden bei dieser Angriffsart Schwachstellen in der Zugriffskontrolle ausgenutzt.

Anschaulich kann ein PHP-Forum betrachtet werden, welches über die PHP-Objekt-Serialisierung einen sogenannten „Super-Cookie“ erzeugt. Dieses Cookie enthält u. a. Angaben zur User-ID, Rolle und einen Passwort-Hash:

a:4:{i:0;i:132;i:1;s:7:”Tom“;i:2;s:8:”Benutzer“;
i:3;s:32:”b6a8b3bea87fe0e05022f8f3c88bc960”;}

Ein Angreifer könnte nun das serialisierte Objekt manipulieren, um sich so administrative Rechte zu verschaffen:

a:4:{i:0;i:1;i:1;s:5:”Jerry“;i:2;s:13:”Administrator“;
i:3;s:32:”b6a8b3bea87fe0e05022f8f3c88bc960”;}

Gegenmaßnahmen

Es sollte darauf geachtet werden, keine serialisierten Objekte aus nicht vertrauenswürdigen Quellen anzunehmen oder möglichst nur serialisierte Datenstrukturen zu akzeptieren, die ausschließlich einfache Datentypen erlauben. Anderenfalls sollten nachstehende Aspekte Berücksichtigung finden:

  • Die Erzeugung bösartiger Objekte oder die Manipulation von Daten in Folge der Deserialisierung kann durch das digitale Signieren von serialisierten Objekten verhindert werden.
  • Es sollte eine strikte Typisierung während der Deserialisierung und bevor Objekte erzeugt werden eingehalten werden. Oftmals wird hierfür nur eine bekannte Menge an Klassen benötigt. Achtung: Diese Maßnahme kann umgangen werden und sollte nicht als einzige Sicherheitshürde eingesetzt werden.
  • Der zuständige Programmcode für die Deserialisierung sollte möglichst isoliert in einer eigenen Umgebung mit geringen Berechtigungen zur Laufzeit ausgeführt werden, z. B. könnte ein hierfür bereitgestellter Microservice eingesetzt werden.
  • Alle Exceptions, z. B. unerwartete Objekt-Typen, die während der Deserialisierung auftreten, sollten protokolliert werden.
  • Ein- und ausgehende Netzwerkaktivitäten von Containern oder Servern, in deren Aufgabenbereich eine Deserialisierung stattfindet, sollten überwacht oder begrenzt
  • Nutzer, die auffällig häufig Deserialisierungen anwenden, sollten überwacht und gemeldet werden.

Zusammenfassung

Die Ausnutzbarkeit von Fehlern in der Deserialisierung ist nicht ganz trivial, da vorhandener Schadcode oftmals manuell an spezifische Gegebenheiten der Webapplikation angepasst werden muss. Einige Hilfswerkzeuge können Schwachstellen in der Deserialisierung aufspüren, dennoch ist meistens eine manuelle Untersuchung des Fundes notwendig. Die Auswirkungen des Angriffsvektors „Unsichere Deserialisierung“ sollten trotzdem nicht unterschätzt werden. Im schlimmsten Fall sind Remote-Code-Executions in verwundbaren Anwendungen durchführbar, welche zu den schwerwiegendsten Bedrohungen in der Informationssicherheit zählen. Wenn nicht unbedingt erforderlich sollte auf die Serialisierung / Deserialisierung von programmierbaren Objekten gänzlich verzichtet und für die Speicherung sowie den Austausch Textformate (XML oder JSON) herangezogen werden.