
Prueba de concepto de escalada de privilegios local para CVE-2023-20938, un use-after-free en el binder de Android, que logra root y desactiva SELinux en builds de prueba vulnerables.
Una prueba de concepto de escalada de privilegios local para CVE-2023-20938, un uso después de liberación (use-after-free) en el driver binder de Android. Desde un shell sin privilegios obtiene uid 0 y establece SELinux a permisivo, luego lanza un shell root.
Objetivo de esta compilación: Android Cuttlefish / Goldfish AVD, x86_64, kernel 5.10.107.
⚠️ Solo para investigación educativa / autorizada. Ejecute esto únicamente contra sus propios dispositivos de prueba y emuladores. El error está corregido en Android actual; este PoC apunta a una compilación de prueba antigua y deliberadamente vulnerable. Sin garantía.

Grabado con asciinema y convertido a GIF con
agg:
drivers/android/binder.c, binder_transaction_buffer_release()binder_transaction_buffer_release() confía en un offsets_size proporcionado por el atacante.
Enviar una transacción cuyo offsets_size es un byte menos que una entrada completa (sizeof(offset) - 1 == 7) hace que la ruta de liberación analice incorrectamente el
flat_binder_object final y decremente en exceso el contador de referencias de un binder_node,
liberándolo mientras una referencia colgante aún está activa.
El exploit encadena una fuga entre cachés (cross-cache leak) en una lectura arbitraria y una escritura
controlada, luego reescribe las credenciales. Etapa por etapa (ver main() en poc_cf.c):
B. Fuga entre cachés (cross-cache leak)
liberar una oleada de binder_nodes (kmalloc-128) -> la página vuelve al asignador
de buddies (buddy allocator) -> reclamar la página con epitems de eventpoll (caché eventpoll_epi,
objetos de 0x80 bytes). Una transacción binder colgante ahora lee
punteros del kernel de un epitem, filtrando un struct file y su dirección de epitem.
C. Lectura arbitraria
reactivar el UAF y usar un fake binder_node rociado para convertir el desenlace
hlist_del de binder en una escritura controlada *(donde) = qué. Dos escrituras
redirigen el f_inode de un archivo víctima a un inodo falso superpuesto sobre el epitem
y lo marcan como S_PRIVATE. FIGETBSZ en el archivo devuelve entonces
*(epitem.event.data + 24): una lectura de 4 bytes dirigible.
D/E/F. Escalada de privilegios
derrotar KASLR (filtrar eventfd_fops mediante f_op), recorrer la lista de tareas de
init_task hasta las credenciales del lanzador, poner a cero su conjunto uid/gid y
seccomp.mode, luego poner a cero selinux_state para establecer SELinux a permisivo.
G. Un lanzador pre-forkeado y libre de eventfd ejecuta un shell root.
Dos procesos no privilegiados necesitan compartir un binder_node para impulsar el UAF;
A través de ITokenManager de hwservicemanager (createToken /get-by-token).
clang -static -Wall -Wextra -o poc_cf poc_cf.c -lpthread
binder.h debe estar junto a poc_cf.c. La compilación es estática a propósito para que el
único binario se ejecute en el objetivo sin necesidad de toolchain.
Desde el directorio del paquete host de Cuttlefish, lance el AVD con el kernel y la initramfs correspondientes:
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 ejecución exitosa recorre las etapas y termina en un shell root:
El exploit es probabilístico. Cada etapa reintenta internamente.
Por lo tanto, ya sea que vea una salida limpia o un pánico en la máquina invitada, simplemente
vuelva a ejecutar (el /data del AVD se borra al reiniciar, así que vuelva a transferir el binario después de un pánico).
Si la tasa de reclamación es baja en un objetivo más lento o con más carga, aumente las esperas
o eleve el conteo de rociado (los #defines cerca del inicio de poc_cf.c):
Los offsets de estructuras y la línea base de símbolos no aleatorizados (*_NOKASLR) son
específicos de esta compilación exacta de 5.10.107 y no funcionarán en otro lugar. Para portar
a un kernel diferente, actualice el bloque de offsets y el bloque de símbolos cerca del inicio
de poc_cf.c con valores del vmlinux correspondiente. Lo probé en Pixel 7 después de extraer los offsets.
| Macro | Por defecto | Significado |
|---|
SETTLE_US | 5000 | pausa después de liberar el nodo, antes del rociado |
FAKE_SPRAY_COUNT | 8 | sendmsg()s bloqueados compitiendo por reclamar la ranura |
FAKE_WAIT_US | 10000 | pausa después de liberar los rociados, antes del desenlace |
PRESKIP_US | 1000 | pausa después del primer sendmsg de sacrificio |
KW_RETRY_COUNT | 8 | intentos por escritura de desenlace individual |
STAGE_C_TRIES | 8 | intentos para activar la lectura arbitraria |