
Writeup e exploit para CVE-2024-34740, estouro de inteiro no BinaryXmlSerializer do Android para escrita de arquivo no system_server e então execução de código no system_server a partir de um aplicativo instalado normal.

As correções para o problema descrito aqui apareceram sob CVE-2024-34740 / A-307288067:
Dentro do system_server do Android, muitos serviços armazenam seu estado entre reinicializações em arquivos 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
Historicamente, esses eram arquivos XML de texto simples com indentação, o que permitia aos desenvolvedores lê-los facilmente, porém [no Android 12, uma nova versão binária desse formato foi introduzida, citando 1,5% de todo o tempo gasto pelo `system_server` sendo gasto nessas operações XML](https://android.googlesource.com/platform/frameworks/base/+/4ccea8796991d678ead4399130ec31edf63ff4fa%5E%21/)
Deve-se notar que este formato é usado apenas internamente pelo sistema e possui arquivos com valor mágico `"ABX\x00"`. É diferente do [formato usado dentro de APKs](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 não possui um "valor mágico" explícito, no entanto, geralmente começa com `0300 0800` (que é o [cabeçalho](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h;l=608;drc=d4e49e63519397789d284a03aea5fafc119cb1b0) com `type=RES_XML_TYPE` e `headerSize=8`)
Sempre que o sistema lê um desses arquivos XML de estado interno, ele [usa o valor mágico `"ABX\0"` no arquivo para escolher entre o analisador para arquivos XML Binários ou o analisador XML regular](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/util/Xml.java;l=188-192;drc=97a370a95275e79c69e79d7ead11aa38934a5575). O fato de esses arquivos serem salvos como XML Binário é [controlado por uma propriedade do 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) e está habilitado por padrão
Quando arquivos XML Binários estão em uso, você pode ler seu conteúdo, por exemplo, através de `adb shell su 0 abx2xml /data/system/packages.xml -`
Uma das coisas que este formato binário oferece são acessadores tipados, então o serializador oferece o método `attributeInt(String namespace, String name, int value)`, que escreve o valor como um inteiro binário, evitando a ida e volta através de String, o que seria uma nova alocação e posterior objeto para Coleta de Lixo
Outro tipo que pode ser diretamente serializado é o array 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;
}
Existe também um método semelhante attributeBytesHex que difere apenas pela tag TYPE_* escrita. Essa tag é usada pela ferramenta abx2xml para converter um array de bytes na representação String apropriada.
mOut é uma instância de FastDataOutput, que fornece funções do DataOutputStream do Java. writeByte/writeShort/writeInt/writeUTF/write usam o mesmo formato que o DataOutputStream padrão.
Similarmente ao Parcel, se algo não corresponder durante a escrita/leitura, os dados lidos subsequentemente serão obtidos em offsets errados. No entanto, ao contrário do Parcel, erros no uso de BinaryXmlSerializer/BinaryXmlPullParser não dão ao atacante a capacidade de adulterar arbitrariamente os dados lidos (neste caso, o atacante não pode introduzir novos nomes/valores de tag/atributo).
Erros dentro da própria classe BinaryXmlSerializer ou em FastDataOutput, no entanto, dão essa capacidade.
No método acima, se tentássemos escrever um array de bytes com comprimento 65536, escreveríamos o comprimento com writeShort(), que efetivamente escreveria 0, após o qual o conteúdo real do array será escrito.
Para explorar essa incompatibilidade, precisamos escolher algum arquivo onde possamos injetar um array de bytes arbitrário em attributeBytesBase64 ou attributeBytesHex, e onde a modificação desse arquivo seja valiosa para o atacante.
A classe PackageInstaller oferece a capacidade de preparar um pacote para instalação. Sem necessidade de permissões, qualquer aplicativo pode escrever um novo APK a ser instalado em um diretório temporário. Depois que tudo necessário para a instalação foi escrito, o aplicativo instalador pode commit() PackageInstaller.Session, o que significa que não poderá fazer mais alterações nos arquivos de instalação e a Session está pronta para aprovação do usuário ou instalação real.
O estado dessas operações é armazenado em /data/system/install_sessions.xml. O aplicativo instalador pode, por exemplo, baixar metade de um APK grande para o diretório temporário criado pelo Package Manager Service para sua PackageInstaller.Session, depois reiniciar, retomar o download, escrever a metade restante e confirmar a instalação.
Uma das possibilidades é escrever dados em install_sessions.xml para marcar a sessão como staged, o que significa que será instalada após a próxima inicialização.