
Exploit für CVE-2022-20452, Privilegieneskalation auf Android von einer installierten App zur System-App (oder einer anderen App) über LazyValue unter Verwendung von Parcel nach recycle()
Android 13 führt viele Verbesserungen ein, um den Parcel-Serialisierungsmechanismus abzusichern.
Das ist großartig – es beseitigt definitiv viele Schwachstellen oder macht sie nicht ausnutzbar. Außerdem beschreiben sie, wie mein früherer Exploit, der es Apps ermöglichte, ihren Code in andere Apps (einschließlich System-Apps) zu laden, geknackt wurde.
Aber jetzt bin ich mit einem neuen Exploit zurück, der dasselbe erreicht, wenn auch auf andere Weise. Er nutzt die folgenden Schwachstellen aus, die während der genannten Parcel-Härtung eingeführt wurden:
![Screenshot einer Anwendung, die Text anzeigt. Titel: LeakValue. Haupttext: Created 6 ValueLeaker-s. Locking ActivityTaskManagerService. Locked ActivityTaskManagerService. Unlocking ActivityTaskManagerService. Unlocked ActivityTaskManagerService. leakedBinders=[android.os.BinderProxy@f06702e]. Leaked interface: android.app.IApplicationThread. Requesting code execution. Shellcode has been executed in uid=1000 pid=6904 packageName=com.android.settings uid=1000(system) gid=1000(system) groups=1000(system),1007(log),1065(reserved_disk),1077(external_storage),3001(net_bt_admin),3002(net_bt),3003(inet),3007(net_bw_acct),9997(everybody) context=u:r:system_app:s0. Am unteren Bildschirmrand befinden sich zwei Schaltflächen: START und MANUAL TESTING](Screenshot_20220723-081920.png)
(Auch logcat von der App-Ausführung, die Ausnutzung ist in den Protokollen laut)
Parcel- und Parcelable-Mismatch-FehlerDie Parcel-Klasse von Android ist die Grundlage der Kommunikation zwischen Prozessen.
Objekte können das Parcelable-Interface implementieren, um das Schreiben in eine Parcel zu ermöglichen, zum Beispiel (kopiert von AOSP):```java
public class UsbAccessory implements Parcelable {
public static final Parcelable.Creator CREATOR =
new Parcelable.Creator() {
public UsbAccessory createFromParcel(Parcel in) {
String manufacturer = in.readString();
String model = in.readString();
String description = in.readString();
String version = in.readString();
String uri = in.readString();
IUsbSerialReader serialNumberReader = IUsbSerialReader.Stub.asInterface(
in.readStrongBinder());
return new UsbAccessory(manufacturer, model, description, version, uri,
serialNumberReader);
}
};
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mManufacturer);
parcel.writeString(mModel);
parcel.writeString(mDescription);
parcel.writeString(mVersion);
parcel.writeString(mUri);
parcel.writeStrongBinder(mSerialNumberReader.asBinder());
} }
Beachten Sie, dass `Parcel` intern die Position speichert, an der ein Schreib- oder Lesevorgang durchgeführt wird. `readString()` parst Daten in einen String und erhöht gleichzeitig die Position. Diese Position kann manuell über [`dataPosition()`](https://developer.android.com/reference/android/os/Parcel#dataPosition())/[`setDataPosition()`](https://developer.android.com/reference/android/os/Parcel#setDataPosition(int)) abgerufen/gesetzt werden. Implementierungen des `Parcelable`-Interfaces müssen sicherstellen, dass ihr `writeToParcel` und `createFromParcel` die gleiche Datenmenge schreiben/lesen, da sonst alle nachfolgenden Lesevorgänge Daten von falschen Offsets erhalten.
[`Bundle`](https://developer.android.com/reference/android/os/Bundle) (eine Schlüssel-Wert-Zuordnung, die zwischen Prozessen gesendet werden kann) kann eine [Vielzahl von Objekten enthalten, die über `writeValue()` in `Parcel` geschrieben werden können](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/Parcel.java;l=1792-1937). Wenn der Inhalt eines `Bundle` aus einem `Parcel` gelesen wird, kann jede im System verfügbare `Parcelable`-Klasse dort gelesen werden.
`Bundle` verzögert die tatsächliche Analyse des Inhalts, indem die Länge der gesamten parcelled-Daten in den `Parcel` geschrieben und dann [der relevante Teil des ursprünglichen Parcel in einen sekundären Parcel kopiert wird, der in `mParcelledData` gespeichert wird](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/BaseBundle.java;l=1675-1683) (dies ermöglicht es z.B. [`Activity.onSaveInstanceState()`](https://developer.android.com/reference/android/app/Activity#onSaveInstanceState(android.os.Bundle)), `Parcelable`s bereitzustellen, die in `system_server` nicht verfügbar sind; das gesamte `Bundle` wird dann unverändert an `system_server` übergeben und zurück, ohne den Inhalt zu analysieren).
Sobald jedoch auf einen Wert im `Bundle` zugegriffen wurde, [wurden alle Werte im `Bundle` entpackt](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/BaseBundle.java;l=227-313) und [jedes vorhandene Schlüssel-Wert-Paar wurde analysiert](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/Parcel.java;l=3613-3632). Enthielt eine solche Karte ein `Parcelable` mit unausgeglichenen `writeToParcel`- und `createFromParcel`-Methoden und wurde dieses `Bundle` später an einen anderen Prozess weitergeleitet, konnte dieser andere Prozess einen anderen Inhalt des `Bundle` sehen. Dies machte alle [solcher Unstimmigkeiten in Klassen, die im System verfügbar sind, zu Schwachstellen](https://github.com/michalbednarski/ReparcelBug), da es [Stellen im System gibt, an denen `Bundle` als sicher überprüft](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/services/core/java/com/android/server/accounts/AccountManagerService.java;l=5037-5046) und dann an einen anderen Prozess weitergeleitet wird.
In diesem Writeup nenne ich ein solches `Bundle`, das einen Inhalt präsentiert und nach der Weiterleitung einen anderen, selbstveränderndes `Bundle`.