
Exploit locale di root per CVE-2025-21479 (Adreno KGSL) su iQOO Neo8 (SM8475) - lettura/scrittura della memoria fisica, disabilita SELinux, avvia una shell di root
Codice sperimentale di escalation di privilegi locale per CVE-2025-21479, che ha come bersaglio la GPU Qualcomm Adreno (KGSL) su vivo V2301A (iQOO Neo8 / PD2301, Snapdragon 8+ Gen 1 / SM8475, Adreno A730, Android 14, kernel GKI 5.10).
In linea di principio, l'exploit sfrutta il comando CP_SMMU_TABLE_UPDATE di KGSL per pilotare la SMMU della GPU,
ottenendo la capacità di lettura/scrittura arbitraria della memoria fisica, per poi disattivare SELinux, individuare i simboli del kernel e ottenere una shell
root — il tutto senza flashare il firmware né dipendere dallo sblocco del Bootloader.
Solo per ricerca di sicurezza su dispositivi di tua proprietà o autorizzati.
and $12, 0x3) del microcodice Adreno A730 (eureka_panther) presenta un difetto: classifica erroneamente i pacchetti Self-Draw-State (SDS) come
RingBuffer (RB). Pertanto, inviando un CP_SMMU_TABLE_UPDATE falsificato all'interno di un blocco SDS dallo spazio utente, è possibile sovrascrivere
il TTBR0 della GPU e far puntare la tabella delle pagine della GPU a pagine fisiche arbitrarie — da quel momento la GPU può leggere e scrivere memoria
fisica arbitraria.stabilize_rw, il mapping viene rinforzato in una regione auto-mappata da 2 MB,
supportando letture rapide di grandi quantità (fast_pread_bytes ecc.).selinux_state.enforcing nella memoria fisica.
Quindi, dall'indirizzo fisico fisso 0xA8000000, esegue il dump dell'immagine del kernel (limite predefinito 43 MB) —
SM8475 non ha KASLR fisico — e analizza direttamente la struttura kallsyms dal dump grezzo
(con riconoscimento automatico dell'endianness), quindi il KASLR virtuale non è un problema.__do_sys_capset con una shellcode AArch64 (prepare_kernel_cred(0) →
commit_creds(), disattivando anche seccomp). Il processo figlio generato da fork chiama capset() → diventa uid 0 → ripristina il codice kernel
sovrascritto → il processo figlio riesegue il comando specificato (predefinito sh) nel dominio u:r:shell:s0. Alla fine, il codice del kernel è
byte per byte identico a prima dell'esecuzione.Le informazioni complete sul dispositivo sono in device_info.txt.
├── source/
│ ├── cheese.c # 漏洞利用主程序(include 了下面两个文件)
│ ├── kallsyms_lookup.c # 针对原始内核映像的 kallsyms 解析器
│ └── adrenaline.h # KGSL ioctl / 命令定义(作者 hawkes)
├── exploit # 预编译 arm64 二进制(动态链接,minSdk 21,
│ # NDK r30-beta2)
├── device_info.txt # 用于调参的目标设备信息
└── README.md
Nota: cheese.c esegue #include di kallsyms_lookup.c e adrenaline.h;
i tre file devono trovarsi nella stessa directory durante la compilazione.
Qualsiasi Android NDK recente (r25+) è sufficiente; esempio:
# 静态链接(自包含、任意 API 等级可运行;体积较大)
aarch64-linux-android34-clang -o cheese source/cheese.c -static
# 动态链接(体积小,依赖设备自带的 libc;API 等级需匹配设备)
aarch64-linux-android34-clang -o exploit_dyn source/cheese.c
# 若要兼容 API 21+ 设备,请改用 aarch64-linux-android21-clang
adb push exploit /data/local/tmp/
adb shell
chmod +x /data/local/tmp/exploit
/data/local/tmp/exploit
Tutorial dettagliato (clicca qui) (contiene uno strumento one-click)
Una volta riuscito, si ottiene una shell con uid=0 e SELinux viene disattivato per la sessione. Dopo l'escalation, il processo riesegue argv[1..]
(predefinito sh). Ogni esecuzione mostra un conto alla rovescia ("pte wait 3 second") e l'avanzamento della lettura del kernel da 43 MB
("Read X / 43 MB").

$(find /data/app -type f -name "libksud.so" | grep resukisu) late-load --kmi android12-5.10
Le costanti specifiche del dispositivo si trovano all'inizio di source/cheese.c.
KERNEL_PHYS_BASE — base fisica del kernel (predefinito 0xA8000000, SM8475)IDMAP_PG_DIR_OFFSET — offset di idmap_pg_dir (predefinito 0x245a000)selinux_state_offset — offset usato per individuare selinux_state (predefinito
0x729030, indirizzo fisico effettivo = indirizzo fisico di idmap_pg_dir + questo offset)gPhyAddrs[] — candidati di base fisica da provare in sequenzaDEFAULT_KERNEL_SIZE_MB — valore di fallback / limite per la lettura del kernelPrerequisiti: GPU Adreno serie A730/A7xx (firmware affetto), kernel di cui sia possibile risolvere kallsyms dal dump fisico e assenza di KASLR fisico.
can't get GPU r/w — Inizializzazione GPU fallita; attendere prima di tutto; se non funziona, provare CHEESE_ATTEMPT=1..n oppure specificare
manualmente CHEESE_PHYADDR; per il debug di un singolo tentativo, aggiungere CHEESE_NO_RETRY=1.CHEESE_READ_FULL_KERNEL=1 (ampliando il limite con
CHEESE_KALLSYMS_MAX_MB), poi valutare CHEESE_FORCE_MANUAL_PATCHFINDER=1.cheese su cui
questo strumento si basaadrenaline.h del 2020, che fornisce le definizioni delle interfacce ioctlBasato su zhuowei/cheese (repository originale senza licenza). Per il principio completo, vedere il bollettino di sicurezza.
Questo repository è destinato esclusivamente a scopi educativi e di ricerca sulla sicurezza; utilizzalo solo su dispositivi di tua proprietà o autorizzati. L'autore non è responsabile per qualsiasi uso improprio. L'utilizzo di questo strumento può invalidare la garanzia o violare i termini di servizio applicabili.
| Modello | vivo V2301A (iQOO Neo8, nome in codice PD2301, scheda madre taro) |
| SoC | SM8475 (Snapdragon 8+ Gen 1) + Adreno 730 |
| Android | 14 (UP1A.231005.007, SDK 34, qssi-user) |
| Kernel | 5.10.198-gki-gfb2f5774872c |
| SELinux | Enforcing (disattivato in runtime da questo strumento) |
| Variabile | Effetto |
|---|
CHEESE_PHYADDR=<phys> | Forza il base fisico del kernel (salta la tabella di rilevamento gPhyAddrs incorporata). |
CHEESE_ATTEMPT=<n> | Usa gPhyAddrs[n] invece di gPhyAddrs[0]; dopo un errore di inizializzazione della GPU, maybe_retry incrementa automaticamente e ritenta. |
CHEESE_SWAPPER_PG_DIR_OFF=<off> | Salta la scansione di swapper_pg_dir e usa direttamente questo offset assoluto. |
CHEESE_READ_FULL_KERNEL=1 | Legge l'intera immagine del kernel (limite di lettura predefinito DEFAULT_KERNEL_SIZE_MB = 43 MB). |
CHEESE_KALLSYMS_MAX_MB=<MB> | Sovrascrive il limite massimo di lettura di kallsyms. |
CHEESE_FORCE_MANUAL_PATCHFINDER=1 | Forza il patchfinder manuale come fallback (init_cred, prepare_kernel_cred). |
CHEESE_SKIP_GPU=1 | Salta la fase di lettura/scrittura della GPU (solo per debug). |
CHEESE_NO_RETRY=1 | In caso di errore, esce direttamente senza ritentare con un altro numero. |