
Root temporaneo (uid 0) su un ASUS Zenfone 9 con bootloader bloccato tramite CVE-2025-21479 + un leak di indirizzo fisico basato su perf. GPLv3.
Codice di ricerca e note per ottenere root temporaneo (uid 0) su un ASUS Zenfone 9 (AI2202) il cui
bootloader non può essere sbloccato — poiché ASUS ha dismesso il suo strumento di sblocco, rimosso
l'interruttore di sblocco OEM dalle opzioni sviluppatore e rifiuta categoricamente fastboot oem unlock / flashing unlock sul
firmware attuale.
Questo non è uno sblocco del bootloader e non flasha nulla. È un root a runtime: una catena di primitive lato dispositivo che termina con il processo chiamante che detiene le credenziali di root. Un riavvio lo cancella.
Verificato su: ASUS Zenfone 9 (AI2202), Android 14, build 34.0304.2004.145, SPL 2024-07-05,
kernel 5.10.205-android12-9-00029-g3f12df86bfdb-ab11799032, SM8475 / Adreno 730, bootloader bloccato.
Autorizzazione / ambito. Tutto ciò che è qui viene eseguito su un dispositivo di proprietà dell'operatore, tramite una connessione ADB autorizzata. Nessun server del produttore viene attaccato, nessuna chiave di firma o token di sblocco viene aggirato, e nulla viene scritto su alcuna partizione. Il telefono usato per lo sviluppo è un dispositivo di riserva, sottoposto a backup e mantenuto offline. Testa su hardware che possiedi e che puoi permetterti di perdere.
Il risultato è uid 0 nel contesto SELinux u:r:kernel:s0 (eredita il SID di init_cred), cioè
di fatto senza restrizioni. SELinux deve essere in modalità Permissive per questo passaggio — sotto Enforcing il processo viene
invece ucciso, perché lo scambio di credenziali aggira l'hook di SELinux.
Imposta prima ZF9_SERIAL con il seriale del tuo dispositivo (tutti gli script lo leggono):
export ZF9_SERIAL=<your-device-serial>
# 0. una tantum: compila e invia i binari del dispositivo (richiede un Android NDK)
# vedi scripts/ per le esatte invocazioni clang usate
adb push cheese_pa call_capset /data/local/tmp/
# 1. ciclo completo: SELinux -> permissive, patch, esegui un comando come root, ripristina tutto
scripts/root-now.sh id
scripts/root-now.sh sh # shell di root
Lo script ripristina sempre il testo originale del kernel e lo stato di SELinux (protetto da trap), e verifica il ripristino tramite rilettura.
uid=0(root) gid=0(root) context=u:r:kernel:s0,
capset(NULL,NULL) -> 0.gpu fault threshold exceeded 3 faults in 3000 msecs), dopodiché ulteriori comandi falliscono con
EPERM. Il successo della patch osservato varia tra 13/13 e 2/13 dword tra le varie esecuzioni.screenrecord in esecuzione. Gli script avviano
il proprio carico per ogni operazione (anche le letture fanno race), ma non eseguono mai gli interni di questi strumenti
allo scoperto.capset può eseguire spazzatura). Gli script
scrivono l'istruzione di ingresso per ultima, verificano ogni dword e ripristinano in caso di fallimento — ma se un'esecuzione degrada,
riavvia prima di riprovare.Regole di sicurezza che vale la pena mantenere: leggi prima di ogni scrittura, ripristina sempre ciò che patchi, non lasciare mai
attiva una voce di page table iniettata, non toccare la memoria fisica secure/TZ (è fatale), e riavvia per
recuperare uno stato degradato. ROADMAP.md contiene l'elenco completo delle insidie con le prove dietro ciascuna.
ROADMAP.md handoff durevole: costanti verificate, procedura, insidie, percorsi aperti
STATUS.md stato attuale + ricevute di verifica
src/ pa_leak.{c,h} · cheese.c · cheese_pa.c (cavallo di battaglia: modalità PROBE/POKE/SELFTEST/ROOT)
call_capset.c · host_kallsyms.c (risolutore di simboli offline)
scripts/ root-now.sh · patch-dwords.sh · demo-root.sh · verify-backup.sh
tools/ btf_offsets.py
Le immagini del firmware, i pacchetti OTA e i log del dispositivo sono deliberatamente non committati (vedi .gitignore).
GPLv3 — vedi LICENSE.
| Fase | Cosa fa | Dove |
|---|
| 1 | CVE-2025-21479 (Adreno KGSL): un pacchetto SDS viene classificato erroneamente come pacchetto ringbuffer, consentendo allo userland di emettere CP_SMMU_TABLE_UPDATE e puntare il TTBR0 della GPU a un indirizzo fisico scelto dall'attaccante | src/cheese.c |
| 2 | Leak dell'indirizzo fisico: perf_event_paranoid = -1 su questa build, quindi un watchpoint hardware su una pagina di nostra proprietà restituisce PERF_SAMPLE_PHYS_ADDR — l'indirizzo fisico di quella pagina. Sostituisce il leak via pagemap su cui si basava upstream (qui i PFN sono azzerati) | src/pa_leak.{c,h} |
| 3 | Lettura/scrittura fisica arbitraria: costruisci la page table falsa a un indirizzo fisico noto (dalla fase 2), quindi nessuna lotteria di spray e nessuna wild walk | src/cheese_pa.c |
| 4 | Root: risolvi i simboli del kernel offline dall'immagine del firmware, deriva lo slide KASLR on-device, applica una patch a __do_sys_capset con uno stub commit_creds(&init_cred), chiama capset() | src/call_capset.c, scripts/ |