
Explota CVE-2026-43499 en dispositivos Android GKI 6.12: lectura/escritura arbitraria determinista del kernel, bypass de KASLR y root completo mediante payload LD_PRELOAD.
Un exploit de kernel Linux para la línea de Android GKI 6.12 (dispositivos
Samsung y Pixel) que apunta a CVE-2026-43499: una reversión defectuosa en
remove_waiter() dentro de rt_mutex_start_proxy_lock() que usa current en
lugar de waiter::task, dejando el pi_blocked_on de un waiter apuntando a su
rt_mutex_waiter de pila (que luego se extrae). Combinado con una sobrescritura
de pila del fd_set de pselect() y un recorrido falso de rb_tree impulsado por
Sched_setattr consumidor, produce una primitiva de escritura de kernel
determinista y root completo.
El exploit se ejecuta como una biblioteca compartida LD_PRELOAD
(preload.so) e instala un demonio su root y un wallpaper como artefactos
post-root.
Estado: desarrollo activo. Los offsets de profundidad de pila por objetivo (
PSELECT_*word shifts) y el comportamiento del slide varían según el dispositivo y todavía se están verificando en el hardware. El objetivo m1q (ZF1) es el foco actual del bring-up.
Cuando FUTEX_CMP_REQUEUE_PI reencola un waiter y el recorrido de la cadena de
detección de interbloqueo del requeue devuelve -EDEADLK,
__rt_mutex_start_proxy_lock() revierte mediante remove_waiter()
(rtmutex.c:1535). La reversión desencola correctamente al waiter del árbol de
espera, pero limpia el pi_blocked_on del llamador del requeue en lugar de
waiter->task->pi_blocked_on. El pi_blocked_on del WAITER queda colgando en
su rt_mutex_waiter de pila, que se extrae cuando el futex expira.
Después del tiempo de espera, la región de la pila del kernel del waiter se
reutiliza: core_sys_select() copia los tres fd_sets en ese buffer de pila (la
ruta de pila nfds < 344 en ZF1). Una matriz de palabras fd_set manipulada
re-materializa un rt_mutex_waiter falso / task falso / rt_mutex falso (con
una raíz rb_tree cuyos punteros están controlados). Un hilo consumidor llama
entonces a sched_setattr_tid(waiter) →
rt_mutex_adjust_pi() → rb_erase_cached, lo que produce una escritura de
dirección arbitraria de un valor controlado.
Toda la primitiva requiere el ciclo de la cadena PI: el propietario
mantiene f_pi_target y también se bloquea en f_pi_chain (retenido por el
waiter), por lo que el recorrido de la cadena alcanza owner → chain → waiter →
target → owner y falla con -EDEADLK. Eliminar el ciclo hace que el requeue
devuelva éxito sin efecto en el kernel.
sched_blocked_reason (principal en m1q): el PC de retorno
guardado de un kworker bloqueado (stack_trace_save_tsk) se lee del ring
buffer y se compara con el offset de worker_thread compilado.SLIDE_LOGGERS_0_1 en los datos sysctl de
boot_id mediante un alias de mapa lineal; el valor filtrado reconstruye
stext.mm_struct y usar una
colisión de hash de futex para localizar un mm_struct en el heap,
filtrando una dirección de página del heap del kernel que se usa como base
del rociado de objetos falsos.configfs_bin_write_iter
en el slot write_iter (+0x30) de los file_operations de ashmem estáticos
en .data. Abrir ashmem yield entonces lectura/escritura arbitraria del
kernel a través de la ruta binaria de configfs.Fase opcional STAGE3=1 que bifurca un hijo puente, intercambia su
mm->pgd con una tabla de páginas falsa escalonada (3 niveles: PGD→L1→L2, hoja
de bucle RX + hoja de pila RW) y deja que el hijo ejecute un blob ensamblador
solo de registros que parchea su propio cred a través de una ventana de
escaneo físico de 2MB — R/W físico de toda la RAM sin las primitivas
configfs/pipe. Actualmente está conectada solo al objetivo m1q y no se ha
ejecutado en el dispositivo.
41 objetivos en src/targets/<codename>-<build>/, cada uno requiere como mínimo
un target.h (offsets de símbolos del kernel, KIMAGE_TEXT_BASE, base del
mapa directo, offsets de estructuras). Los objetivos Pixel (comet, tokay,
tegu, caiman, komodo, frankel, mustang, rango, stallion,
blazer) sobrescriben las fuentes compartidas; los objetivos Samsung (m1q-*)
añaden lógica específica del dispositivo.
make list-projects # lista completa
Los diseños de las estructuras del kernel difieren según el dispositivo: los offsets deben verificarse contra el binario real del kernel / kallsyms para cada objetivo.
Salida: build/<PROJECT>/bin/preload.so (biblioteca compartida cargada vía
LD_PRELOAD).
# Proyecto por defecto
CC=clang make
# Objetivo de dispositivo específico (por defecto: blazer-CP2A.260605.012)
CC=clang make PROJECT=m1q-BP4A.251205.006
# Sin CC=clang: usa NDK si se encuentra ($NDK_ROOT / $ANDROID_NDK_HOME /
# $ANDROID_NDK_ROOT); de lo contrario, clang del host + sysroot de Android
make PROJECT=m1q-BP4A.251205.006
# Mostrar configuración de compilación
make info
# Limpiar
make clean
La compilación embebe un binario PIE su_daemon (src/su_daemon.c, compilado
en build/embed/su_daemon_aarch64_pie) y assets/wallpaper.webp en
preload.so mediante src/su_blob.S / src/wallpaper_blob.S.
Empuje los artefactos de compilación a /data/local/tmp y luego ejecute el
exploit bajo LD_PRELOAD. Ejecute SIN tee en un objetivo de bring-up en vivo —
tee almacena en búfer su propio stdio y pierde la cola del registro en un
pánico del kernel (el stdout del exploit no tiene búfer, por lo que redirigir
directamente a un archivo conserva cada línea):
adb push build/m1q-BP4A.251205.006/bin/preload.so /data/local/tmp/preload.so
adb push build/embed/su_daemon_aarch64_pie /data/local/tmp/su_daemon_aarch64_pie
adb shell "chmod 755 /data/local/tmp/preload.so /data/local/tmp/su_daemon_aarch64_pie"
adb shell "LD_PRELOAD=/data/local/tmp/preload.so \
/data/local/tmp/su_daemon_aarch64_pie \
> /data/local/tmp/output.log 2>&1"
En caso de éxito, el demonio escucha en /data/local/tmp/temp_su.sock y su
se instala en /apex/com.android.virt/bin.
exploit/
Makefile
assets/ # wallpaper.webp (artefacto post-root embebido)
src/
main.c # punto de entrada run_exploit() (compartido)
preload.c # constructor LD_PRELOAD, instalación de su/wallpaper
util.c # traducción de direcciones, preparación de páginas, helpers de configfs
pipe.c # lectura/escritura física basada en pipes
root.c # parcheo de cred / recorrido de tareas
slide.c # slide de KASLR (rutas tracefs + boot_id)
fops.c # corrupción pselect + manipulación de fops
faketables.c # constructores de tablas de páginas falsas de etapa 3 (3 niveles)
stage3.c # hijo puente pgd-swap (no-op salvo que se configure)
stage3_loop.S, stage3_poll.S # blob puente + trampolín de entrada
su_daemon.c # demonio root (compilado como PIE separado)
su_blob.S, wallpaper_blob.S # binarios embebidos
offset.h # extrae target.h por objetivo vía -DTARGET_CONFIG_H
kernelsnitch/ # biblioteca de fuga de futex-hash de mm_struct
targets/<codename>-<build>/ # target.h por dispositivo + sobrescrituras opcionales
poc/poc.c # POC independiente (separado del exploit)
Los objetivos sobrescriben las fuentes compartidas mediante el mecanismo
pick_src del Makefile: si existe src/targets/<PROJECT>/<file>.c, se usa en
lugar de src/<file>.c compartido; de lo contrario, se usa la versión
compartida.
-EDEADLK (errno 35). Un ciclo faltante aparece
como success=1 sin efecto en el kernel.PSELECT_WAITER_WORD_SHIFT debe ser 0 (profundidades de pila ZF1
verificadas desde ELF). Otros objetivos todavía usan 1 por defecto: verifique
por dispositivo antes de confiar en ellos.p0_data_alias() usa DIRECT_MAP_BASE (el mapa lineal), que difiere de
P0_PAGE_OFFSET en ZF1. Los alias de datos para objetos del kernel
dereferenciados deben usar el mapa lineal.pr_error() es fatal (exit(-1)), a diferencia de las macros de
advertencia/info.SLIDE_LOGGERS_0_1, no
SLIDE_NFULNL_LOGGER; la palabra es un alias de mapa lineal no
aleatorizado, por lo que la ruta boot_id es independiente del slide (úsela
solo en slide=0 — ejecute primero la fuga de tracefs).Este es código de exploit para dispositivos Android y está destinado a investigación de seguridad, pruebas de vulnerabilidad autorizadas y trabajo defensivo en hardware que usted posee o para el que tiene permiso explícito para probar. El uso indebido en dispositivos que no le pertenecen es ilegal. El autor no es responsable de dispositivos brickeados, kernels brickeados ni garantías anuladas.
preload.c instala el demonio su embebido (montado en tmpfs en
/apex/com.android.virt/bin, además de variantes en el namespace de adbd y
local) y cambia el wallpaper.| Variable | Predeterminado | Propósito |
|---|
PSELECT_ROUTE_SHIFT | tiempo de compilación | Desplazamiento de palabras fd_set A/B para la ruta principal (m1q) |
SLIDE_SHIFT | tiempo de compilación | Desplazamiento de palabras fd_set A/B para la ruta del slide |
PSELECT_ROUTE_DELAY_USEC | 50000 | Retardo del consumidor antes de sched_setattr (debe ser > 0) |
SLIDE_CONSUME_USEC, SLIDE_ENTER_DELAY_USEC, SLIDE_CONSUMER_CORE | — | Temporización/afinidad de la ruta del slide |
SKIP_SLIDE | 0 | Usar el fallback de mapa directo (slide = 0) |
SLIDE_ONLY | 0 | Ejecutar solo el slide de KASLR y salir |
SLIDE_P0_OFFSET, SLIDE_BOOTID_OFF | — | Sobrescribir offsets de alias p0 / boot_id del slide |
KSNITCH_COLLISIONS | 4 | Número de colisiones de KernelSnitch |
STAGE3 | 0 | Habilitar la fase del hijo puente pgd-swap (m1q) |
STAGE3_DRYRUN | 0 | Preparar/verificar tablas y luego abortar antes del intercambio |
PAGE_RECLAIM_SENDS, PSELECT_SIMPLE_LAYOUT | — | Ajustes de rociado/diseño de fd_set |
TMP_PAGE_* / TMP_UNAME_* | — | Ruta experimental tmp_page (m1q) |