
Writeup y exploit para escalada de privilegios de una app instalada a nivel de sistema en Android 12 Beta mediante CVE-2021-0928, una discrepancia de serialización `writeToParcel`/`createFromParcel` en `OutputConfiguration`
CVE-2021-0928, discrepancia de serialización writeToParcel/createFromParcel en android.hardware.camera2.params.OutputConfiguration
Este es un exploit que utiliza esa vulnerabilidad para escalar privilegios desde una app de Android instalada hacia la app de Configuración de Android (o cualquier otra app instalada a la que se pudiera enviar un <receiver> declarado en AndroidManifest.xml; la escalada de privilegios enviando a un <activity> también era posible, aunque no se presenta aquí)
Encontré el problema originalmente en Android 12 Developer Preview 3
La versión del exploit presente en este repositorio funciona en Android 12 Beta 2 y 3
La vulnerabilidad se corrigió en la primera versión oficial de Android 12
El informe que aparece a continuación fue escrito originalmente para Google para que este informe se considerara como una cadena de exploit completa
En el momento de redactar este informe, Android 12 no estaba disponible en AOSP (las versiones Developer Preview/Beta de Android no son de código abierto)

La mayor parte de la IPC en Android se realiza a través de la clase llamada Parcel
El uso básico de Parcel es el siguiente:```java Parcel p = Parcel.obtain(); p.writeInt(1); p.writeString("Hello");
Entonces `Parcel` se envía a otro proceso [a través de `Binder`](https://developer.android.com/reference/android/os/IBinder#transact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)). Alternativamente, para pruebas se puede llamar a [`p.setDataPosition(0)`](https://developer.android.com/reference/android/os/Parcel#setDataPosition(int)) para rebobinar el parcel a la posición inicial y comenzar a leer:```java
int a = p.readInt(); // a = 1
String b = p.readString(); // b = "Hello"
Debe tenerse en cuenta que Parcel internamente mantiene la posición desde la cual se realizan las lecturas. Es responsabilidad del usuario de la clase Parcel asegurarse de que los métodos read* coincidan con los métodos write* utilizados previamente; de lo contrario, las lecturas posteriores se realizarán desde posiciones incorrectas en el búfer.
Parcel también proporciona la capacidad de escribir objetos personalizados; la forma preferida de hacerlo es implementando la interfaz Parcelable.
Aquí hay un ejemplo de implementación de la interfaz Parcelable (se eliminó código irrelevante; la clase WindowContainerTransaction se usa en el exploit como parte de la cadena de gadgets, sin embargo, no hay nada incorrecto en ella).```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);
}
};
}
Como se puede ver arriba, el método `writeToParcel()` se utiliza durante la escritura. Luego, al leer, se llama al método de fábrica `CREATOR.createFromParcel()`. Es responsabilidad de la implementación de `Parcelable` asegurarse de que `createFromParcel` lea la misma cantidad de datos que fue escrita por `writeToParcel`; de lo contrario, todas las lecturas posteriores de ese `Parcel` leerán datos desde un offset incorrecto.
Dicha clase se puede escribir/leer desde/hacia `Parcel` mediante:
* Llamando directamente a `obj.writeToParcel(parcel, 0)` / `obj = WindowContainerTransaction.CREATOR.createFromParcel()`, esto se usa a menudo cuando se conoce el tipo de la clase, por ejemplo cuando `Parcelable` tiene un campo con otro `Parcelable` o en código generado por AIDL cuando el método RPC definido tiene un `Parcelable` como argumento
* A través de `Parcel.writeParcelable`/`readParcelable`. [`writeParcelable`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=1909;drc=58787794eb5c879f6e39ee58c0071b36e337f8e3) primero escribe el nombre de la clase y luego llama al método `writeToParcel` de la interfaz `Parcelable`. [`readParcelable`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=3282;drc=58787794eb5c879f6e39ee58c0071b36e337f8e3) lee el nombre de clase escrito, encuentra la clase con ese nombre en el `ClassLoader` proporcionado o en `BOOTCLASSPATH` si se proporcionó null. Una vez que se encuentra la clase, se usa su campo estático `CREATOR` para obtener una instancia de [`Parcelable.Creator`](https://developer.android.com/reference/android/os/Parcelable.Creator), que es la fábrica utilizada para leer esa clase. Es importante notar que cuando se usa el método `readParcelable`, puede leer cualquier `Parcelable` disponible en el classpath, ya que el nombre del objeto a crear se lee del mismo `Parcel`
* `readParcelable` es utilizado por muchos otros métodos de `Parcel`, por ejemplo `readList` visto en el ejemplo anterior lee elementos a través de `readValue`, que es el método más genérico para transferir objetos en `Parcel` y una de las formas que usa es a través de `readParcelable`. Además, en el ejemplo anterior, debido al Type Erasure de Java, el campo `ArrayList<HierarchyOp> mHierarchyOps` puede contener en realidad cualquier objeto soportado por Parcel, no solo aquellos compatibles con el tipo especificado en la declaración del tipo genérico
# Desajustes de `writeToParcel`/`createFromParcel`
Como se señaló anteriormente, es responsabilidad de la implementación de la interfaz `Parcelable` asegurarse de que `createFromParcel` lea la misma cantidad de datos del `Parcel` que el `writeToParcel` correspondiente escribió previamente. Siempre que haya en `BOOTCLASSPATH` un `Parcelable` que pueda violar ese contrato, se crea una vulnerabilidad, ya que permite el siguiente escenario:
1. Una aplicación maliciosa envía a `system_server` un `Bundle` O un `Parcelable` que contiene una instancia `Parcelable` defectuosa junto con datos específicamente construidos que se leerán realmente en el paso 3 pero se pasarán textualmente durante el paso 2
2. `system_server` verifica que el `Bundle` es seguro y luego lo reenvía O `system_server` pasa el `Parcelable` proporcionado a un método AIDL que también tiene datos críticos pasados en el siguiente parámetro (si los datos recibidos en ese parámetro pudieran modificarse, eso causaría un problema de seguridad)
3. Otra aplicación recibe datos de `system_server` y confía en ellos; sin embargo, debido a la serialización defectuosa, los datos que realmente ve difieren de los datos que `system_server` pretendía enviar