
Exploit para CVE-2022-20452, escalada de privilegios en Android desde una aplicación instalada a una aplicación del sistema (u otra aplicación) mediante LazyValue usando Parcel después de recycle()
Android 13 introduce muchas mejoras para endurecer el mecanismo de serialización Parcel
Eso es genial, definitivamente elimina o vuelve inexplotables muchas vulnerabilidades. También describen cómo rompieron mi exploit anterior, que permitía a las aplicaciones cargar su código en otras aplicaciones (incluidas las del sistema)
Pero ahora vuelvo con un nuevo exploit que logra lo mismo, aunque de manera diferente. Se basa en las siguientes vulnerabilidades que se introdujeron durante el mencionado endurecimiento de Parcel:
![Captura de pantalla de la aplicación mostrando texto. Título: LeakValue. Texto principal: 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. En la parte inferior de la pantalla hay dos botones: START y MANUAL TESTING](Screenshot_20220723-081920.png)
(También logcat de la ejecución de la aplicación, la explotación es ruidosa en los registros)
Parcel y ParcelableLa clase Parcel de Android es la base de la comunicación entre procesos
Los objetos pueden implementar la interfaz Parcelable para permitir escribirlos en Parcel, por ejemplo (copiado de 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());
} }
Note that `Parcel` internally stores position at which write or read is performed, `readString()` parses data into String as well as advances position. That position can be manually get/set through [`dataPosition()`](https://developer.android.com/reference/android/os/Parcel#dataPosition())/[`setDataPosition()`](https://developer.android.com/reference/android/os/Parcel#setDataPosition(int)). Implementations of `Parcelable` interface must ensure that their `writeToParcel` and `createFromParcel` write/read same amount of data, otherwise all subsequent reads will get data from wrong offsets
[`Bundle`](https://developer.android.com/reference/android/os/Bundle) (key-value map that can be sent across processes) can contain [variety of objects that can be written to Parcel through `writeValue()`](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/Parcel.java;l=1792-1937). When contents of `Bundle` are read from `Parcel`, any `Parcelable` class available in system there can be read
`Bundle` defers actual parsing of contents by having length of whole parcelled data written into `Parcel` and then just [copying relevant part of original Parcel to secondary Parcel stored in `mParcelledData`](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/BaseBundle.java;l=1675-1683) (this allows for example [`Activity.onSaveInstanceState()`](https://developer.android.com/reference/android/app/Activity#onSaveInstanceState(android.os.Bundle)) to provide `Parcelable`s which are not available in `system_server`, whole `Bundle` is then passed to `system_server` and back verbatim without parsing contents)
Once however any value in `Bundle` was accessed, all values inside `Bundle` [were unparcelled](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/BaseBundle.java;l=227-313) and [every present key-value pair was parsed](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/Parcel.java;l=3613-3632). If such map contained `Parcelable` which had unbalanced `writeToParcel` and `createFromParcel` methods and later such `Bundle` was forwarded to another process, that another process could see different contents of `Bundle`. This made all such [mismatches in classes available in system vulnerabilities](https://github.com/michalbednarski/ReparcelBug) as there are [places in system where `Bundle` is inspected to be safe](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) and then forwarded to another process
In this writeup I'm calling such `Bundle` which presents one contents and then other after being forwarded a self-changing `Bundle`
Another important thing here is that besides just bytes (Strings, numbers, objects made of above), `Parcel` can also contain File Descriptors and `Binder`s. `Binder`s are objects on which one can make RPC call, that is one process creates `Binder` object and overrides [`onTransact()` method](https://developer.android.com/reference/android/os/Binder#onTransact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)). Then `Binder` is passed to another process, in example code above you can see `read`/`writeStrongBinder()` calls used to read and write it to `Parcel`. In another process, when `readStrongBinder()` is used a `BinderProxy` object is created (hidden behind [`IBinder` interface](https://developer.android.com/reference/android/os/IBinder)). Then that another process can call [`transact()`](https://developer.android.com/reference/android/os/IBinder#transact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)) on that object and in original object `onTransact()` will be executed. Usually through, one doesn't manually write `transact()`/`onTransact()` but [uses AIDL instead](https://developer.android.com/guide/components/aidl)
# Entra `LazyValue`, el fin de los `Bundle`s auto-cambiables