
# Writeup und Exploit für installierte App zur System-Privilegienerweiterung auf Android 12 Beta durch CVE-2021-0928, eine `writeToParcel`/`createFromParcel`-Serialisierungsabweichung in `OutputConfiguration`
CVE-2021-0928, writeToParcel/createFromParcel-Serialisierungsfehlanpassung in android.hardware.camera2.params.OutputConfiguration
Dies ist ein Exploit, der diese Schwachstelle zur Privilegienerweiterung von einer installierten Android-App in die Android-Einstellungen-App nutzt (oder in jede andere installierte App, an die die App an ein in AndroidManifest.xml deklariertes <receiver> senden konnte; eine Privilegienerweiterung durch Senden an <activity> war ebenfalls möglich, wird hier jedoch nicht dargestellt)
Ich habe das Problem ursprünglich in Android 12 Developer Preview 3 gefunden
Die in diesem Repository vorhandene Exploit-Version funktioniert auf Android 12 Beta 2 und 3
Die Schwachstelle wurde im ersten offiziellen Android-12-Release behoben
Der folgende Writeup wurde ursprünglich für Google verfasst, um diesen Bericht als vollständige Exploit-Kette zu berücksichtigen
Zum Zeitpunkt der Erstellung war Android 12 nicht in AOSP verfügbar (Android Developer Preview/Beta-Releases sind nicht Open Source)

Der Großteil der IPC-Kommunikation auf Android erfolgt über die Klasse Parcel
Die grundlegende Verwendung von Parcel ist wie folgt:```java Parcel p = Parcel.obtain(); p.writeInt(1); p.writeString("Hello");
Dann wird `Parcel` an einen anderen Prozess [über `Binder`](https://developer.android.com/reference/android/os/IBinder#transact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)) gesendet. Alternativ kann man zum Testen [`p.setDataPosition(0)`](https://developer.android.com/reference/android/os/Parcel#setDataPosition(int)) aufrufen, um den Parcel an den Anfang zurückzuspulen und mit dem Lesen zu beginnen:```java
int a = p.readInt(); // a = 1
String b = p.readString(); // b = "Hello"
Es ist zu beachten, dass Parcel intern eine Position verwaltet, von der aus Lesevorgänge durchgeführt werden. Es liegt in der Verantwortung des Benutzers der Parcel-Klasse sicherzustellen, dass die read*-Methoden mit den zuvor verwendeten write*-Methoden übereinstimmen, andernfalls erfolgen nachfolgende Lesevorgänge von falschen Positionen im Puffer.
Parcel bietet außerdem die Möglichkeit, benutzerdefinierte Objekte zu schreiben. Der bevorzugte Weg hierfür ist die Implementierung des Parcelable-Interfaces.
Hier ist eine Beispielimplementierung des Parcelable-Interfaces (irrelevanter Code entfernt, die WindowContainerTransaction-Klasse wird im Exploit als Teil der Gadget-Kette verwendet, jedoch gibt es daran nichts auszusetzen)```java
package android.window;
public final class WindowContainerTransaction implements Parcelable {
private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>();
private final ArrayList mHierarchyOps = new ArrayList<>();
private WindowContainerTransaction(Parcel in) {
in.readMap(mChanges, null /* loader */);
in.readList(mHierarchyOps, null /* loader */);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeMap(mChanges);
dest.writeList(mHierarchyOps);
}
@NonNull
public static final Creator<WindowContainerTransaction> CREATOR =
new Creator<WindowContainerTransaction>() {
@Override
public WindowContainerTransaction createFromParcel(Parcel in) {
return new WindowContainerTransaction(in);
}
};
}
Wie oben zu sehen ist, wird die Methode `writeToParcel()` beim Schreiben verwendet. Beim Lesen wird dann die Factory-Methode `CREATOR.createFromParcel()` aufgerufen. Es liegt in der Verantwortung der `Parcelable`-Implementierung sicherzustellen, dass `createFromParcel` die gleiche Datenmenge liest, die von `writeToParcel` geschrieben wurde, da sonst alle nachfolgenden Lesevorgänge aus diesem `Parcel` Daten von einer falschen Position lesen.
Eine solche Klasse kann wie folgt in einen `Parcel` geschrieben bzw. daraus gelesen werden:
* Durch direkten Aufruf von `obj.writeToParcel(parcel, 0)` / `obj = WindowContainerTransaction.CREATOR.createFromParcel()`. Dies wird häufig verwendet, wenn der Typ der Klasse bekannt ist, z. B. wenn ein `Parcelable` ein Feld mit einem anderen `Parcelable` hat oder in von AIDL generiertem Code, wenn eine definierte RPC-Methode ein `Parcelable` als Argument hat
* Über `Parcel.writeParcelable`/`readParcelable`. [`writeParcelable`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=1909;drc=58787794eb5c879f6e39ee58c0071b36e337f8e3) schreibt zuerst den Namen der Klasse und ruft dann die Methode `writeToParcel` aus dem `Parcelable`-Interface auf. [`readParcelable`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=3282;drc=58787794eb5c879f6e39ee58c0071b36e337f8e3) liest den geschriebenen Klassennamen, findet die Klasse mit diesem Namen im bereitgestellten `ClassLoader` oder im `BOOTCLASSPATH`, falls null bereitgestellt wurde. Sobald die Klasse gefunden ist, wird ihr statisches Feld `CREATOR` verwendet, um eine [`Parcelable.Creator`](https://developer.android.com/reference/android/os/Parcelable.Creator)-Instanz zu erhalten, die als Factory zum Lesen dieser Klasse dient. Es ist wichtig zu beachten, dass bei Verwendung der Methode `readParcelable` jedes beliebige `Parcelable` gelesen werden kann, das im Klassenpfad verfügbar ist, da der Name des zu erstellenden Objekts aus demselben `Parcel` gelesen wird
* `readParcelable` wird von vielen anderen `Parcel`-Methoden verwendet, z. B. liest `readList` aus dem obigen Beispiel Elemente über `readValue`, die generischste Methode zum Übertragen von Objekten in einem `Parcel`, und eine ihrer Verwendungsarten ist über `readParcelable`. Auch im obigen Beispiel kann das Feld `ArrayList<HierarchyOp> mHierarchyOps` aufgrund der Java-Typ-Erasure tatsächlich beliebige Objekte enthalten, die von Parcel unterstützt werden, nicht nur solche, die mit dem in der generischen Typdeklaration angegebenen Typ kompatibel sind
# `writeToParcel`/`createFromParcel`-Abweichungen
Wie oben erwähnt, liegt es in der Verantwortung der `Parcelable`-Interface-Implementierung sicherzustellen, dass `createFromParcel` die gleiche Datenmenge aus dem `Parcel` liest, die das zugehörige `writeToParcel` zuvor geschrieben hat. Wann immer sich im `BOOTCLASSPATH` ein `Parcelable` befindet, das diesen Vertrag verletzen kann, entsteht eine Schwachstelle, da sie das folgende Szenario ermöglicht: