
Writeup y exploit para CVE-2024-34740, desbordamiento de entero en el BinaryXmlSerializer de Android para escritura de archivos en system_server y posterior ejecución de código en system_server desde una aplicación normal instalada.

Las correcciones para el problema descrito aquí aparecieron bajo CVE-2024-34740 / A-307288067:
Dentro de system_server de Android, muchos servicios guardan su estado entre reinicios en archivos 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
Históricamente, estos han sido archivos XML de texto plano con sangría, lo que permitía a los desarrolladores leerlos fácilmente; sin embargo, [en Android 12 se introdujo una nueva versión binaria de ese formato, citando que el 1,5 % de todo el tiempo empleado por `system_server` se gastaba en estas operaciones XML](https://android.googlesource.com/platform/frameworks/base/+/4ccea8796991d678ead4399130ec31edf63ff4fa%5E%21/)
Cabe señalar que este formato solo se utiliza internamente por el sistema y tiene archivos con el valor mágico `"ABX\x00"`. Es diferente del [formato utilizado dentro de los APK](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/androidfw/ResourceTypes.cpp;l=1770;drc=d4e49e63519397789d284a03aea5fafc119cb1b0) para `AndroidManifest.xml`, `res/xml/*.xml`, `res/layout/*.xml`, etc., que no tiene un "valor mágico" explícito, aunque normalmente comienza con `0300 0800` (que es el [encabezado](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` y `headerSize=8`)
Cada vez que el sistema lee uno de estos archivos XML de estado interno, [utiliza el valor mágico `"ABX\0"` en el archivo para elegir entre el analizador de archivos XML binarios o el analizador XML normal](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/util/Xml.java;l=188-192;drc=97a370a95275e79c69e79d7ead11aa38934a5575). El que estos archivos se guarden como XML binario está [controlado por una propiedad del 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) y está habilitado de forma predeterminada
Cuando los archivos XML binarios están en uso, puedes leer su contenido por ejemplo mediante `adb shell su 0 abx2xml /data/system/packages.xml -`
Una de las cosas que hace este formato binario es ofrecer métodos de acceso tipados, por lo que el serializador ofrece el método `attributeInt(String namespace, String name, int value)`, que escribe el valor como un entero binario, evitando el viaje de ida y vuelta a través de String que supondría una nueva asignación y un objeto posterior para el recolector de basura
Otro tipo que se puede serializar directamente es la matriz de bytes```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;
}
También existe un método similar attributeBytesHex que solo se diferencia por la etiqueta TYPE_* escrita. Esa etiqueta la usa la herramienta abx2xml para convertir el array de bytes a la representación String adecuada.
mOut es una instancia de FastDataOutput, que proporciona las funciones del DataOutputStream de Java. writeByte/writeShort/writeInt/writeUTF/write usan el mismo formato que el DataOutputStream estándar.
De forma similar a Parcel, si algo no coincide durante la escritura/lectura, los datos leídos posteriormente se tomarán de offsets incorrectos; sin embargo, a diferencia de Parcel, los errores en el uso de BinaryXmlSerializer/BinaryXmlPullParser no dan al atacante la capacidad de manipular arbitrariamente los datos leídos (el atacante no puede introducir nuevos nombres/valores de etiquetas o atributos en ese caso).
Sin embargo, los errores dentro de la propia clase BinaryXmlSerializer o en FastDataOutput sí lo permiten.
En el método anterior, si intentáramos escribir un array de bytes de longitud 65536, escribiríamos la longitud con writeShort(), lo que efectivamente escribiría 0; después se escribiría el contenido real del array.
Para explotar ese desajuste, necesitaremos elegir algún archivo en el que podamos inyectar un array de bytes arbitrario a attributeBytesBase64 o attributeBytesHex, y cuya modificación además sea valiosa para el atacante.
La clase PackageInstaller ofrece la capacidad de preparar un paquete para su instalación. Sin necesidad de ningún permiso, cualquier app puede escribir un nuevo APK para que se instale en un directorio temporal. Una vez que se ha escrito todo lo necesario para la instalación, la app instaladora puede commit() PackageInstaller.Session, lo que significa que no podrá realizar más cambios en los archivos de instalación y que la Session está lista para la aprobación del usuario o para la instalación real.
El estado de estas operaciones se almacena en /data/system/install_sessions.xml. La app instaladora puede, por ejemplo, descargar la mitad de un APK grande en el directorio temporal creado por el Package Manager Service para su PackageInstaller.Session; después de un reinicio, reanudar la descarga, escribir la mitad restante y confirmar la instalación.
Una de las posibilidades es escribir datos en install_sessions.xml para marcar la sesión como staged, lo que significa que se instalará tras el siguiente arranque.