Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
AbxOverflow — 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. | Kitploit
Strumenti/GitHubGitHub/michalbednarski/abxoverflow
Sicurezza AndroidEscalation di PrivilegiAnalisi delle VulnerabilitàAnalisi del CodiceExploitApprendimento e FormazioneSviluppo PayloadBinary Exploitation
GitHubmichalbednarski/abxoverflow

AbxOverflow

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.

682510 mesi faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi
Vedi Repository

Screenshot dell'applicazione Android con titolo AbxDroppedApk e molto testo che descrive l'esecuzione all'interno di system_server

Le correzioni per il problema descritto qui sono apparse sotto CVE-2024-34740 / A-307288067:

  • Bollettino
  • Patch collegata dal bollettino
  • Altre due patch: 1 2

XML Binario di Android

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

root@kitploit:~
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

Scelta del target di iniezione ABX

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

Un'altra possibilità, presentata qui, è modificare il percorso della directory temporanea in cui vengono preparati i file di installazione, poiché openWrite()/openRead() accettano qualsiasi nome file valido purché non ci sia path traversal e posizionano quel file nella directory puntata dal campo stageDir, che viene letto da XML

Sfruttamento dell'iniezione ABX

Ora dobbiamo effettivamente ottenere il nostro array di byte controllato in attributeBytesBase64()

PackageInstaller.Session offre il metodo setChecksums()

Lato system_server, i Checksum forniti vengono opzionalmente verificati rispetto alla firma fornita dal chiamante e poi inseriti in mChecksums

Quando install_sessions.xml viene scritto, checksum.getValue() viene passato a writeByteArrayAttribute, che a sua volta lo passa a attributeBytesBase64()

Ci sono alcuni eventi che attivano la scrittura di install_sessions.xml, uno dei quali è la creazione di una nuova Session, quindi questo exploit, dopo aver impostato Checksum su una sessione, crea una nuova Session per assicurarsi che la prima Session sia stata salvata su file

Ora scriviamo un array di byte con lunghezza 65536, quindi dopo che viene letto, la sua dimensione viene interpretata come zero e il contenuto di quell'array diventa dati grezzi che BinaryXmlPullParser analizza

Non è specificato un conteggio degli attributi; ogni voce ha un byte di tag che contiene token. Nel nibble inferiore c'è uno dei tipi di evento definiti in XmlPullParser, come START_TAG, END_TAG o END_DOCUMENT. Oltre a questi tipi, esiste un tipo speciale ATTRIBUTE, che non viene segnalato tramite next() ma invece dopo aver visto il token START_TAG, il parser scruta i token successivi finché non vede un token non ATTRIBUTE

Poiché non è specificato un conteggio degli attributi, possiamo procedere immediatamente alla chiusura dell'elemento corrente tramite il token END_TAG. Quindi chiudiamo anche </session> poiché tutti gli attributi degli elementi interessanti sono nel tag di apertura <session> ma siamo oltre quel punto, tuttavia ora possiamo aprire un nuovo elemento <session> e impostarli lì

Come notato sopra, FastDataInput è compatibile con il DataInputStream di Java, tranne per il fatto che esiste un metodo aggiuntivo readInternedUTF(), che può fare riferimento a String passate. Poiché non sappiamo quali String sono state internate in precedenza, specifichiamo sempre che è stata scritta una String mai vista prima. Questo aggiunge anche le stringhe appena lette al pool, il che potrebbe causare problemi con la lettura dei dati scritti dopo il nostro punto di iniezione, tuttavia come parte dell'iniezione inserisco tutti i tag di chiusura e il token END_DOCUMENT, quindi dopo la mia iniezione non verrà letto altro da quel file

Utilizzo di PackageInstaller.Session con stageDir manomesso

Quando il sistema legge il file install_sessions.xml modificato, otteniamo un oggetto PackageInstallerSession con stageDir impostato al valore da noi controllato

La mia prima idea era impostare stageDir su /proc/self, poi leggere maps e scrivere mem, ma non ha funzionato

Quando ho provato a usare openRead() per aprire /proc/self/maps, system_server ha aperto con successo il file, ma passare quel file a untrusted_app tramite Binder è stato bloccato da SELinux

Le scritture invece non vengono effettuate passando il descrittore di file grezzo a un altro processo, ma sono proxyate tramite system_server, poiché system_server deve essere in grado di revocare l'accesso in scrittura una volta che la sessione viene confermata. Significa questo che potremmo scrivere su /proc/self/mem? A quanto pare, mentre system_server può aprire quel file, prima di scrivere qualsiasi cosa chiama Os.chmod() su quel file, cosa che non può fare su /proc/self/mem. Quindi non possiamo usarlo per lo sfruttamento qui, anche se a parte questo system_server è in grado di aprire quel file ed eseguire scritture a offset specificati da noi e quel file permette di sovrascrivere pagine di codice, il che ci darebbe direttamente l'esecuzione di codice

Non essendo questa un'opzione, ho provato l'idea successiva: sostituire il contenuto di /data/system/packages.xml. Questo è un file che contiene lo stato di PackageManagerService, in particolare quali app sono installate e quali uid sono loro assegnati

Sembra che a system_server non sia permesso scrivere direttamente su quel file: invece, quando il sistema scrive quel file, prima scrive in un file temporaneo e poi sostituisce packages.xml con quel file temporaneo e abilita la protezione su di esso

Tuttavia, quando legge /data/system/packages.xml, il sistema prima controlla se il file /data/system/packages-backup.xml è presente e se sì considera il primario packages.xml corrotto e legge invece il backup. Durante il normale funzionamento, il file /data/system/packages-backup.xml non è presente e possiamo crearne uno utilizzando una PackageInstallerSession costruita con stageDir impostato su /data/system

Inoltre, system_server può inviare un descrittore di file in sola lettura di /data/system/packages.xml quando uso openRead(), quindi posso facilmente costruire un file modificato contenente solo le mie modifiche senza corrompere i contenuti precedenti

Concessione dell'accesso sharedUserId="android.uid.system"

In packages.xml ho le definizioni delle applicazioni installate registrate, come:```xml <package name="com.android.settings" codePath="/system_ext/priv-app/Settings" ... sharedUserId="1000" ...>

root@kitploit:~
Potremmo scrivere un nuovo APK da qualche parte in `/data/app` (usando un'altra `PackageInstallerSession`) e aggiungere un nuovo elemento `<package>` a `packages.xml` e farlo installare in questo modo?

Sì, tuttavia dobbiamo fornire in `<cert>` una firma valida del nostro APK appena installato e il sistema la verificherà rispetto al file APK durante l'avvio

Potremmo impostare l'attributo `userId` (invece di `sharedUserId` per indicare un APK senza attributo `<manifest android:sharedUserId>` in `AndroidManifest.xml`) al valore che vogliamo?

Sì, tuttavia non dobbiamo usare un valore già utilizzato da un altro pacchetto o `sharedUserId`

Potremmo impostare `sharedUserId="1000"` per la nostra app?

Se lo facciamo, durante l'avvio il sistema convaliderà tale impostazione tramite [`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)

In particolare quel metodo userà [`checkCapability()`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/SigningDetails.java;l=613-637;drc=97a370a95275e79c69e79d7ead11aa38934a5575) per verificare se le firme corrispondono esattamente o se la firma da un lato corrisponde a una delle firme passate dall'altro

Queste "firme passate" provengono da `packages.xml`, in particolare quando abbiamo un elemento `<sigs>` con `<cert>`, possiamo aggiungere un elemento `<pastSigs>` sotto `<sigs>` per aggiungere nuove voci a [`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)

Alla fine, il nostro elemento `<shared-user>` manomesso appare così:```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>

<cert> elemento sotto <pastSigs> viene inserito due volte perché l'ultima firma passata è considerata attuale e quindi non viene considerata

flags="2" significa che il certificato è consentito per sharedUserId

Inoltre, la registrazione di <package sharedUserId="1000"> deve essere applicata all'app che dichiara android:sharedUserId="android.uid.system" nel manifest, quindi deve essere un APK separato da quello che esegue lo sfruttamento

L'app appena installata con system-uid non riesce ad avviarsi

Sebbene sia riuscito a registrare un nuovo certificato attendibile per android:sharedUserId="android.uid.system", normalmente un'app firmata con quel certificato e che dichiara solo sharedUserId nel manifest non riuscirebbe ad avviarsi. Quando la si avvia, vedremo il seguente messaggio 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:~
Questo perché nessuna delle definizioni nel [`seapp_contexts file`](https://cs.android.com/android/platform/superproject/main/+/main:system/sepolicy/private/seapp_contexts) ha corrisposto

La regola `user=` in quel file è [mappata da `uid`](https://cs.android.com/android/platform/superproject/main/+/main:external/selinux/libselinux/src/android/android_seapp.c;l=819-833;drc=530165a996d8ca5ab5959c33bc040c78951bcb59) (primo argomento di `selinux_android_setcontext()`), nel nostro caso sarà `user=system`, per le app normali è `user=_app`

L'altra cosa da far corrispondere è la regola `seinfo=`, che viene presa dal 3° argomento di `selinux_android_setcontext()` fino al primo due punti. Originariamente quel valore deriva dal confronto della firma dell'app lanciata con quelle definite in `/system/etc/selinux/plat_mac_permissions.xml`

Alla fine la nostra app tenta di far corrispondere `user=system seinfo=default` e non esiste una tale regola in `seapp_contexts`

Tuttavia, mentre il processo per la nostra nuova app con `android:sharedUserId="android.uid.system"` non può essere avviato, l'app può comunque essere caricata in un processo esistente se specificato tramite l'[attributo `android:process`](https://developer.android.com/guide/topics/manifest/application-element#proc). In particolare, le app in esecuzione sotto `android.uid.system` possono specificare `android:process="system"` per essere caricate in `system_server`

# Crash del sistema

In generale, [un'app che causa il crash di `system_server` è considerata un bug con impatto sulla sicurezza trascurabile](https://bughunters.google.com/learn/invalid-reports/android-platform/5148417640366080/bugs-with-negligible-security-impact#triggering-a-local-temporary-denial-of-service) e qui vale la pena notarlo solo perché è parte di una catena di exploit che richiede due riavvii di `system_server`

Comunque, abbiamo una catena `Parcelable`:

* [`IAlarmManager.set()` Metodo AIDL accetta `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` chiama il deprecato `readParcelable()` senza argomento di tipo](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/apex/jobscheduler/framework/java/android/app/AlarmManager.java;l=1598;drc=04bf84e220ade9d7ad8ef0b2f7e6ce6ec72841c8) (perché è in un modulo apex e questi non sono stati passati ai nuovi metodi)
* Specifico [`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) come classe `Parcelable`
* La lettura di ciò porta all'[invocazione di qualsiasi costruttore pubblico che accetti un singolo argomento `Parcel`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/PackageParser.java;l=7789-7795;drc=7d3ffbae618e9e728644a96647ed709bf39ae759)
* Specifico [`android.os.PooledStringWriter`, che chiama `writeInt(0)` sul `Parcel` fornito](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/os/PooledStringWriter.java;l=55;drc=782d49826862cbdc9d020fc9d85f8a6f64675dcb)
* Quella chiamata `writeInt()` è stata effettuata sul `Parcel` ricevuto come argomento `data` di [`onTransact()`](https://developer.android.com/reference/android/os/Binder#onTransact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)), che è supportato da memoria di sola lettura `mmap`-ata da `/dev/binder`. Scrivere su quella causa `SIGSEGV`

Da notare anche, [ho usato la combinazione `PackageParser`+`PooledStringWriter` come parte di segnalazioni precedenti, ad esempio per CVE-2023-21098](https://github.com/michalbednarski/TheLastBundleMismatch)

# L'intero flusso

Ecco cosa succede una volta che premi il pulsante "Do everything" all'interno dell'app

1. `RebootBackgroundRunner` viene avviato come processo separato, che ora userà [`setsid()`](https://man7.org/linux/man-pages/man2/setsid.2.html) per sopravvivere al riavvio userspace e successivamente resterà in attesa in background
2. Viene allocato un nuovo `PackageInstaller.Session` e viene aggiunto un nuovo oggetto `Checksum`. Quel oggetto `Checksum` contiene un array di byte con una dimensione che causerà un integer overflow durante la serializzazione e una volta che i suoi dati vengono deserializzati, il sistema vedrà `PackageInstaller.Session` i cui dati erano precedentemente il payload di `Checksum`. In particolare, vengono iniettate due sessioni:
   * Una con `sessionStageDir="/data/system"` e `prepared="true"` (significa che la directory stage è già pronta e non deve essere creata)
   * Una con `sessionStageDir="/data/app/dropped_apk"` e `prepared="false"` (significa che la directory verrà creata al primo `Session.openWrite()`)
3. Un nuovo `PackageInstaller.Session` viene allocato e poi immediatamente distrutto. Questo innesca la scrittura dei contenuti aggiornati in `install_sessions.xml`
4. Dopo un piccolo ritardo, viene innescato un crash di `system_server`
5. Durante il successivo avvio di `system_server`, il file `install_sessions.xml` viene letto e ora i `PackageInstaller.Session` che abbiamo iniettato possono essere utilizzati
6. `RebootBackgroundRunner` è stato in attesa in background durante il riavvio userspace e una volta che nota che il sistema è di nuovo attivo e pronto, esegue i passaggi successivi
7. Utilizzando un `PackageInstaller.Session`, un nuovo APK viene estratto dagli assets e scritto in `/data/app/dropped_apk/base.apk`
8. L'altra sessione viene utilizzata per leggere `/data/system/packages.xml`, quel file viene modificato per dichiarare che l'APK appena depositato è già stato installato e il certificato usato per esso era stato usato in precedenza per `android:sharedUserId="android.uid.system"` ed è ancora attendibile per quello scopo. Il file alterato viene scritto come `/data/system/packages-backup.xml`
9. Viene innescato un altro crash di `system_server`
10. Quando `system_server` durante l'avvio vede `packages-backup.xml`, considera l'originale `packages.xml` come corrotto e usa il backup
11. Poiché il sistema ha letto il `packages.xml` modificato, l'app appena depositata è presente e si avvia da [`ACTION_BOOT_COMPLETED`](https://developer.android.com/reference/android/content/Intent#ACTION_BOOT_COMPLETED). Quella nuova app viene eseguita all'interno di `system_server` perché ha `<manifest android:sharedUserId="android.uid.system">` e `<application android:process="system">` in `AndroidManifest.xml`

# Script in `utils/`

Insieme all'app PoC c'è una directory `utils` con alcuni script

* `moveapk.sh` sposta l'APK compilato da depositare in `assets` del dropper, da eseguire dopo `gradle :droppedapk:assembleRelease`
* `peeksessions.sh` permette di visualizzare il contenuto corrente di `install_sessions.xml` (richiede build `eng`/`userdebug` di Android)
* `wipesessions.sh` cancella qualsiasi `PackageInstaller.Session` presente e riavvia il sistema (richiede build `eng`/`userdebug` di Android)

# Curiosità

Non sono sicuro se questo sia correlato, ma guardando la cronologia per possibili bug legati ad ABX (`cd frameworks/base ; git log -S ABX`) ho trovato il commit ["Stop processing on IOException"](https://android.googlesource.com/platform/frameworks/base/+/5112cfef2a2023a2629a426154547444593e9f9b%5E!/), che **include l'aggiunta di un test unitario con file ABX troncato**. Quel commit era un follow-up di ["Ignore malformed shortcuts"](https://android.googlesource.com/platform/frameworks/base/+/d5122bfaf18f1503e73c1a3a177a56d0f604a008%5E%21/), che è stato [descritto nel bollettino come DoS](https://source.android.com/docs/security/bulletin/2022-12-01#framework)
Scarica lo strumento