Unsichere Deserialisierung

OWASP-Kategorie: MASVS-CODE: Codequalität

Übersicht

Beim Speichern oder Übertragen großer Mengen von Java-Objektdaten ist es oft effizienter, die Daten zuerst zu serialisieren. Die Daten werden dann von der empfangenden Anwendung, Aktivität oder dem Provider, der die Daten verarbeitet, deserialisiert. Unter normalen Umständen werden Daten ohne Eingreifen des Nutzers serialisiert und dann deserialisiert. Die Vertrauensbeziehung zwischen dem Deserialisierungsprozess und dem beabsichtigten Objekt kann jedoch von einem böswilligen Akteur missbraucht werden, der beispielsweise serialisierte Objekte abfangen und ändern könnte. Dadurch könnte der böswillige Akteur Angriffe wie Denial-of-Service (DoS), Rechteausweitung und Remote-Codeausführung (RCE) durchführen.

Die Serializable Klasse ist eine gängige Methode zum Verwalten der Serialisierung. Android hat jedoch eine eigene Klasse für die Serialisierung namens Parcel. Mit der Parcel Klasse können Objektdaten in einen Byte Stream serialisiert und mithilfe der Parcelable Schnittstelle in ein Parcel-Objekt gepackt werden. Dadurch kann das Parcel-Objekt effizienter übertragen oder gespeichert werden.

Die Verwendung der Klasse Parcel sollte jedoch sorgfältig überlegt werden, da sie als hocheffizienter IPC-Übertragungsmechanismus gedacht ist, aber nicht zum Speichern serialisierter Objekte im lokalen persistenten Speicher verwendet werden sollte, da dies zu Problemen mit der Datenkompatibilität oder zu Datenverlust führen kann. Wenn die Daten gelesen werden müssen, kann die Schnittstelle Parcelable verwendet werden, um das Parcel zu deserialisieren und wieder in Objektdaten umzuwandeln.

Es gibt drei Hauptvektoren für die Ausnutzung der Deserialisierung in Android:

  • Ausnutzung der falschen Annahme eines Entwicklers, dass die Deserialisierung von Objekten aus einem benutzerdefinierten Klassentyp sicher ist. In Wirklichkeit kann jedes Objekt, das von einer beliebigen Klasse stammt, potenziell durch schädliche Inhalte ersetzt werden, die im schlimmsten Fall die Classloader derselben oder anderer Anwendungen beeinträchtigen können. Diese Beeinträchtigung erfolgt in Form der Einfügung gefährlicher Werte, die je nach Zweck der Klasse beispielsweise zu Datenexfiltration oder Kontoübernahme führen können.
  • Ausnutzung von Deserialisierungsmethoden, die von Natur aus als unsicher gelten (z. B. CVE-2023-35669, ein Fehler zur lokalen Rechteausweitung, der die Einfügung von beliebigem JavaScript-Code über einen Deserialisierungsvektor für Deep-Links ermöglichte)
  • Ausnutzung von Fehlern in der Anwendungslogik (z. B. CVE-2023-20963, ein Fehler zur lokalen Rechteausweitung, der es einer App ermöglichte, Code in einer privilegierten Umgebung herunterzuladen und auszuführen, und zwar über einen Fehler in der Android-Paketlogik von WorkSource).

Auswirkungen

Jede Anwendung, die nicht vertrauenswürdige oder schädliche serialisierte Daten deserialisiert, kann anfällig für Angriffe zur Remote-Codeausführung oder Denial-of-Service-Angriffe sein.

Risiko: Deserialisierung nicht vertrauenswürdiger Eingaben

Ein Angreifer kann die fehlende Paketprüfung in der Anwendungslogik ausnutzen, um beliebige Objekte einzufügen, die nach der Deserialisierung die Anwendung zwingen könnten, schädlichen Code auszuführen, was zu Denial-of-Service (DoS), Rechteausweitung und Remote-Codeausführung (RCE) führen kann.

Diese Arten von Angriffen können subtil sein. Eine Anwendung kann beispielsweise eine Absicht enthalten, die nur einen Parameter erwartet, der nach der Validierung deserialisiert wird. Wenn ein Angreifer einen zweiten, unerwarteten schädlichen zusätzlichen Parameter zusammen mit dem erwarteten Parameter sendet, werden alle eingefügten Datenobjekte deserialisiert, da die Absicht die Extras als Bundle behandelt. Ein böswilliger Nutzer kann dieses Verhalten ausnutzen, um Objektdaten einzufügen, die nach der Deserialisierung zu RCE, Datenkompromittierung oder Datenverlust führen können.

Gegenmaßnahmen

Gehen Sie grundsätzlich davon aus, dass alle serialisierten Daten nicht vertrauenswürdig und potenziell schädlich sind. Um die Integrität serialisierter Daten zu gewährleisten, führen Sie Überprüfungen der Daten durch, um sicherzustellen, dass sie der richtigen Klasse und dem richtigen Format entsprechen, die von der Anwendung erwartet werden.

Eine mögliche Lösung besteht darin, das Look-Ahead-Muster für die java.io.ObjectInputStream Bibliothek zu implementieren. Durch Ändern des für die Deserialisierung verantwortlichen Codes können Sie sicherstellen, dass nur eine explizit angegebene Menge von Klassen innerhalb der Absicht deserialisiert wird.

Ab Android 13 (API-Level 33) wurden mehrere Methoden in der Klasse Intent aktualisiert, die als sicherere Alternativen zu älteren und jetzt veralteten Methoden für die Verarbeitung von Paketen gelten. Diese neuen typsicheren Methoden wie getParcelableExtra(java.lang.String, java.lang.Class) und getParcelableArrayListExtra(java.lang.String, java.lang.Class) führen Datentypüberprüfungen durch, um Schwachstellen durch Typabweichungen zu erkennen, die zum Absturz von Anwendungen führen und möglicherweise zur Durchführung von Angriffen zur Rechteausweitung ausgenutzt werden können, z. B. CVE-2021-0928.

Das folgende Beispiel zeigt, wie eine sichere Version der Klasse Parcel implementiert werden kann:

Angenommen, die Klasse UserParcelable implementiert Parcelable und erstellt eine Instanz von Nutzerdaten, die dann in ein Parcel-Objekt geschrieben werden. Die folgende typsichere Methode von readParcelable kann dann verwendet werden, um das serialisierte Paket zu lesen:

Kotlin

val parcel = Parcel.obtain()
val userParcelable = parcel.readParcelable(UserParcelable::class.java.classLoader)

Java

Parcel parcel = Parcel.obtain();
UserParcelable userParcelable = parcel.readParcelable(UserParcelable.class, UserParcelable.CREATOR);

Beachten Sie im obigen Java-Beispiel die Verwendung von UserParcelable.CREATOR in der Methode. Dieser erforderliche Parameter gibt der Methode readParcelable den erwarteten Typ an und ist leistungsstärker als die jetzt veraltete Version der Methode readParcelable.

Spezifische Risiken

In diesem Abschnitt werden Risiken zusammengefasst, die nicht standardmäßige Gegenmaßnahmen erfordern oder auf einer bestimmten SDK-Ebene gemindert wurden und hier der Vollständigkeit halber aufgeführt sind.

Risiko: Unerwünschte Objektdeserialisierung

Durch die Implementierung der Schnittstelle Serializable in einer Klasse werden automatisch alle Untertypen der angegebenen Klasse zur Implementierung der Schnittstelle gezwungen. In diesem Szenario erben einige Objekte die oben genannte Schnittstelle, was bedeutet, dass bestimmte Objekte, die nicht deserialisiert werden sollen, trotzdem verarbeitet werden. Dadurch kann die Angriffsfläche unbeabsichtigt vergrößert werden.

Gegenmaßnahmen

Wenn eine Klasse die Serializable Schnittstelle erbt, sollte die readObject Methode gemäß den OWASP-Richtlinien wie folgt implementiert werden, um zu vermeiden, dass eine Reihe von Objekten in der Klasse deserialisiert werden kann:

Kotlin

@Throws(IOException::class)
private final fun readObject(in: ObjectInputStream) {
    throw IOException("Cannot be deserialized")
}

Java

private final void readObject(ObjectInputStream in) throws java.io.IOException {
    throw new java.io.IOException("Cannot be deserialized");
}

Ressourcen