
Proof-of-concept di escalation dei privilegi locale per CVE-2023-20938, un use-after-free in Android binder, che raggiunge root e disabilita SELinux su build di test vulnerabili.
Una prova di concetto di escalation di privilegi locale per CVE-2023-20938, un use-after-free nel driver binder di Android. Da una shell senza privilegi ottiene uid 0 e porta SELinux a permissive, quindi avvia una shell di root.
Target di questa build: Android Cuttlefish / Goldfish AVD, x86_64, kernel 5.10.107.
⚠️ Solo per uso di ricerca autorizzato / didattico. Eseguilo solo contro i tuoi dispositivi di test ed emulatori. Il bug è corretto nella Android attuale; questa PoC ha come target una vecchia build di test deliberatamente vulnerabile. Nessuna garanzia.

Registrata con asciinema e resa in GIF con agg:
drivers/android/binder.c, binder_transaction_buffer_release()binder_transaction_buffer_release() si fida di un offsets_size fornito dall'attaccante. L'invio di una transazione il cui offsets_size è di un byte più corto di una voce completa (sizeof(offset) - 1 == 7) fa sì che il percorso di rilascio interpreti erroneamente il flat_binder_object finale e decrementi eccessivamente il refcount di un binder_node, liberandolo mentre un riferimento pendente è ancora attivo.
L'exploit combina una cross-cache leak in una lettura arbitraria e una scrittura controllata, quindi riscrive le credenziali. Fase per fase (vedi main() in poc_cf.c):
B. Cross-cache leak
free a wave of binder_nodes (kmalloc-128) -> page returns to the buddy
allocator -> reclaim the page with eventpoll epitems (eventpoll_epi cache,
0x80-byte objects). A dangling binder transaction now reads kernel
pointers out of an epitem, leaking a struct file and its epitem address.
C. Arbitrary read
re-trigger the UAF and use a sprayed fake binder_node to turn binder's
hlist_del unlink into a controlled *(where) = what write. Two writes
redirect a victim file's f_inode at a fake inode overlaid on the epitem
and mark it S_PRIVATE. FIGETBSZ on the file then returns
*(epitem.event.data + 24): a steerable 4-byte read.
D/E/F. Privilege escalation
defeat KASLR (leak eventfd_fops via f_op), walk init_task's task list to
the launcher's cred, zero its uid/gid set and seccomp.mode, then zero
selinux_state to drop SELinux to permissive.
G. A pre-forked, eventfd-free launcher execs a root shell.
Due processi senza privilegi devono condividere un nodo binder per guidare la UAF; tramite l'ITokenManager di hwservicemanager (createToken /get-by-token).
clang -static -Wall -Wextra -o poc_cf poc_cf.c -lpthread
binder.h deve trovarsi accanto a poc_cf.c. La build è volutamente statica, così il singolo binario gira sul target senza che sia presente una toolchain.
Dalla directory del pacchetto host di Cuttlefish, avvia l'AVD con il kernel + initramfs corrispondente:
HOME=$PWD ./bin/launch_cvd \
--kernel_path=bzImage \
--initramfs_path=initramfs.img
adb push poc_cf /data/local/tmp/
adb shell /data/local/tmp/poc_cf
Una esecuzione riuscita attraversa le fasi e termina in una shell di root:
L'exploit è probabilistico. Ogni fase riprova internamente.
Quindi, che tu veda un'uscita pulita o un panic della guest, basta riesegui (la /data dell'AVD viene cancellata al riavvio, quindi ricopialo dopo un panic).
Se il tasso di successo del reclaim è scarso su un target più lento o più occupato, allarga le attese o aumenta il numero di spray (i #define vicino all'inizio di poc_cf.c):
| Macro | Predefinito | Significato |
|---|---|---|
SETTLE_US | 5000 | pausa dopo che il nodo è stato liberato, prima dello spray |
FAKE_SPRAY_COUNT | 8 | sendmsg() bloccati in competizione per reclamare lo slot |
FAKE_WAIT_US | 10000 | pausa dopo il rilascio degli spray, prima dell'unlink |
PRESKIP_US | 1000 | pausa dopo il primo sendmsg sacrificale |
KW_RETRY_COUNT | 8 | tentativi per ogni singola scrittura di unlink |
STAGE_C_TRIES | 8 | tentativi per portare online la lettura arbitraria |
Gli offset delle struct e la baseline dei simboli non randomizzati (*_NOKASLR) sono specifici di questa esatta build 5.10.107 e non funzioneranno altrove. Per portare su un kernel diverso, aggiorna il blocco degli offset e il blocco dei simboli vicino all'inizio di poc_cf.c con i valori della vmlinux corrispondente. Ho provato su Pixel 7 dopo aver estratto gli offset.