
Writeup ed exploit per CVE-2024-34740, overflow di intero in BinaryXmlSerializer di Android che porta a scrittura di file su system_server e poi a esecuzione di codice su system_server da un'app installata normale.

Le correzioni per il problema descritto qui sono apparse sotto CVE-2024-34740 / A-307288067:
All'interno di Android system_server, molti servizi salvano il loro stato tra i riavvii in file XML```
$ adb shell su 0 find /data/system -name '*.xml' | sort
/data/system/appops_accesses.xml
/data/system/cachequota.xml
/data/system/device_policies.xml
/data/system/device_policy_state.xml
/data/system/display-manager-state.xml
/data/system/input-manager-state.xml
/data/system/inputmethod/subtypes.xml
/data/system/install_sessions.xml
/data/system/job/jobs_1000.xml
/data/system/job/jobs_10131.xml
/data/system/log-files.xml
/data/system/netpolicy.xml
/data/system/notification_policy.xml
/data/system/overlays.xml
/data/system/packages.xml
/data/system/package-watchdog.xml
/data/system/sensor_privacy_impl.xml
/data/system/sensor_privacy.xml
/data/system/shortcut_service.xml
/data/system/users/0/app_idle_stats.xml
/data/system/users/0/appwidgets.xml
/data/system/users/0/package-restrictions.xml
/data/system/users/0/settings_global.xml
/data/system/users/0/settings_secure.xml
/data/system/users/0/settings_system.xml
/data/system/users/0/wallpaper_info.xml
/data/system/users/0.xml
/data/system/users/userlist.xml
/data/system/watchlist_settings.xml
Storicamente questi sono stati file XML di testo semplice con indentazione, che permettevano agli sviluppatori di leggerli facilmente, tuttavia [in Android 12 è stata introdotta una nuova versione binaria di quel formato, citando il 1.5% del tempo totale speso da `system_server` in queste operazioni XML](https://android.googlesource.com/platform/frameworks/base/+/4ccea8796991d678ead4399130ec31edf63ff4fa%5E%21/)
Va notato che questo formato è utilizzato solo internamente dal sistema e ha file con valore magico `"ABX\x00"`. È diverso dal [formato utilizzato all'interno degli APK](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/androidfw/ResourceTypes.cpp;l=1770;drc=d4e49e63519397789d284a03aea5fafc119cb1b0) per `AndroidManifest.xml`, `res/xml/*.xml`, `res/layout/*.xml`, ecc. che non ha un esplicito "valore magico", ma di solito inizia con `0300 0800` (che è [header](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h;l=608;drc=d4e49e63519397789d284a03aea5fafc119cb1b0) con `type=RES_XML_TYPE` e `headerSize=8`)
Ogni volta che il sistema legge uno di questi file XML di stato interno, [usa il valore magico `"ABX\0"` nel file per scegliere tra un parser per file XML binario o un parser XML regolare](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/util/Xml.java;l=188-192;drc=97a370a95275e79c69e79d7ead11aa38934a5575). Il fatto che questi file vengano salvati come XML binario è [controllato da una proprietà di sistema](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/util/Xml.java;drc=97a370a95275e79c69e79d7ead11aa38934a5575;l=74?q=Xml.java) ed è abilitato per impostazione predefinita
Quando i file XML binari sono in uso, puoi leggerne il contenuto, ad esempio tramite `adb shell su 0 abx2xml /data/system/packages.xml -`
Una delle cose che questo formato binario fa è offrire accessori tipizzati, quindi il serializzatore offre il metodo `attributeInt(String namespace, String name, int value)`, che scrive il valore come intero binario, evitando il passaggio attraverso String che comporterebbe una nuova allocazione e un successivo oggetto per la Garbage Collection
Un altro tipo che può essere serializzato direttamente è l'array di byte```java
@Override
public XmlSerializer attributeBytesBase64(String namespace, String name, byte[] value)
throws IOException {
if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
mOut.writeByte(ATTRIBUTE | TYPE_BYTES_BASE64);
mOut.writeInternedUTF(name);
mOut.writeShort(value.length);
mOut.write(value);
return this;
}
Esiste anche un metodo simile attributeBytesHex che differisce solo per il tag TYPE_* scritto. Quel tag è usato dallo strumento abx2xml per convertire un array di byte nella rappresentazione String appropriata
mOut è un'istanza di FastDataOutput, che fornisce le funzioni del DataOutputStream di Java. writeByte/writeShort/writeInt/writeUTF/write usano lo stesso formato dello standard DataOutputStream
Analogamente a Parcel, se qualcosa non corrisponde durante la scrittura/lettura, i dati letti successivamente verranno presi da offset errati, tuttavia a differenza di Parcel, errori nell'uso di BinaryXmlSerializer/BinaryXmlPullParser non danno all'attaccante la capacità di alterare arbitrariamente i dati letti (l'attaccante non può introdurre nuovi nomi/valori di tag/attributi in quel caso)
Errori nella classe BinaryXmlSerializer stessa o in FastDataOutput invece sì
Nel metodo sopra, se provassimo a scrivere un array di byte con lunghezza 65536, scriveremo la lunghezza con writeShort(), che effettivamente scriverà 0, dopodiché il contenuto effettivo dell'array sarà scritto
Per sfruttare questa discrepanza, dovremo scegliere un file in cui saremo in grado di iniettare un array di byte arbitrario in attributeBytesBase64 o attributeBytesHex e la modifica di quel file sarà preziosa per l'attaccante
La classe PackageInstaller offre la possibilità di preparare un pacchetto per l'installazione. Senza bisogno di alcun permesso, qualsiasi app può scrivere un nuovo APK da installare in una directory temporanea. Una volta scritto tutto il necessario per l'installazione, l'app installante può chiamare commit() di PackageInstaller.Session, il che significa che non sarà in grado di apportare ulteriori modifiche ai file di installazione e la Session è pronta per l'approvazione dell'utente o per l'installazione effettiva
Lo stato di queste operazioni è memorizzato in /data/system/install_sessions.xml. L'app dell'installatore può, ad esempio, scaricare metà di un grande APK in una directory temporanea creata da Package Manager Service per la sua PackageInstaller.Session, poi dopo il riavvio riprendere il download, scrivere la metà rimanente e confermare l'installazione
Una delle possibilità è scrivere dati in install_sessions.xml per contrassegnare la sessione come staged, il che significa che verrà installata dopo il prossimo avvio