
Writeup ed exploit per l'app installata per l'escalation dei privilegi di sistema su Android 12 Beta tramite CVE-2021-0928, una discrepanza di serializzazione `writeToParcel`/`createFromParcel` in `OutputConfiguration`
CVE-2021-0928, mancata corrispondenza di serializzazione writeToParcel/createFromParcel in android.hardware.camera2.params.OutputConfiguration
Questo è un exploit che utilizza quella vulnerabilità per l'escalation dei privilegi da un'app Android installata all'app Impostazioni di Android (o qualsiasi altra app installata a cui l'app potesse inviare un <receiver> dichiarato in AndroidManifest.xml; l'escalation dei privilegi inviando a un <activity> era possibile anche se non presentata qui)
Ho trovato il problema originariamente su Android 12 Developer Preview 3
La versione dell'exploit presente in questo repository funziona su Android 12 Beta 2 e 3
La vulnerabilità è stata corretta nella prima release ufficiale di Android 12
Il writeup qui sotto è stato originariamente scritto per Google per la considerazione di questo report come catena di exploit completa
Al momento della scrittura, Android 12 non era disponibile in AOSP (le release Android Developer Preview/Beta non sono open source)

La maggior parte dell'IPC su Android viene effettuata tramite la classe chiamata Parcel
L'uso di base di Parcel è il seguente:```java Parcel p = Parcel.obtain(); p.writeInt(1); p.writeString("Hello");
Then `Parcel` viene inviato a un altro processo [tramite `Binder`](https://developer.android.com/reference/android/os/IBinder#transact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)). In alternativa, per testare, si può chiamare [`p.setDataPosition(0)`](https://developer.android.com/reference/android/os/Parcel#setDataPosition(int)) per riportare il parcel alla posizione iniziale e iniziare a leggere:```java
int a = p.readInt(); // a = 1
String b = p.readString(); // b = "Hello"
Va notato che Parcel internamente mantiene la posizione da cui vengono eseguite le letture. È responsabilità dell'utente della classe Parcel assicurarsi che i metodi read* corrispondano ai metodi write* utilizzati in precedenza, altrimenti le letture successive avverranno da posizioni errate nel buffer
Parcel offre anche la possibilità di scrivere oggetti personalizzati, il modo preferito per farlo è implementando l'interfaccia Parcelable
Ecco un esempio di implementazione dell'interfaccia Parcelable (codice irrilevante rimosso, la classe WindowContainerTransaction viene utilizzata nell'exploit come parte della catena di gadget, tuttavia non c'è nulla di sbagliato in essa)```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);
}
};
}
Come si può vedere sopra, il metodo `writeToParcel()` viene utilizzato durante la scrittura. Poi, durante la lettura, viene chiamato il metodo factory `CREATOR.createFromParcel()`. È responsabilità dell'implementazione di `Parcelable` garantire che `createFromParcel` legga la stessa quantità di dati che è stata scritta da `writeToParcel`, altrimenti tutte le letture successive da quella `Parcel` leggeranno dati da un offset errato
Tale classe può essere scritta/letta da/verso `Parcel` tramite:
* Chiamando direttamente `obj.writeToParcel(parcel, 0)` / `obj = WindowContainerTransaction.CREATOR.createFromParcel()`, questo viene spesso usato quando il tipo della classe è noto, ad esempio quando `Parcelable` ha un campo con un diverso `Parcelable` o nel codice generato da AIDL quando il metodo RPC definito ha un `Parcelable` come argomento
* Tramite `Parcel.writeParcelable`/`readParcelable`. [`writeParcelable`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=1909;drc=58787794eb5c879f6e39ee58c0071b36e337f8e3) scrive prima il nome della classe e poi chiama il metodo `writeToParcel` dell'interfaccia `Parcelable`. [`readParcelable`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=3282;drc=58787794eb5c879f6e39ee58c0071b36e337f8e3) legge il nome della classe scritto, trova la classe con quel nome nel `ClassLoader` fornito o in `BOOTCLASSPATH` se è stato fornito null. Una volta trovata la classe, il suo campo statico `CREATOR` viene usato per ottenere un'istanza di [`Parcelable.Creator`](https://developer.android.com/reference/android/os/Parcelable.Creator) che è la factory usata per leggere quella classe. È importante notare che quando viene usato il metodo `readParcelable` può leggere qualsiasi `Parcelable` disponibile nel class path poiché il nome dell'oggetto da creare viene letto dalla stessa `Parcel`
* `readParcelable` è usato da molti altri metodi di `Parcel`, ad esempio `readList` visto nell'esempio sopra legge gli elementi tramite `readValue`, che è il metodo più generico per trasferire oggetti in `Parcel` e uno dei modi in cui opera è tramite `readParcelable`. Inoltre, nell'esempio sopra, a causa della Type Erasure di Java, il campo `ArrayList<HierarchyOp> mHierarchyOps` può in realtà contenere qualsiasi oggetto supportato da Parcel, non solo quelli compatibili con il tipo specificato nella dichiarazione del tipo generico
# Mismatch `writeToParcel`/`createFromParcel`
Come notato sopra, è responsabilità dell'implementazione dell'interfaccia `Parcelable` garantire che `createFromParcel` legga dalla `Parcel` la stessa quantità di dati che il corrispondente `writeToParcel` ha precedentemente scritto. Ogni volta che in `BOOTCLASSPATH` c'è un `Parcelable` che può violare questo contratto, si crea una vulnerabilità poiché consente il seguente scenario:
1. Un'applicazione malintenzionata invia a `system_server` un `Bundle` OPPURE un `Parcelable` contenente un'istanza `Parcelable` difettosa insieme a dati costruiti appositamente che verranno effettivamente letti nel passaggio 3 ma passati verbatim durante il passaggio 2
2. `system_server` verifica che il `Bundle` sia sicuro e poi lo inoltra OPPURE `system_server` passa il `Parcelable` fornito a un metodo AIDL che ha anche dati critici passati nel parametro successivo (se i dati ricevuti in quel parametro potessero essere modificati, ciò causerebbe un problema di sicurezza)
3. Un'altra app riceve dati da `system_server` e si fida di essi, tuttavia a causa della serializzazione difettosa i dati che vede effettivamente differiscono da quelli che `system_server` intendeva inviare