
Exploit Dirty Pagetable per CVE-2025-8045
Un exploit di escalation dei privilegi locale per CVE-2025-8045, una use-after-free nel driver del kernel della GPU Arm Mali (kbase). Un processo locale non privilegiato mette in gara il percorso di dump della coda KCPU del CSF per liberare e riallocare un cpu_queue.buffer, trasforma l'allocazione pendente in una tabella delle pagine di sua proprietà ("dirty pagetable") e usa questa primitiva per ottenere lettura/scrittura fisica arbitraria del kernel — culminando in una shell di root con SELinux impostato a permissive.
Testato su Pixel 7 Pro (build
BP3A.250905.014). Gli offset hardcoded inexploit.csono calibrati per questo target; altri dispositivi/build richiedono di ricavare nuovamente gli offset (vedi Tuning e portabilità).
CVE-2025-8045 è una use-after-free nei driver del kernel Arm Valhall e 5th Gen GPU Architecture (r53p0–r54p1). Un processo locale non privilegiato può pilotare le operazioni di elaborazione della GPU in modo da lasciare il driver a operare su memoria già liberata, aprendo la porta a divulgazione della memoria del kernel, corruzione ed esecuzione di codice.
Questo exploit raggiunge il bug tramite l'interfaccia della coda KCPU del CSF esposta da /dev/mali0:
CQS_WAIT insoddisfacibile seguito da un FENCE_SIGNAL. L'attesa blocca la coda così che il timer del fence scatti più tardi, su un confine prevedibile del bucket della timer-wheel.KBASE_IOCTL_CS_CPU_QUEUE_DUMP guida kbase_csf_cpu_queue_dump_buffer(). Far gareggiare la transizione di stato della coda (ISSUED → COMPLETE/PENDING) contro il percorso di dump libera cpu_queue.buffer mentre è ancora referenziato — la UAF.L'exploit è un exploit di temporizzazione: prima calibra la scadenza, poi la prevede a ogni round e lancia una "wave" di chiamate di dump innocue per allargare la finestra di gara attorno al momento della liberazione.
Poiché una pagina finisce per essere sia un'allocazione Mali sia una tabella delle pagine CPU attiva, il processo rimane parcheggiato per sempre invece di uscire — liberarla sarebbe un double-free e manderebbe in panic il kernel.
# Android / arm64 (NDK)
aarch64-linux-android31-clang -O2 -o exploit exploit.c
./exploit
In caso di successo ottieni una shell di root (uid=0) con SELinux permissive. Flusso di esempio:
[*] pid=... cpus=8 wave=64 harvest cpu=7 rounds=256
[*] round 1 calibrate deadline t0+... ms phase ... ms
[*] round N read +... us
[+] UAF page: spray region R page P @ ..., 1 live descriptor
[+] entry 0 = ... -- a CPU page table, writable through the GPU
[+] alias: dirty pagetable
[+] selinux_state.enforcing = 0 @ phys ...
[+] sel_read_enforce patched @ phys ...
[+] root shell: uid=0 euid=0
Se esegue tutti i round senza successo, cpu_queue.c:71 in dmesg indica che la gara sta atterrando ma il recupero non è andato a segno — regola i parametri di reclaim/wave.
I valori in exploit.c sono calibrati per il Pixel 7 Pro, build BP3A.250905.014. Gli offset e le costanti sono specifici di dispositivo/kernel e devono essere ricalcolati per qualsiasi altro target:
KERNEL_PA_BASE, SEL_READ_ENFORCE, INIT_CRED, COMMIT_CREDS, SELINUX_STATE — offset dei simboli/fisici del kernel.ARM64_PTE_FLAGS — template del descrittore di ultimo livello per la configurazione MMU del target.WAVE_*, SWEEP_*, BUCKET_NS, WAIT_NS, dimensioni degli spray — manopole per il timing della gara e l'heap-grooming.Aggiorna al rilascio del driver corretto fornito da Arm per rimediare.
Questo codice è pubblicato esclusivamente per ricerca sulla sicurezza difensiva, ingegneria delle detection e istruzione. Non eseguirlo su sistemi che non possiedi o per cui non hai un'autorizzazione scritta esplicita al test. L'autore non si assume alcuna responsabilità per un uso improprio o per qualsiasi danno causato. Usarlo su sistemi senza permesso è illegale.
| Fase | Cosa succede |
|---|
| Calibrazione | Il round 1 misura il fronte ISSUED→COMPLETE per apprendere la fase del bucket della timer-wheel. |
| Previsione + sweep | I round successivi prevedono la scadenza e spazzano l'offset di lettura ±ms per centrare esattamente la liberazione. |
| Wave | 64 thread accumulano chiamate CS_CPU_QUEUE_DUMP su csf.lock per racchiudere la scadenza e allungare la finestra. |
| Recupero | Svuota preventivamente le free list UNMOVABLE, poi spruzza pagine GPU marcate così che il blocco liberato torni sotto il nostro controllo. |
| Dirty pagetable | Libera di nuovo e spruzza immediatamente tabelle delle pagine CPU nella stessa pagina; l'allocazione GPU ora è in alias con una tabella attiva di ultimo livello. |
| Lettura/scrittura arbitraria | Una PTE di una finestra mai riutilizzata viene puntata a pagine fisiche del kernel; la TLB viene forzata a un flush tramite un round-trip PROT_NONE su un grande mapping scratch. |
| Root | Azzera selinux_state.enforcing, applica la patch a sel_read_enforce con commit_creds(init_cred), rilascia un processo figlio in attesa che esegue una shell di root. |