
CVE-2026-43499 exploit de root GhostLock para Chromecast con Google TV (sabrina)
Exploit de root para Chromecast con Google TV (sabrina) mediante CVE-2026-43499 -- un use-after-free en el subsistema futex PI (herencia de prioridad) del kernel de Linux.
Consigue root en un dispositivo con bootloader bloqueado ejecutando Android 14 con kernel 5.15.170 (PGO+BOLT+LTO, clang 17).
GhostLock explota un fallo en remove_waiter() invocado desde la ruta de reversión -EDEADLK en rt_mutex_start_proxy_lock(). La función limpia current->pi_blocked_on (el del requeuer, que ya es NULL) en lugar del de la tarea en espera. El pi_blocked_on de la tarea en espera nunca se limpia, dejando un puntero colgante a un rt_mutex_waiter liberado en la pila del kernel.
Un sched_setattr posterior dispara rt_mutex_adjust_pi -> rt_mutex_adjust_prio_chain, que sigue el puntero colgante y recorre una cadena PI sobre datos controlados por el atacante en una página del heap reclamada -- proporcionando una primitiva de escritura arbitraria mediante rb_erase.
| Etapa | Técnica |
|---|---|
| Fuga de KASLR | perf_event_open con PERF_SAMPLE_IP (condicionado por TID) — la IP mínima del kernel está en .entry.text (+0x10000), _text está alineado a 2MB y el desplazamiento de KASLR es múltiplo de 2MB, por lo que min_ip & ~0x1fffff recupera exactamente el _text en tiempo de ejecución |
| Fuga de task struct | perf_event_open con PERF_SAMPLE_REGS_INTR (condicionado por TID, modo de direcciones del mapa lineal) |
| Validación de la tabla de símbolos | perf-samples de &init_user_ns a partir de los argumentos de registro de security_capable() durante una tormenta de setpriority(-20) y lo compara con kaslr_base + off_init_user_ns — demuestra que el layout de .data/.bss del kernel en ejecución coincide con la tabla de offsets antes de cualquier escritura ciega |
| Fuga de mm_struct | KernelSnitch -- canal lateral de temporización por colisión de hash de futex |
| Heap spray | Coreografía de descarte de SLUB (memfd-close, desbordamiento de CPU-partial) + reclamación de páginas de orden 2 mediante io_uring_setup(256) |
| Superposición de pila | AF_UNIX SOCK_SEQPACKET sendmsg -- move_addr_to_kernel copia un sockaddr de 128 bytes a la pila del kernel, superponiéndose a los campos task/lock/prio del waiter colgante |
| Disparo del recorrido | sched_setattr con una escalera monótona de nice (7 -> 14 -> 19) dispara el recorrido de la cadena PI, uno por cada ronda de superposición |
| Primitiva de escritura | rb_erase Caso 1: `{pc=(TARGET-8) |
| Desactivación de SELinux (recorrido 0) | Cero de 8 bytes en selinux_state limpia enforcing, checkreqprot, initialized y policycap[0..4] — avc_denied() nunca deniega (enforcing=0) y security_compute_av() cortocircuita a permitir-todo (!initialized), por lo que el kernel-SID posterior al swap (u:r:kernel) deja de importar |
| Intercambio de credenciales (recorridos 1+2) | task->cred = fake_cred y task->real_cred = fake_cred (uid=0, caps FULL, user_namespace falso autocontenido en la página del spray; ambas escrituras son necesarias — commit_creds() en exec hace BUG_ON a menos que cred == real_cred) |
| Batería de root | sondas solo con syscalls crudas (uid/SELinux//proc/1//dev/kmsg/wifi/paquetes//data/data/kallsyms) en /data/local/tmp/.ghostlock_out |
| Shell de root | execve("/system/bin/sh") — commit_creds en la ruta de exec copia fake_cred en una credencial de slab limpia; condicionado a que todos los borrados planificados hayan surtido efecto |
rb_right = 0 realiza un único almacenamiento limpio de 8 bytes a cero en una dirección arbitraria del kernel (sin almacenamiento lateral, sin desreferencias salvajes); poner a cero el primer qword de selinux_state da doble permisivo (enforcing=0 + !initialized → permitir todo), lo que desbloquea toda la E/S de archivos posterior al root a pesar del SID del kernel&init_user_ns desde la ruta activa de security_capable() y aborta al flujo solo-cred a menos que coincida con kaslr_base + off_init_user_nsucounts = NULL -- cap_capable coincide en la primera iteración e inc_rlimit_ucounts termina tras un bucle, eliminando la necesidad de filtrar &init_user_nsPERF_SAMPLE_TID garantizan que solo se cuenten las instantáneas de registros del hilo llamante, evitando que los servicios del sistema en caliente dominen el voto de modo5.15.170-android14-11-gf4a1f03072af (aarch64, PGO+BOLT+LTO, clang 17.0.2)CONFIG_FUTEX_PI=y, CONFIG_IO_URING=y, perf_event_paranoid=-1, SELinux en modo enforcing, panic_on_oops=1, sin namespaces de usuarioexport ANDROID_NDK_HOME=/path/to/android-ndk
make -j$(nproc)
Requiere el Android NDK (probado con r27). Genera un binario aarch64 enlazado estáticamente.
adb push ghostlock /data/local/tmp/
adb shell "cd /data/local/tmp && ./ghostlock --cred"
El exploit tarda ~25 segundos (filtraciones de KASLR/task/layout + heap spray + fuerza bruta de KernelSnitch + tres rondas de overlay de 3 segundos). En caso de éxito, escribe la batería de root en /data/local/tmp/.ghostlock_out, el marcador en /data/local/tmp/.ghostlock_root, y ejecuta /system/bin/sh con uid=0 bajo un kernel permisivo.
Ajustes de entorno:
GHOST_SELINUX=0 — omite la escritura de SELinux, ejecuta la ruta probada de solo credenciales (modo 6)GHOST_SELINUX_FORCE=1 — arma la escritura de SELinux incluso si falla la validación del diseño de símbolos del dispositivoGHOST_EXEC=0 — escribe la batería y sale con exit(99) en lugar de ejecutar un shellCRED_ATTEMPTS=n — reintentos de spray completo cuando una ejecución no logra la reclamación (predeterminado 3)Este exploit fue portado y desarrollado por Claude Opus 4.6 (Anthropic) con GLM-5.3 (Z.ai) como consultor de explotación de kernel.