
adb-driven KernelSU loader para Google Pixel de serie: R/W temporal del kernel mediante CVE-2026-43499 (GhostLock), que luego carga en fase tardía un kernelsu.ko con firma coincidente para el KMI en ejecución. Independiente del gestor.
Una herramienta basada en adb que convierte un Google Pixel estándar con bootloader bloqueado en un dispositivo rooteado con KernelSU sin desbloquear el bootloader ni modificar la imagen de arranque. Desde el host ejecuta un exploit de kernel en espacio de usuario sin privilegios en el dispositivo para obtener acceso de lectura/escritura temporal al kernel, utiliza esa primitiva para parchear una credencial de root y luego carga tardíamente un módulo de kernel cargable de KernelSU (kernelsu.ko) en el kernel GKI en ejecución y entrega el control al gestor de KernelSU que ya esté instalado (KernelSU, KernelSU-Next, SukiSU u otra variante). Está dirigido a Pixels con Android 17 en kernels GKI 6.1 y 6.6 y gestiona todo el flujo a través de adb shell para una secuenciación determinista y registros con marcas de tiempo.
La carga útil del lado del dispositivo es la cadena LPE completa de terceros para CVE-2026-43499 ("GhostLock"), un use-after-free de pila en la herencia de prioridad futex/rtmutex en kernel/locking/rtmutex.c. En la ruta de reversión de requeue-PI, remove_waiter() limpia pi_blocked_on en el requeuer (current) en lugar de en el waiter real, dejando un puntero colgante a una ranura de pila del kernel liberada que contenía un rt_mutex_waiter. El fallo es alcanzable desde un proceso normal sin privilegios:
owner, waiter, consumer) construyen una cadena PI; el waiter se estaciona en FUTEX_WAIT_REQUEUE_PI, el hilo principal dispara FUTEX_CMP_REQUEUE_PI, y un sched_setattr del consumer impulsa la reversión.pselect()/select() controlado (o una ruta TCP_ZEROCOPY_RECEIVE en algunos objetivos 6.1) cuyas palabras fd_set caen sobre la estructura del waiter, escribiendo un rt_mutex_waiter plano falsificado para que el puntero colgante recorra campos de árbol rb y de bloqueo controlados por el atacante — una primitiva de escritura de puntero único controlado.mm_struct// rociados.El estado de root y SELinux se parchea entonces a través de la primitiva de tubería: el cred de la tarea hija raíz se pone a cero con uid/gid 0 y conjuntos de capacidades completos, su osid/sid de SELinux se establece en SECINITSID_KERNEL, seccomp se limpia y selinux_state.enforcing se establece en 0.
La cadena de exploit, el oráculo KASLR y el canal lateral KernelSnitch se originan en la investigación IonStack Part II — GhostLock de NebuSec (el PoC NebuSec/CyberMeowfia, Apache-2.0), adaptada aquí para Pixel/aarch64. Ver Atribución y Licencia.
Exactamente una etapa de la cadena puede provocar un pánico en el kernel: la derivación del desplazamiento KASLR, que compite por una página que espera haber reclamado. Todas las demás etapas son seguras para reintentos, y la base del texto del kernel es fija durante la vida de un solo arranque. El flujo del host se divide según esta propiedad:
KASLR_BASE en su entorno. La escritura del waiter falsificado redirige el ctl_table.data del sysctl random_table a un puntero conocido del texto del kernel; leer /proc/sys/kernel/random/boot_id lo filtra a través de proc_do_uuid(), y restar el desplazamiento de la imagen produce _stext/la base KASLR. restore_slide_boot_id() repara el ctl_table.data corrupto. Debido a que una carrera perdida reinicia el dispositivo, una espera de arranque precede a cada intento y una verificación de actividad clasifica una desaparición como un pánico. En caso de éxito, el registro del dispositivo emite slide-kaslr-ok pid=<pid> base=<hex>, y la base se fija al arranque actual.KASLR_BASE=0x<base> exportado. Esta ruta nunca provoca pánico y se repite hasta que id informe a través del su temporal.El dispositivo conectado se resuelve contra data/targets.json en tiempo de ejecución; nada está codificado para el dispositivo. Se producen dos resoluciones independientes:
uname -r), ya sea de la entrada de objetivo coincidente o derivado de la cadena de versión (p. ej. android14-6.1).La reutilización de una carga útil en muchos dispositivos se deriva de la estructura GKI/KMI. Cada dispositivo en la misma compilación GKI ejecuta el vmlinux idéntico byte por byte, y los desplazamientos de campos de estructura (task_struct->cred, cred->uid, …) están congelados durante la vida de una rama KMI por el contrato de tipos KMI y la aplicación de CRC de MODVERSIONS. Las direcciones absolutas de símbolos del kernel, por el contrario, las decide el enlazador por compilación ab<NNN>, por lo que los desplazamientos fijos del exploit pertenecen a un vmlinux específico; por lo tanto, imágenes de kernel distintas requieren cargas útiles distintas incluso cuando su KMI coincide. data/targets.json codifica exactamente esto: muchos dispositivos se deduplican en una carga útil clave por imagen de kernel, mientras que una imagen de kernel diferente obtiene la suya propia.
La carga tardía del LKM requiere un kernel GKI (5.10+) con soporte de módulos cargables y un .ko con KMI coincidente. El módulo de kernel de KernelSU autentica a su gestor verificando el bloque de firma v2 del APK del gestor en el kernel y comparando el SHA-256 del certificado de firma contra un par KSU_EXPECTED_SIZE/KSU_EXPECTED_HASH compilado en el .ko. El kernelsu.ko incluido en una versión del gestor y el APK de ese gestor comparten por tanto una identidad de firma; un ksud no coincidente carga el controlador pero nunca establece el bit de autorización del gestor, dejando el dispositivo sin root utilizable.
El flujo respeta este vínculo: resuelve la ruta del APK del gestor instalado (pm path <paquete del gestor>), extrae el APK, extrae lib/arm64-v8a/libksud.so como binario ksud, y si no hay gestor instalado aborta. Con el root temporal retenido, ese ksud se prepara como ejecutable propiedad de root y se invoca como ksud late-load --kmi <kmi> --package-name <paquete del gestor>. La carga tardía detecta el KMI actual, extrae "{kmi}_kernelsu.ko" de sus recursos integrados, realiza la reubicación manual de símbolos (resolviendo cada símbolo SHN_UNDEF contra /proc/kallsyms, reescribiendo las entradas a SHN_ABS) y llama a init_module(2) en el búfer parcheado. Luego ejecuta el resto de la canalización de arranque que init haría (instalar ksud, restorecon, cargar sepolicy.rule y perfiles de root, ejecutar scripts post-fs-data/stage, montar la superposición de módulos).
La carga tardía se demoniza y vuelve a imponer SELinux en su hijo bifurcado, que desmonta el daemon su temporal del exploit; por lo tanto, la verificación no debe pasar por su. En su lugar, el controlador cargado se consulta directamente a través de su superficie de syscalls, alcanzable desde un shell simple sin root: se sondea ksud debug version y se analiza la versión del kernel informada. Una versión no vacía y distinta de cero confirma que el controlador está residente y respondiendo. La ruta de instalación del controlador es el mecanismo de magia reboot(2) → fd de instalación → KSU_IOCTL_GET_INFO (reboot(0xDEADBEEF, 0xCAFEBABE, 0, &fd) instala un fd anónimo [ksu_driver]; GET_INFO devuelve {version, flags, features, uapi_version}), con el canal heredado prctl(0xDEADBEEF, …) probado como respaldo. La misma sonda ejecutada al inicio cortocircuita todo el flujo cuando el módulo ya está residente para el arranque actual.
El exploit prepara su su temporal en /apex/com.android.virt/bin/su, en un tmpfs montado sobre ese directorio bin del apex dentro del espacio de nombres de montaje de adbd, porque ese directorio precede a /system/bin en el PATH del shell — de modo que un su simple en adb shell alcanza el temporal mientras el flujo se ejecuta. La carga tardía desmonta entonces el daemon su temporal (ver (e)) sin eliminar la sombra, lo que deja un adb shell su simple ejecutando un cliente huérfano que falla con su: connect daemon: Permission denied aunque root esté funcionando, y deja los binarios reales del apex (crosvm, virtmgr, vm, …) ocultos. Una vez que la verificación informa un controlador vivo, el flujo desmonta el tmpfs de preparación — a través del propio /system/bin/su de KernelSU, ya que el daemon del exploit ya no existe — elimina el cliente su temporal, el socket y el registro, e informa qué su resuelve ahora un simple. Es de mejor esfuerzo: en caso de fallo advierte con el comando manual en lugar de fallar la ejecución, y un reinicio limpia el montaje de todos modos.
adb en el host, con el dispositivo autorizado (depuración USB habilitada).kernelsu.ko integrado.artifacts/exploits/ (ver Compilación de cargas útiles).# Un dispositivo en adb; gestor instalado; cargas útiles compiladas.
bin/pixel-ksu-root
El controlador resuelve el dispositivo contra data/targets.json, ejecuta el flujo KASLR de dos fases, carga tardíamente el módulo a través del ksud derivado del gestor y verifica mediante la syscall del controlador. Sale con código distinto de cero si no se resuelve ninguna carga útil para el dispositivo, si no hay gestor instalado o si la verificación nunca informa un controlador vivo.
KASLR_BASE=0x<hex> — se pasa a la carga útil del dispositivo durante la Fase B para reproducir contra una base fija ya derivada por arranque. Sin establecer durante la Fase A para que la carga útil derive la base por sí misma.ANDROID_NDK_HOME — ruta al NDK de Android, requerida solo al compilar cargas útiles.API — nivel de API de Android para la cadena de herramientas NDK al compilar cargas útiles (predeterminado 35).pixel-ksu-root/
├── bin/ Punto de entrada del controlador del host (flujo impulsado por adb)
├── data/
│ └── targets.json Tabla de resolución dispositivo→carga útil y dispositivo→KMI
├── exploit/ Código fuente de la carga útil CVE-2026-43499 de terceros
│ ├── Makefile Compilación aarch64 NDK por objetivo
│ ├── src/ Conjunto de código fuente base android15-6.6
│ │ ├── main.c slide.c fops.c pipe.c root.c preload.c util.c
│ │ ├── su_daemon.c Helper su temporal generado por la cadena
│ │ ├── kernelsnitch/ Cabeceras del canal lateral de ocupación de hash futex
│ │ └── targets/ target.h por dispositivo+compilación (desplazamientos del kernel)
│ └── src/61/ Conjunto de código fuente android14-6.1 (slide61.c, ruta TCP)
├── lib/ Funciones compartidas/helper de shell del lado del host
├── scripts/
│ └── build-payloads.sh Compila y deduplica el conjunto de cargas útiles
├── artifacts/
│ └── exploits/ Archivos .so de cargas útiles compiladas y deduplicadas
└── docs/ Notas de diseño y análisis
scripts/build-payloads.sh envuelve el exploit/Makefile por objetivo y emite el conjunto de cargas útiles deduplicadas nombrado en data/targets.json en artifacts/exploits/. Compila un .so por grupo de desplazamientos único (del objetivo build_from de ese grupo) en lugar de uno por dispositivo.
export ANDROID_NDK_HOME=/ruta/al/android-ndk # debe contener la cadena de herramientas aarch64 NDK
scripts/build-payloads.sh # compila cada carga útil en data/targets.json
El Makefile selecciona la cadena de herramientas Clang aarch64 NDK de ANDROID_NDK_HOME y compila un solo objetivo a la vez; API (predeterminado 35) elige el controlador aarch64-linux-android<API>-clang. El conjunto de código fuente se elige por familia de kernel — los objetivos android15-6.6 compilan la base src/, los objetivos android14-6.1 compilan src/61/ — y los desplazamientos absolutos del kernel de cada objetivo provienen de src/targets/<nombre-de-código>-<compilación>/target.h. Para compilar un solo objetivo directamente:
make -C exploit TARGET=husky-CP2A.260705.006
data/targets.json lista 19 entradas de dispositivo/compilación que cubren 18 modelos de Pixel (bluejay aparece en dos compilaciones de firmware), agrupadas en 5 cargas útiles de desplazamientos de kernel. La selección es por imagen de kernel, por lo que los dispositivos que comparten un vmlinux se deduplican en una carga útil; una imagen de kernel diferente obtiene la suya propia.
Los dispositivos en la imagen de kernel compartida android14-6.1-a (6.1.157-android14-11-gbd23337e42e7-ab14791245) abarcan las familias Pixel 6/6 Pro/6a, 7/7 Pro/7a, 8/8 Pro y 9/9 Pro/9 Pro XL/9 Pro Fold; android14-6.1-b y android14-6.1-akita aíslan modelos en el mismo KMI cuya compilación de kernel o desplazamientos difieren; android15-6.6 cubre la familia Pixel 10.
NebuSec/CyberMeowfia bajo Apache-2.0; descubrimiento acreditado a las herramientas VEGA de NebuSec, divulgado el 2026-07-07.exploit/ de terceros añade desplazamientos de objetivo android14-6.1 y android15-6.6 y un daemon de carga tardía de KernelSU sobre el exploit de NebuSec; no lleva licencia separada y hereda los términos Apache-2.0 ascendentes.ksud y el modelo de autorización del gestor que esta herramienta carga tardíamente.El código fuente de terceros bajo exploit/ conserva su licencia ascendente (Apache-2.0 para el exploit derivado de NebuSec). Este proyecto es agnóstico respecto al gestor: carga tardíamente la variante de KernelSU cuyo gestor esté instalado y no apunta ni incluye ninguna bifurcación específica.
ksudinit_module y detección del controladorapk_sign.rssk_buffpipe_bufferashmem_miscs[0].fops con un file_operations falsificado cuyas ranuras apuntan todas a funciones reales del kernel compatibles con el prototipo (configfs_bin_write_iter, configfs_read_iter, copy_splice_read, ashmem_ioctl, noop_llseek, …), de modo que la CFI de borde frontal se satisface mientras read/write/splice en un fd de ashmem producen un R/W restringido del kernel.pipe_buffer en la página de slab filtrada (page apuntando a cualquier objetivo mediante la conversión vmemmap↔direct-map, ops = anon_pipe_buf_ops, PIPE_BUF_FLAG_CAN_MERGE), de modo que read()/write() simples en la tubería mueven bytes hacia y desde direcciones arbitrarias del kernel — un R/W arbitrario estable del kernel.uid=0/proc/sys/kernel/random/boot_id en vivo con el boot registrado en el momento de la captura; cualquier cambio descarta la base y regresa a la Fase A. Un bucle externo repite derivar→reproducir a través de reinicios.adb shellumount| Carga útil | KMI | Compilado desde | Dispositivos |
|---|
android14-6.1-a | android14-6.1 | bluejay-CP2A.260705.006 | oriole, raven, bluejay (CP2A), panther, cheetah, comet, shiba, husky, lynx, caiman |
android14-6.1-b | android14-6.1 | komodo-CP2A.260705.006 | komodo, tegu, tokay |
android14-6.1-akita | android14-6.1 | akita-CP2A.260805.005 | akita |
android14-6.1-cp1a | android14-6.1 | bluejay-CP1A.260405.005 | bluejay (CP1A) |
android15-6.6 | android15-6.6 | blazer-CP2A.260705.006 | frankel, blazer, mustang, rango |