Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
AbxOverflow — Writeup und Exploit für CVE-2024-34740, Integer-Überlauf in Androids BinaryXmlSerializer, der zu einem Datei-Schreibvorgang in system_server und anschließend zur Codeausführung in system_server von einer normal installierten App führt. | Kitploit
Tools/GitHubGitHub/michalbednarski/abxoverflow
Android-SicherheitPrivilege EscalationSchwachstellenanalyseCode-AnalyseExploitationLernen & BildungPayload-EntwicklungBinary-Exploitation
GitHubmichalbednarski/abxoverflow

AbxOverflow

Writeup und Exploit für CVE-2024-34740, Integer-Überlauf in Androids BinaryXmlSerializer, der zu einem Datei-Schreibvorgang in system_server und anschließend zur Codeausführung in system_server von einer normal installierten App führt.

6825vor 10 MonatenVon Kitploit geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen
Repository anzeigen

Screenshot einer Android-Anwendung mit dem Titel AbxDroppedApk und viel Text, der beschreibt, dass sie innerhalb von system_server läuft

Korrekturen für das hier beschriebene Problem erschienen unter CVE-2024-34740 / A-307288067:

  • Bulletin
  • Mit dem Bulletin verlinkter Patch
  • Zwei weitere Patches: 1 2

Android Binary XML

Innerhalb von Android system_server speichern viele Dienste ihren Zustand über Neustarts hinweg in XML-Dateien``` $ 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

root@kitploit:~
Historisch gesehen handelte es sich dabei um einfache Text-XML-Dateien mit Einrückung, die Entwicklern ein einfaches Lesen ermöglichten. Allerdings wurde [in Android 12 eine neue binäre Version dieses Formats eingeführt, wobei angeführt wurde, dass 1,5 % der gesamten von `system_server` aufgewendeten Zeit auf diese XML-Operationen entfallen](https://android.googlesource.com/platform/frameworks/base/+/4ccea8796991d678ead4399130ec31edf63ff4fa%5E%21/)

Es sollte beachtet werden, dass dieses Format nur intern vom System verwendet wird und Dateien mit dem Magic-Wert `"ABX\x00"` besitzt. Es unterscheidet sich vom [Format, das innerhalb von APKs verwendet wird](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/androidfw/ResourceTypes.cpp;l=1770;drc=d4e49e63519397789d284a03aea5fafc119cb1b0) für `AndroidManifest.xml`, `res/xml/*.xml`, `res/layout/*.xml` usw., das keinen expliziten „Magic-Wert“ besitzt, aber normalerweise mit `0300 0800` beginnt (was ein [Header](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h;l=608;drc=d4e49e63519397789d284a03aea5fafc119cb1b0) mit `type=RES_XML_TYPE` und `headerSize=8` ist).

Immer wenn das System eine dieser internen Zustands-XML-Dateien liest, [verwendet es den Magic-Wert `"ABX\0"` in der Datei, um entweder den Parser für die Binary-XML-Datei oder den regulären XML-Parser auszuwählen](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/util/Xml.java;l=188-192;drc=97a370a95275e79c69e79d7ead11aa38934a5575). Ob diese Dateien als Binary XML gespeichert werden, wird [über eine Systemeigenschaft gesteuert](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/util/Xml.java;drc=97a370a95275e79c69e79d7ead11aa38934a5575;l=74?q=Xml.java) und ist standardmäßig aktiviert.

Wenn Binary-XML-Dateien verwendet werden, kann man ihren Inhalt beispielsweise über `adb shell su 0 abx2xml /data/system/packages.xml -` lesen.

Eine der Funktionen dieses binären Formats besteht darin, typisierte Zugriffsmethoden bereitzustellen. So bietet der Serializer die Methode `attributeInt(String namespace, String name, int value)`, die den Wert als binäre Ganzzahl schreibt und so den Umweg über String vermeidet, der eine neue Allokation und ein anschließendes Objekt für die Garbage Collection bedeuten würde.

Ein weiterer Typ, der direkt serialisiert werden kann, ist Byte-Array.```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;
}

Es gibt auch eine ähnliche Methode attributeBytesHex, die sich nur durch das geschriebene TYPE_*-Tag unterscheidet. Dieses Tag wird vom Tool abx2xml verwendet, um ein Byte-Array in die passende String-Darstellung zu konvertieren.

mOut ist eine Instanz von FastDataOutput, die Funktionen von Java's DataOutputStream bereitstellt. writeByte/writeShort/writeInt/writeUTF/write verwenden dasselbe Format wie das standardmäßige DataOutputStream.

Ähnlich wie bei Parcel werden bei einer Abweichung während des Schreibens/Lesens nachfolgend gelesene Daten von falschen Offsets übernommen. Im Gegensatz zu Parcel geben Fehler bei der Verwendung von BinaryXmlSerializer/BinaryXmlPullParser dem Angreifer jedoch nicht die Möglichkeit, gelesene Daten willkürlich zu manipulieren (der Angreifer kann in diesem Fall keine neuen Tag-/Attributnamen/-werte einführen).

Fehler innerhalb der BinaryXmlSerializer-Klasse selbst oder in FastDataOutput jedoch schon.

Wenn wir in der obigen Methode versuchen würden, ein Byte-Array mit der Länge 65536 zu schreiben, würden wir die Länge mit writeShort() schreiben, was effektiv 0 schreibt, wonach der eigentliche Array-Inhalt geschrieben wird.

Wahl des ABX-Injektionsziels

Um diese Abweichung auszunutzen, müssen wir eine Datei wählen, in der wir ein beliebiges Byte-Array in attributeBytesBase64 oder attributeBytesHex injizieren können. Außerdem muss eine Änderung dieser Datei für den Angreifer wertvoll sein.

Die Klasse PackageInstaller bietet die Möglichkeit, ein Paket für die Installation vorzubereiten. Ohne jegliche Berechtigungen kann jede App ein neues APK schreiben, das in ein temporäres Verzeichnis installiert werden soll. Sobald alles für die Installation Notwendige geschrieben wurde, kann die installierende App commit() PackageInstaller.Session aufrufen, was bedeutet, dass sie keine weiteren Änderungen an den Installationsdateien vornehmen kann und die Session entweder für die Benutzerfreigabe oder die tatsächliche Installation bereit ist.

Der Zustand dieser Vorgänge wird in /data/system/install_sessions.xml gespeichert. Die Installer-App kann zum Beispiel die Hälfte eines großen APK in das temporäre Verzeichnis herunterladen, das vom Package Manager Service für ihre PackageInstaller.Session erstellt wurde. Nach einem Neustart kann sie den Download fortsetzen, die restliche Hälfte schreiben und die Installation committen.

Eine der Möglichkeiten besteht darin, Daten in install_sessions.xml zu schreiben, um die Sitzung als gestaged zu markieren, was bedeutet, dass sie nach dem nächsten Boot installiert wird.

Eine andere, hier vorgestellte Möglichkeit besteht darin, den Pfad zum temporären Verzeichnis zu ändern, in dem die Installationsdateien vorbereitet werden, da openWrite()/openRead() jeden gültigen Dateinamen akzeptieren, solange kein Pfad-Traversal vorliegt und diese Datei in das durch das stageDir-Feld angegebene Verzeichnis legen, das aus der XML gelesen wird

Ausnutzung der ABX-Injektion

Jetzt müssen wir unser kontrolliertes Byte-Array tatsächlich in attributeBytesBase64() bringen.

PackageInstaller.Session bietet die Methode setChecksums() an.

Auf der system_server-Seite werden die bereitgestellten Checksum-Objekte optional gegen die vom Aufrufer bereitgestellte Signatur verifiziert und dann in mChecksums abgelegt.

Wenn install_sessions.xml geschrieben wird, wird checksum.getValue() an writeByteArrayAttribute übergeben, was es wiederum an attributeBytesBase64() übergibt.

Es gibt einige Ereignisse, die das Schreiben von install_sessions.xml auslösen. Eines davon ist die Erstellung einer neuen Session. Daher erstellt dieser Exploit nach dem Setzen des Checksum auf einer Sitzung eine neue Session, um sicherzustellen, dass die erste Session in der Datei gespeichert wurde.

Nun schreiben wir ein Byte-Array mit der Länge 65536. Nachdem es gelesen wurde, wird seine Größe als Null interpretiert und der Inhalt dieses Arrays wird zu Rohdaten, die BinaryXmlPullParser parst.

Es gibt keine Attributanzahl, die angegeben ist. Jeder Eintrag hat ein Tag-Byte, das den token enthält. Im unteren Nibble befindet sich einer der Ereignistypen, die in XmlPullParser definiert sind, wie START_TAG, END_TAG oder END_DOCUMENT. Zusätzlich zu diesen Typen gibt es einen speziellen ATTRIBUTE-Typ, der nicht über next() gemeldet wird, sondern nachdem ein START_TAG-Token gesehen wurde, der Parser in die nächsten Tokens schaut, bis er ein Nicht-ATTRIBUTE-Token sieht.

Da keine Attributanzahl angegeben ist, können wir sofort zum Schließen des aktuellen Elements über das END_TAG-Token übergehen. Dann schließen wir auch </session>, da sich alle interessanten Elementattribute im öffnenden Tag <session> befinden, aber wir diesen Punkt bereits passiert haben. Allerdings können wir jetzt ein neues <session>-Element öffnen und sie dort setzen.

Wie oben erwähnt, ist FastDataInput mit Java's DataInputStream kompatibel, außer dass es eine zusätzliche Methode readInternedUTF() gibt, die sich auf vergangene Strings beziehen kann. Da wir nicht wissen, welche Strings zuvor interniert wurden, geben wir immer an, dass ein zuvor nicht gesehener String geschrieben wurde. Dadurch werden neu gelesene Strings auch zum Pool hinzugefügt, was ein Problem beim Lesen von Daten verursachen könnte, die nach unserer Injektionsstelle geschrieben wurden. Im Rahmen der Injektion füge ich jedoch alle End-Tags und das END_DOCUMENT-Token ein, sodass nach meiner Injektion nichts anderes mehr aus dieser Datei gelesen wird.

Verwendung von PackageInstaller.Session mit manipuliertem stageDir

Sobald das System die modifizierte install_sessions.xml liest, erhalten wir ein PackageInstallerSession-Objekt, dessen stageDir auf einen von uns kontrollierten Wert gesetzt ist.

Meine erste Idee war, stageDir auf /proc/self zu setzen, dann maps zu lesen und mem zu schreiben, aber das funktionierte nicht.

Als ich versuchte, mit openRead() /proc/self/maps zu öffnen, öffnete system_server die Datei erfolgreich. Die Übergabe dieser Datei an untrusted_app über Binder wurde jedoch von SELinux blockiert.

Schreibvorgänge werden jedoch nicht durch die Übergabe eines rohen Dateideskriptors an einen anderen Prozess durchgeführt, sondern über system_server vermittelt, da system_server in der Lage sein muss, den Schreibzugriff zu widerrufen, sobald die Sitzung committet ist. Heißt das, dass wir in /proc/self/mem schreiben könnten? Es stellt sich heraus, dass system_server die Datei zwar öffnen kann, aber bevor es etwas schreibt, ruft es Os.chmod() auf dieser Datei auf, was es bei /proc/self/mem nicht kann. Wir können das hier also nicht zur Ausnutzung verwenden, obwohl system_server abgesehen davon in der Lage ist, diese Datei zu öffnen und Schreibvorgänge an von uns angegebenen Offsets durchzuführen, und diese Datei das Überschreiben von Code-Seiten erlaubt, was uns direkt Code-Ausführung ermöglichen würde.

Da diese Option nicht verfügbar war, habe ich die nächste Idee ausprobiert: den Inhalt von /data/system/packages.xml ersetzen. Dies ist die Datei, die den Zustand des PackageManagerService enthält, insbesondere welche Apps installiert sind und welche UIDs ihnen zugewiesen wurden.

Es scheint, dass system_server nicht direkt in diese Datei schreiben darf: Stattdessen schreibt das System diese Datei immer zuerst in eine temporäre Datei und ersetzt dann packages.xml durch diese temporäre Datei und aktiviert den Schutz darauf.

Beim Lesen von /data/system/packages.xml prüft das System jedoch zuerst, ob die Datei /data/system/packages-backup.xml vorhanden ist. Ist dies der Fall, betrachtet es die primäre packages.xml als beschädigt und liest stattdessen das Backup. Während des normalen Betriebs ist die Datei /data/system/packages-backup.xml nicht vorhanden, und wir können eine mit einer manipulierten PackageInstallerSession erstellen, deren stageDir auf /data/system gesetzt ist.

Außerdem darf system_server einen schreibgeschützten Dateideskriptor von /data/system/packages.xml senden, wenn ich openRead() verwende. So kann ich leicht eine gepatchte Datei erstellen, die nur meine Änderungen enthält, ohne den vorherigen Inhalt zu beschädigen.

Gewährung von sharedUserId="android.uid.system"-Zugriff

In packages.xml habe ich Definitionen registrierter installierter Anwendungen, wie zum Beispiel:```xml <package name="com.android.settings" codePath="/system_ext/priv-app/Settings" ... sharedUserId="1000" ...>

root@kitploit:~
Könnten wir eine neue APK irgendwo in `/data/app` schreiben (mithilfe einer anderen `PackageInstallerSession`) und ein neues `<package>`-Element zu `packages.xml` hinzufügen und sie auf diese Weise installieren?

Ja, allerdings müssen wir in `<cert>` eine gültige Signatur unserer neu installierten APK angeben, und das System wird sie während des Bootvorgangs gegen die APK-Datei prüfen.

Könnten wir das Attribut `userId` (anstelle von `sharedUserId`, um eine APK ohne `<manifest android:sharedUserId>`-Attribut in `AndroidManifest.xml` zu kennzeichnen) auf den gewünschten Wert setzen?

Ja, allerdings dürfen wir keinen Wert verwenden, der bereits von einem anderen Paket oder einer `sharedUserId` verwendet wird.

Könnten wir `sharedUserId="1000"` für unsere App setzen?

Wenn wir das tun, wird das System während des Bootvorgangs diese Einstellung über [`canJoinSharedUserId()`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java;l=658-750;drc=7ec13b04c3bbaeac99cbbc4db9f9f80492c508fe) validieren.

Insbesondere verwendet diese Methode [`checkCapability()`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/SigningDetails.java;l=613-637;drc=97a370a95275e79c69e79d7ead11aa38934a5575), um zu prüfen, ob Signaturen entweder exakt übereinstimmen oder ob eine Signatur auf der einen Seite mit einer der früheren Signaturen auf der anderen Seite übereinstimmt.

Diese „früheren Signaturen“ stammen aus `packages.xml`; insbesondere können wir, wenn wir ein `<sigs>`-Element mit `<cert>` haben, ein `<pastSigs>`-Element unter `<sigs>` hinzufügen, um neue Einträge zu [`SigningDetails.mPastSigningCertificates`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/SigningDetails.java;l=74-87;drc=97a370a95275e79c69e79d7ead11aa38934a5575) hinzuzufügen.

Am Ende sieht unser manipuliertes `<shared-user>`-Element wie folgt aus:```xml
<shared-user name="android.uid.system" userId="1000">
  <sigs count="1" schemeVersion="3">
    <cert index="3" />
    <pastSigs count="2" schemeVersion="3">
      <cert index="19" flags="2" />
      <cert index="19" flags="2" />
    </pastSigs>
  </sigs>
</shared-user>

Das <cert>-Element unter <pastSigs> wird zweimal eingefügt, weil die letzte frühere Signatur als aktuelle gilt und daher nicht berücksichtigt wird

flags="2" bedeutet, dass das Zertifikat für sharedUserId zulässig ist

Außerdem muss die Registrierung von <package sharedUserId="1000"> auf eine App angewendet werden, die android:sharedUserId="android.uid.system" im Manifest deklariert, daher muss sie eine separate APK von derjenigen sein, die die Ausnutzung durchführt

Neu installierte System-UID-App startet nicht

Zwar konnte ich ein neues Zertifikat für android:sharedUserId="android.uid.system" als vertrauenswürdig registrieren, aber eine App, die mit diesem Zertifikat signiert ist und im Manifest nur sharedUserId deklariert, würde normalerweise nicht starten können. Beim Starten sehen wir die folgende Meldung in logcat:``` signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr -------- Abort message: 'JNI FatalError called: (com.example.abxoverflow.droppedapk) frameworks/base/core/jni/com_android_internal_os_Zygote.cpp:1976: selinux_android_setcontext(1000, 0, "default:privapp:targetSdkVersion=33:complete", "com.example.abxoverflow.droppedapk") failed'

root@kitploit:~
Das liegt daran, dass keine der Definitionen in der [`seapp_contexts`-Datei](https://cs.android.com/android/platform/superproject/main/+/main:system/sepolicy/private/seapp_contexts) übereinstimmt.

Die Regel `user=` in dieser Datei wird [aus `uid` abgeleitet](https://cs.android.com/android/platform/superproject/main/+/main:external/selinux/libselinux/src/android/android_seapp.c;l=819-833;drc=530165a996d8ca5ab5959c33bc040c78951bcb59) (dem ersten Argument von `selinux_android_setcontext()`). In unserem Fall wird es `user=system` sein, bei normalen Apps `user=_app`.

Die andere Sache, die übereinstimmen muss, ist die Regel `seinfo=`, die aus dem dritten Argument von `selinux_android_setcontext()` bis zum ersten Doppelpunkt übernommen wird. Ursprünglich stammt dieser Wert aus dem Vergleich der Signatur der gestarteten App mit den in `/system/etc/selinux/plat_mac_permissions.xml` definierten Signaturen.

Letztendlich versucht unsere App, `user=system seinfo=default` abzugleichen, aber es gibt keine solche Regel in `seapp_contexts`.

Allerdings kann der Prozess für unsere neue App mit `android:sharedUserId="android.uid.system"` zwar nicht gestartet werden, die App kann aber dennoch in einen vorhandenen Prozess geladen werden, wenn dies über das [`android:process`-Attribut](https://developer.android.com/guide/topics/manifest/application-element#proc) angegeben wird. Insbesondere können Apps, die unter `android.uid.system` laufen, `android:process="system"` angeben, um in den `system_server` geladen zu werden.

# Das System zum Absturz bringen

Im Allgemeinen gilt: [Eine App, die einen Absturz des `system_server` auslöst, wird als Bug mit vernachlässigbarer Sicherheitsauswirkung betrachtet](https://bughunters.google.com/learn/invalid-reports/android-platform/5148417640366080/bugs-with-negligible-security-impact#triggering-a-local-temporary-denial-of-service). Hier ist es nur erwähnenswert, weil es Teil einer Exploit-Kette ist, die zwei Neustarts des `system_server` erfordert.

Jedenfalls haben wir eine `Parcelable`-Kette:

* [Die AIDL-Methode `IAlarmManager.set()` akzeptiert `AlarmManager.AlarmClockInfo`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl;l=32-35;drc=ca41ed611ac9c6584c6d5c38ae8428b8e4f3b135)
* [`AlarmClockInfo` ruft das veraltete `readParcelable()` ohne Typargument auf](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/apex/jobscheduler/framework/java/android/app/AlarmManager.java;l=1598;drc=04bf84e220ade9d7ad8ef0b2f7e6ce6ec72841c8) (weil es sich in einem Apex-Modul befindet und diese nicht auf die neuen Methoden umgestellt wurden)
* Ich gebe [`android.content.pm.PackageParser$Activity`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/PackageParser.java;l=8240;drc=7d3ffbae618e9e728644a96647ed709bf39ae759) als `Parcelable`-Klasse an.
* Das Lesen davon führt zum [Aufruf eines beliebigen öffentlichen Konstruktors, der ein einzelnes `Parcel`-Argument akzeptiert](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/PackageParser.java;l=7789-7795;drc=7d3ffbae618e9e728644a96647ed709bf39ae759).
* Ich gebe [`android.os.PooledStringWriter`, der `writeInt(0)` auf dem bereitgestellten `Parcel` aufruft](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/os/PooledStringWriter.java;l=55;drc=782d49826862cbdc9d020fc9d85f8a6f64675dcb) an.
* Dieser `writeInt()`-Aufruf wurde auf einem `Parcel` ausgeführt, das als `data`-Argument von [`onTransact()`](https://developer.android.com/reference/android/os/Binder#onTransact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)) empfangen wurde und von schreibgeschütztem Speicher gestützt wird, der per `mmap` aus `/dev/binder` erstellt wurde. Ein Schreiben darauf verursacht `SIGSEGV`.

Erwähnenswert ist auch, dass [ich die Kombination aus `PackageParser` und `PooledStringWriter` bereits im Rahmen früherer Berichte verwendet habe, beispielsweise für CVE-2023-21098](https://github.com/michalbednarski/TheLastBundleMismatch).

# Der gesamte Ablauf

Dies passiert, sobald man in der App die Schaltfläche „Do everything“ drückt.

1. `RebootBackgroundRunner` wird als separater Prozess gestartet, der nun einfach [`setsid()`](https://man7.org/linux/man-pages/man2/setsid.2.html) verwendet, um den Userspace-Neustart zu überleben, und danach im Hintergrund wartet.
2. Eine neue `PackageInstaller.Session` wird angelegt und ein neues `Checksum`-Objekt wird hinzugefügt. Dieses `Checksum`-Objekt enthält ein Byte-Array mit einer Größe, die während der Serialisierung einen Integer-Überlauf verursacht. Sobald seine Daten deserialisiert sind, sieht das System `PackageInstaller.Session`-Objekte, deren Daten zuvor `Checksum`-Nutzlast waren. Insbesondere werden zwei Sessions injiziert:
    * Eine mit `sessionStageDir="/data/system"` und `prepared="true"` (das bedeutet, dass das Staging-Verzeichnis bereits bereit ist und nicht erstellt werden muss)
    * Eine mit `sessionStageDir="/data/app/dropped_apk"` und `prepared="false"` (das bedeutet, dass das Verzeichnis beim ersten `Session.openWrite()` erstellt wird)
3. Eine neue `PackageInstaller.Session` wird angelegt und dann sofort wieder zerstört. Dadurch wird das System veranlasst, aktualisierte Inhalte in `install_sessions.xml` zu schreiben.
4. Nach einer kurzen Verzögerung wird ein Absturz des `system_server` ausgelöst.
5. Beim nächsten Start des `system_server` wird die Datei `install_sessions.xml` gelesen, und nun können die von uns injizierten `PackageInstaller.Session`-Objekte verwendet werden.
6. `RebootBackgroundRunner` hat während des Userspace-Neustarts im Hintergrund gewartet und führt, sobald es bemerkt, dass das System wieder verfügbar und bereit ist, die nächsten Schritte aus.
7. Mit einer `PackageInstaller.Session` wird eine neue APK aus den Assets extrahiert und in `/data/app/dropped_apk/base.apk` geschrieben.
8. Die andere Session wird verwendet, um `/data/system/packages.xml` zu lesen. Diese Datei wird gepatcht, um zu deklarieren, dass die neu abgelegte APK bereits installiert wurde und dass das für sie verwendete Zertifikat zuvor für `android:sharedUserId="android.uid.system"` verwendet wurde und für diesen Zweck weiterhin vertrauenswürdig ist. Die veränderte Datei wird als `/data/system/packages-backup.xml` geschrieben.
9. Ein weiterer Absturz des `system_server` wird ausgelöst.
10. Wenn `system_server` beim Start `packages-backup.xml` sieht, betrachtet es die ursprüngliche `packages.xml` als beschädigt und verwendet stattdessen das Backup.
11. Nachdem das System die modifizierte `packages.xml` gelesen hat, ist die gerade abgelegte App vorhanden und startet sich selbst über [`ACTION_BOOT_COMPLETED`](https://developer.android.com/reference/android/content/Intent#ACTION_BOOT_COMPLETED). Diese neue App läuft innerhalb des `system_server`, weil sie `<manifest android:sharedUserId="android.uid.system">` und `<application android:process="system">` in der `AndroidManifest.xml` enthält.

# Skripte in `utils/`

Zusammen mit der PoC-App gibt es ein Verzeichnis `utils` mit einigen Skripten.

* `moveapk.sh` verschiebt die kompilierte APK, um sie in die `assets` des Droppers zu legen; auszuführen nach `gradle :droppedapk:assembleRelease`.
* `peeksessions.sh` ermöglicht das Anzeigen des aktuellen Inhalts von `install_sessions.xml` (erfordert ein `eng`/`userdebug`-Build von Android)
* `wipesessions.sh` entfernt alle vorhandenen `PackageInstaller.Session`-Objekte und startet das System neu (erfordert einen `eng`/`userdebug`-Build von Android)

# Wissenswertes

Ich bin nicht sicher, ob das damit zusammenhängt, aber als ich in der Historie nach möglicherweise ABX-bezogenen Fehlern gesucht habe (`cd frameworks/base ; git log -S ABX`), habe ich den [„Stop processing on IOException“-Commit](https://android.googlesource.com/platform/frameworks/base/+/5112cfef2a2023a2629a426154547444593e9f9b%5E!/) gefunden, der **das Hinzufügen eines Unit-Tests mit einer abgeschnittenen ABX-Datei enthält**. Dieser Commit war eine Nachfolgearbeit zu [„Ignore malformed shortcuts“](https://android.googlesource.com/platform/frameworks/base/+/d5122bfaf18f1503e73c1a3a177a56d0f604a008%5E%21/), der im Bulletin als DoS [beschrieben wurde](https://source.android.com/docs/security/bulletin/2022-12-01#framework).
Tool herunterladen