
Adaptador de GhostLock (CVE-2026-43499) para kernel Android 4.19.152-perf+
Exploit de kernel para dispositivos OnePlus/OPPO/realme con bootloader bloqueado. Consigue root e instala KernelSU sin desbloquear el bootloader ni modificar la imagen de arranque. Detección automática en tiempo de ejecución de la versión del kernel con una tabla de offsets multi-dispositivo.
CVE-2026-43499 — Use-After-Free en Futex PI (Priority Inheritance)
Afecta al kernel de Linux 2.6.39 ~ 7.1. Corregido en mainline 7.1 (commit 3bfdc63936dd). Android GKI 6.12.x sigue siendo vulnerable.
La syscall pselect6 copia datos de fd_set a la pila del kernel. Cuando se combina con el mecanismo de waiter de futex PI, un stack frame liberado puede reutilizarse como estructura rt_mutex_waiter. El rebalanceo del rb-tree durante el recorrido de la cadena PI escribe entonces valores controlados en direcciones arbitrarias del kernel.
| Dispositivo | SoC | Kernel | Estado |
|---|---|---|---|
| OnePlus Ace 6T (PLR110) | SM8845 | 6.12.38-...-ab14275539 | Funciona |
| OnePlus Ace 6T (PLR110) | SM8845 | 6.12.38-...-ab14552068 | Funciona |
| OnePlus 15 (CPH2749) | SM8850 | 6.12.23-...-ab14541642 | Funciona |
| Xiaomi 17 (pudding) | SM8850 | 6.12.23-...-abogki463945075 | Funciona |
| Xiaomi 17 (pudding) | SM8850 | 6.12.69-...-abogki514973465 | Funciona (actualización de agosto de 2026) |
| OnePlus 13 (IN2060) | SM8750 | 6.6.89-...-abogki446052083 | Funciona (PSELECT_SHIFT=-2) |
| OPPO Pad 4 Pro | SM8750 | 6.6.89-...-ab14358676 | Funciona (PSELECT_SHIFT=-2) |
| Dispositivo | SoC | Kernel | Notas |
|---|---|---|---|
| OnePlus 15T (PLZ110) | SM8845 | 6.12.38-...-ab14552068 | Mismo kernel que el Ace 6T. Verificado en QEMU, SP diff=-64. |
La superposición de pila de pselect solo funciona cuando el rt_mutex_waiter liberado cae dentro de la región controlable por el usuario del buffer stack_fds. Dónde cae el waiter lo determina la salida del compilador (PGO + LTO), no la versión del kernel. Consulta Distribución de pila para más detalles.
| Dispositivo | SoC | Kernel | Motivo |
|---|---|---|---|
| OPPO Find X9 Ultra | SM8750 | 6.12.58-android16-6 | PGO elimina el frame de do_futex → SP diff=+32, waiter word=14. No existe ningún shift seguro. |
| OPPO Find X7 | — | 6.1.157 | GKI 6.1: waiter en word 13 (todos los dispositivos OPLUS/GKI 6.1) |
| realme RMX5070 | SM6650 | 6.1.141 | GKI 6.1: waiter en word 13 |
| realme RMX3852 | SM8635 | 6.1.141 | Misma rama 6.1 que RMX5070 |
| OnePlus 13R / Ace 5 | SM8650 | 6.1.x | Misma rama 6.1 |
| OnePlus 12 | SM8650 | 6.1.141 | GKI 6.1: do_futex inline por PGO, waiter word=13/19 |
| OPPO Pad 5 (OPD2502) | MT6878 | 6.1.134 | Misma rama 6.1 |
| OPPO PKW110 | — | 5.15.180 | frame de do_futex 0x140 (4,5 veces el normal) → waiter word=-29, inalcanzable |
| Motorola Edge 60 Fusion | MT6878 | 6.1.145 | GKI 6.1: waiter en word 13 (no OPLUS, mismo resultado) |
| iQOO Z9 5G | — | 5.15.178 | frame de do_futex demasiado grande, waiter inalcanzable. No es un dispositivo OPLUS (vivo). |
Dos rutas de root, seleccionadas automáticamente según las capacidades del dispositivo:
Requiere off_ashmem_misc_fops != 0 (ashmem en C con miscdevice estático en BSS).
PI write (mode=4) → redirect miscdevice fops to fake fops (via W0 pi_tree)
configfs r/w established
→ pipe physrw (1-byte precise kernel r/w)
→ SELinux enforcing = 0 (single byte, no policycap corruption)
→ UMH: inject work_struct into system_unbound_wq
kernel executes /data/local/tmp/a/e --umh as UID 0
→ root script → ksud late-load → KSU installed
Ventajas sobre la ruta B:
selinux_state.policycap (soluciona los problemas de red en OnePlus 13)Actualmente disponible en: OnePlus 13 (kernel 6.6, ashmem en C). No disponible en dispositivos con ashmem en Rust (GKI 6.12) — el miscdevice se asigna en el heap, por lo que su dirección no es predecible en tiempo de compilación.
Se usa cuando no están disponibles los offsets de UMH ni los misc_fops de ashmem en C.
Write 1 (mode=1) → SELinux enforcing = 0
(low byte of kernel ptr = 0x00, 8-byte write)
Write 2 (mode=2) → task->cred = init_cred
(uid=0, all capabilities)
Root shell → ksud late-load (KernelSU LKM)
→ su -c load_policy (fix SELinux policycap)
→ dynamic manager registration
App (seccomp) → Write 1 (no perf needed)
→ mini-adb connect TCP (port from /data/local/tmp/a/adb_port, default 5555)
→ adb shell: full exploit (perf works, no seccomp)
→ root → KSU → network fix
BOOT_COMPLETED → BootCompletedReceiver
├─ su available → skip (soft reboot / already rooted)
└─ no root → GhostlockService → setsid exploit --bootstrap
Con NFDS=320, el core_sys_select del kernel asigna un buffer stack_fds de 256 bytes:
stack_fds: 0 5 10 14 | 15 20 25 29
├─in─┤─out─┤─ex──┤ ├res_in┤res_out┤res_ex┤
◄── USER CONTROLLED ──►│◄── KERNEL ZEROED ──►
El exploit escribe campos de waiter falsos (task, lock) en los bitmaps de entrada de fd_set. Para que esto funcione, los campos task y lock del waiter deben caer dentro de la zona controlable (words 0-14).
Ace 6T ✅ (waiter at word 2):
░░████████████████░░│░░░░░░░░░░░░░░░░░░
▲waiter t l │
task/lock controllable
RMX5070 ❌ (waiter at word 13):
░░░░░░░░░░░░░████│██████████████░░░░░░
▲ │ t l
waiter task/lock ZEROED
Regla de viabilidad: la word del waiter + 11 (offset de lock en rt_waiter_node) debe ser ≤ 14. La word de waiter máxima viable es 3.
La posición del waiter la determina la distribución del stack frame del compilador (perfiles de optimización PGO + LTO + BOLT), que varía según la rama del SoC. La misma versión de kernel puede tener distribuciones diferentes en distintos SoC.
Todas las escrituras al kernel pasan a través del alias de mapa lineal de la imagen:
data_addr(x) = PAGE_OFFSET + (kernel_phys_load - PHYS_OFFSET) + (x - KIMAGE_TEXT_BASE)
El bootloader elige kernel_phys_load, por lo que varía según el SoC y no está en boot.img ni en el DT. Es un campo por dispositivo en struct kernel_offsets; 0 = usar el valor predeterminado de target.h.
| SoC | kernel_phys_load |
|---|---|
| SM8845 (Ace 6T, 15T) | 0xa8000000 |
| SM8750 (OnePlus 13, OPPO Pad 4 Pro) | 0xa8000000 |
| SM8850 (OnePlus 15, Xiaomi 17) | 0xc7800000 |