Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-43499-S26 — Exploit del kernel Android GKI 6.12 para CVE-2026-43499, que encadena un fallo de reversión de rt_mutex con una sobrescritura de pila en pselect para obtener root en dispositivos Samsung y Pixel. | Kitploit
Herramientas/GitHubGitHub/sammyenigma/cve-2026-43499-s26
Seguridad AndroidEscalada de PrivilegiosForensia de MemoriaMecanismos de PersistenciaExplotaciónIngeniería InversaShellcodePost-ExplotaciónSeguridad Móvil

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
Desarrollo de Payloads
Explotación de Binarios
GitHubsammyenigma/cve-2026-43499-s26

CVE-2026-43499-S26

Exploit del kernel Android GKI 6.12 para CVE-2026-43499, que encadena un fallo de reversión de rt_mutex con una sobrescritura de pila en pselect para obtener root en dispositivos Samsung y Pixel.

Ver Repositorio
1hace 22 díasAún no revisado

CVE-2026-43499 — Exploit del kernel de Android GKI 6.12 pselect / configfs

Un exploit del kernel de Linux para la línea GKI 6.12 de Android (dispositivos Samsung y Pixel) dirigido a CVE-2026-43499: un rollback defectuoso de remove_waiter() en 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 la pila del kernel (que luego se saca). Combinado con una sobrescritura de la pila del fd_set de pselect() y un recorrido falso de rb_tree impulsado por sched_setattr del consumidor, produce una primitiva de escritura en el kernel determinista y root completo.

El exploit se ejecuta como una biblioteca compartida LD_PRELOAD (preload.so) e instala un demonio su root más un fondo de pantalla como artefactos posteriores al root.

Estado: desarrollo activo. El objetivo m1q (ZF1) es el foco de la puesta en marcha. El bootstrap pipei tmp_page-uname es la ruta activa actual: el recorrido está probado limpio en el dispositivo (build #33: regiones rt_mutex sembradas por hijo) y el barrido completo de 168 candidatos está en curso. La ruta configfs CFI es un callejón sin salida en m1q (Rust ashmem — sin slot fops inyectable) y sigue siendo la ruta para objetivos C-ashmem. Los offsets por objetivo varían según el dispositivo — verifíquelos contra el binario real del kernel antes de confiar en ellos.

Primitiva vulnerable (CVE-2026-43499)

Cuando FUTEX_CMP_REQUEUE_PI reencola un waiter y el recorrido de la cadena de detección de deadlock del reencolado devuelve -EDEADLK, __rt_mutex_start_proxy_lock() hace rollback mediante remove_waiter() (rtmutex.c:1535). El rollback desencola correctamente el waiter del árbol de espera pero limpia el pi_blocked_on del llamador del reencolado en lugar de waiter->task->pi_blocked_on. El pi_blocked_on del WAITER queda colgando en su rt_mutex_waiter de la pila, que se saca una vez que el futex expira.

Tras el timeout, la región de la pila del kernel del waiter se reutiliza: core_sys_select() copia los tres fd_sets en ese búfer de pila (la ruta de pila nfds < 344 en ZF1). Un array de palabras fd_set manipulado vuelve a materializar un rt_mutex_waiter falso / task falsa / rt_mutex falso (con una raíz de rb_tree cuyos punteros están controlados). Un hilo consumidor entonces llama a sched_setattr_tid(waiter) → rt_mutex_adjust_pi() → rb_erase_cached, lo que produce una escritura en 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 (mantenido por el waiter), de modo que el recorrido de la cadena llega a owner → chain → waiter → target → owner y falla con -EDEADLK. Eliminar el ciclo hace que el reencolado devuelva éxito con efecto nulo en el kernel.

Cadena de explotación

  1. Slide (KASLR) — filtrar la base del kernel. Dos rutas:
    • tracefs sched_blocked_reason (primaria en m1q, probada en dispositivo): 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. Se ejecuta antes de usar cualquier palabra de la ruta boot_id para que los objetivos de escritura de alias de datos (data_addr() = alias p0 + slide_p0_offset) sigan siendo correctos con un slide distinto de cero.
    • ruta boot_id pselect (respaldo, independiente del slide): una escritura pselect planta SLIDE_LOGGERS_0_1 en los datos del sysctl boot_id mediante un alias de mapa lineal; el valor filtrado reconstruye stext. Aparcada en m1q: su objetivo W1 (SLIDE_RANDOM_BOOT_ID_DATA_OFF, RAM física baja) tiene escriturabilidad dependiente de la página — el slide mueve el objetivo a través de los límites de página en cada arranque, por lo que ~6/7 ejecuciones fallan. Solo se ejercita explícitamente mediante SLIDE_FORCE_BOOTID (prueba de mecanismo) con tree_pc/tree_left redirigidos a la página rociada.
  2. KernelSnitch — rociar objetos del tamaño de y usar una colisión de hash de futex para localizar un en el heap, filtrando una dirección de página del heap del kernel usada como base del rociado de objetos falsos. Los waiters se desasocian (no se unen) en la limpieza para evitar el OOM de la pila del kernel que hizo fallar builds anteriores al #26.

Forma del recorrido (m1q, probada en dispositivo)

Las palabras fd_set de pselect vuelven a materializar un rt_mutex_waiter falso en la pila del kernel del waiter; el sched_setattr_tid(waiter) de un hilo consumidor lo recorre mediante rt_mutex_adjust_pi(). Mapa de palabras ZF1 (verificado por disasm): PSELECT_WAITER_WORD_SHIFT = 0, palabra 12 = task (@+0x50), palabra 13 = lock (@+0x58), palabra 14 = wake_state (@+0x60 = 3). Con un rt_mutex falso sembrado el recorrido se completa limpiamente: las rb_erase W1 *(tree_left)=tree_pc / W2 *(tree_pc&~3+8)=tree_left de [7] son las únicas escrituras en el kernel; [11] (setprio / dequeue_pi) y el wake de [9] se omiten ambos (el waiter falso local con prio 100 mantiene el nodo de pila fuera del top-waiter). Cada finalización que entra al recorrido desde el build #19 sobrevive en el dispositivo; los fallos deterministas anteriores eran un error de colocación de payload de 8 bytes (SKB_DATA_DELTA), no un fallo del cuerpo del recorrido.

Etapa 3 (puente pgd-swap, solo estático/build)

Fase opcional STAGE3=1 que bifurca un hijo puente, cambia su mm->pgd a una tabla de páginas falsa preparada (3 niveles: PGD→L1→L2, hoja de bucle RX + hoja de pila RW), y deja que el hijo ejecute un blob de ensamblador solo de registros que parchea su propio cred a través de una ventana de escaneo físico de 2MB — lectura/escritura física de toda la RAM sin las primitivas configfs/pipe. Actualmente solo está conectada al objetivo m1q y no se ha ejecutado en dispositivo.

Objetivos soportados

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 structs). Los objetivos Pixel (comet, tokay, tegu, caiman, komodo, frankel, mustang, rango, stallion, blazer) sobrescriben fuentes compartidas; los objetivos Samsung (m1q-*) añaden lógica específica del dispositivo.

root@kitploit:~
make list-projects   # full list

Los diseños de structs del kernel difieren por dispositivo — los offsets deben verificarse contra el binario real del kernel / kallsyms para cada objetivo.

Build

Salida: build/<PROJECT>/bin/preload.so (biblioteca compartida cargada vía LD_PRELOAD).

root@kitploit:~
# Default project
CC=clang make

# Specific device target (default: blazer-CP2A.260605.012)
CC=clang make PROJECT=m1q-BP4A.251205.006

# Without CC=clang: uses NDK if found ($NDK_ROOT / $ANDROID_NDK_HOME /
# $ANDROID_NDK_ROOT), otherwise host clang + Android sysroot
make PROJECT=m1q-BP4A.251205.006

# Show build configuration
make info

# Clean
make clean

El build embebe un binario PIE su_daemon (src/su_daemon.c, compilado a build/embed/su_daemon_aarch64_pie) y assets/wallpaper.webp en preload.so mediante src/su_blob.S / src/wallpaper_blob.S.

Ejecución en dispositivo

Empuje las salidas del build a /data/local/tmp, luego ejecute el exploit bajo LD_PRELOAD. Ejecute SIN tee en un objetivo de puesta en marcha en vivo — tee almacena en búfer su propio stdio y pierde la cola del log en un pánico del kernel (la salida estándar del exploit no tiene búfer, por lo que redirigir directamente a un archivo preserva cada línea):

root@kitploit:~
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.

Barrido pipei de m1q (puesta en marcha actual)

tools/run_pipei.sh empuja el binario, verifica su sha256 en el dispositivo (un preload.so obsoleto produce veredictos silenciosamente ilegibles — la escalera una vez ejecutó un build < #22 cuyo formato de log difiere), luego ejecuta el barrido completo de 168 candidatos dividido en 3x56 para que un pánico pierda como máximo un fragmento (~10 min). La salida estándar del exploit se transmite directamente a través de adb (sin redirección en el dispositivo — un tee del lado del dispositivo pierde la cola en un pánico); se mantiene una copia en el host de cada fragmento en /tmp/m1q_pipei_chunk{1..3}_stream.log:

root@kitploit:~
sh tools/run_pipei.sh

PASS = cualquier fragmento muestra tmp_page uname changed + after uname='CatOS. PIPEI_CHILD_REGIONS debe establecerse al menos al tamaño del fragmento o las regiones rt_mutex por hijo permanecen sin sembrar y el barrido falla en el candidato 0. La cobertura completa también requiere TMP_UNAME_PIPEI_SPLIT_ORDER_PAGES=1 — sin él solo se barre una página del bloque mm liberado de 8 páginas. run_pipei_slim.sh es la variante de diagnóstico de un solo candidato; run_m1q_ladder.sh es la batería de recorrido/aislamiento (oráculo B16-2, mecanismo de slide, pasos del barrido).

Configuración en tiempo de ejecución (variables de entorno)

Diseño del proyecto

root@kitploit:~
exploit/
  Makefile
  assets/                     # wallpaper.webp (embedded post-root artifact)
  src/
    main.c                    # run_exploit() entry point (shared)
    preload.c                 # LD_PRELOAD constructor, su/wallpaper install
    util.c                    # addr translation, page prep, configfs helpers
    pipe.c                    # pipe-based physical read/write
    root.c                    # cred patching / task walk
    slide.c                   # KASLR slide (tracefs + boot_id routes)
    fops.c                    # pselect corruption + fops manipulation
    faketables.c              # stage-3 fake page-table builders (3-level)
    stage3.c                  # pgd-swap bridge child (no-op unless configured)
    stage3_loop.S, stage3_poll.S  # bridge blob + enter trampoline
    su_daemon.c               # root daemon (compiled as separate PIE)
    su_blob.S, wallpaper_blob.S   # embedded binaries
    offset.h                  # pulls per-target target.h via -DTARGET_CONFIG_H
    kernelsnitch/             # mm_struct futex-hash leak library
    targets/<codename>-<build>/    # per-device target.h + optional overrides
poc/poc.c                     # standalone POC (separate from the exploit)

Los objetivos sobrescriben fuentes compartidas mediante el mecanismo pick_src del Makefile: si existe src/targets/<PROJECT>/<file>.c se usa en lugar del src/<file>.c compartido; de lo contrario se usa la versión compartida.

Notas y trampas

  • Nunca elimine el ciclo de la cadena PI. La primitiva de escritura depende de que el reencolado falle con -EDEADLK (errno 35). Un ciclo ausente se manifiesta como success=1 con efecto nulo en el kernel.
  • El waiter debe mantener el bloqueo de la cadena PI durante pselect. Liberarlo primero deja que el propietario despierte y compita con el recorrido falso de rb_tree → pánico.
  • m1q: PSELECT_WAITER_WORD_SHIFT debe ser 0 (profundidades de pila ZF1 verificadas desde el ELF). Otros objetivos aún 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 desreferenciados deben usar el mapa lineal.
  • pr_error() es fatal (exit(-1)), a diferencia de las macros de advertencia/información.
  • La reconstrucción del slide usa 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 (úsala solo con slide=0 — ejecuta primero la fuga de tracefs).

Descargo de responsabilidad

Este es código de exploit para dispositivos Android y está destinado a la investigación de seguridad, pruebas de vulnerabilidad autorizadas y trabajo de defensa en hardware que usted posee o tiene permiso explícito de probar. El uso indebido en dispositivos que no posee es ilegal. El autor no es responsable de dispositivos inutilizados, kernels inutilizados o garantías anuladas.

Descargar herramienta
mm_struct
mm_struct
  • Ruta principal — bootstrap pipei tmp_page (m1q, foco actual) — la ruta configfs CFI original es un callejón sin salida en m1q: el ashmem de Rust no tiene un slot estático write_iter inyectable y el objeto de heap ASHMEM_SET_NAME es un KVec cuyo diseño no se alinea con el private_data de configfs_bin_write_iter. m1q pivota a una escritura kmalloc en su lugar: reclamar el bloque de orden 3 de mm filtrado como objetos pipe_inode_info y usar la escritura pselect W1 (*(tree_left) = tree_pc) para sobrescribir el slot tmp_page de un candidato (+0x90) con la página del namespace UTS (init_uts_ns). Una escritura de abanico planta un nombre marcador en el offset sysname y uname() informa "CatOS" — una escritura en el kernel verificable sin dependencia de objetos estáticos. Los objetivos C-ashmem mantienen la ruta configfs.
  • Pipe physrw — forjar páginas de búfer de pipe en la página del kernel rociada para lectura/escritura física: parcheo de cred, desactivación de SELinux y manipulación directa de la memoria del kernel.
  • Root — parchear el cred del hijo root (uid/gid/caps/SID de SELinux), luego preload.c instala el demonio su embebido (montado en tmpfs en /apex/com.android.virt/bin, más variantes de namespace adbd y locales) y cambia el fondo de pantalla.
  • VariablePor defectoPropósito
    PSELECT_ROUTE_SHIFTtiempo de compilaciónDesplazamiento de palabra fd_set A/B para la ruta principal (m1q)
    SLIDE_SHIFTtiempo de compilaciónDesplazamiento de palabra fd_set A/B para la ruta de slide
    PSELECT_ROUTE_DELAY_USEC50000Retardo del consumidor antes de sched_setattr (debe ser > 0)
    SLIDE_CONSUME_USEC, SLIDE_ENTER_DELAY_USEC, SLIDE_CONSUMER_CORE—Sincronización/fijación de la ruta de slide
    SKIP_SLIDE0Usar respaldo de mapa directo (slide = 0)
    SLIDE_ONLY0Ejecutar solo el slide de KASLR y salir
    SLIDE_P0_OFFSET, SLIDE_BOOTID_OFF—Sobrescribir offsets de alias p0 / boot_id del slide
    SLIDE_FORCE_BOOTID0Forzar la ruta boot_id pselect tras el slide de tracefs (prueba de mecanismo)
    KSNITCH_COLLISIONS4Número de colisiones de KernelSnitch
    STAGE30Habilitar la fase del hijo puente pgd-swap (m1q)
    STAGE3_DRYRUN0Preparar/verificar tablas y abortar antes del swap
    PAGE_RECLAIM_SENDS, PSELECT_SIMPLE_LAYOUT—Ajustes de rociado/diseño de fd_set
    TMP_PAGE_UNAME0Ejecutar el bootstrap pipei tmp_page-uname de m1q
    TMP_UNAME_PIPEI_SWEEP, TMP_UNAME_PIPEI_ALIGN_ORDER, TMP_UNAME_PIPEI_SPLIT_ORDER_PAGES—Modo de barrido; split_order barre las 8 páginas del bloque mm liberado
    TMP_UNAME_PIPEI_SLOT, TMP_UNAME_PIPEI_SLOT_CANDIDATES—Rango del barrido (inicio/cantidad del fragmento)
    PIPEI_CHILD_REGIONS0Regiones rt_mutex sembradas por hijo; DEBE ser >= fragmento del barrido o las regiones sin sembrar fallan en el candidato 0
    PIPEI_RECLAIM_COUNT, PIPEI_DRAIN_COUNT—Pares de pipe de reclamación/drenaje; aumentar en un fallo de reclamación
    PIPEI_SPONGE_MB3072Esponja de inundación de orden 0 mantenida durante la reclamación (build #29)
    PSELECT_W0_PRIO_OVERRIDE130Prio del top-waiter; inerte con regiones sembradas (guarda heredada de árbol obsoleto, solo A/B)
    PSELECT_SKB_DELTA_OVERRIDE0Offset intra-página del payload A/B (solo si un dispositivo difiere de -0xe78)
    TMP_UNAME_NAME, TMP_UNAME_HOLD_SEC"CatOS", 5Nombre marcador / tiempo de retención tras el éxito
  • SKB_DATA_DELTA es -0xe78 (medido en dispositivo, build #19) — la cabeza real de fragmento skb es 8 bytes menos que el -0xe80 derivado del disasm; ese error de 8 bytes causó todos los fallos deterministas de pselect de los builds #6-18 (entrada al recorrido → desreferencia basura). Valor compartido de GKI 6.12; las copias de util.c por objetivo llevan un ajuste A/B de entorno SKB_DATA_DELTA. NO apile PSELECT_SKB_DELTA_OVERRIDE sobre la constante fijada.
  • El cuerpo del recorrido está limpio; la colocación era el error. Con el payload sembrado en los offsets modelados, el recorrido de la cadena se completa sin fallos en el dispositivo. Un fallo con entrada al recorrido significa que la memoria en las direcciones de palabra fd NO es el payload (carrera de reclamación/colocación), no un error del modelo.
  • m1q: la ruta configfs CFI es un callejón sin salida (Rust ashmem) — sin slot fops estático inyectable y el envoltorio KVec de ASHMEM_SET_NAME no se alinea con configfs_bin_write_iter. Use el bootstrap pipei tmp_page. Las guardas PSELECT_WRITE_RIGHT_SPRAYED / PSELECT_PI_TOP_TASK_SELF en el target.h de m1q redirigen las escrituras [11] del recorrido fuera de los fops estáticos RO.
  • Regiones rt_mutex por hijo (build #33): cada hijo del barrido redirige la palabra fd w13 a su propia región sembrada (payload+0x6000 + idx*0x40) dentro de la página reclamada. Los barridos con fake_lock compartido fallaban en el candidato ~9-12 (nodos obsoletos de pila del kernel reciclados), y las regiones vacías del build #32 fallaban en el candidato 0 (wake de [9] owner==NULL sobre la fake_task llena de 0x41). PIPEI_CHILD_REGIONS debe establecerse o las regiones permanecen sin sembrar.
  • El slide pselect boot_id de m1q está aparcado — su objetivo W1 es RAM baja cuya escriturabilidad de página varía por arranque; tracefs es el primario y mide el slide en cada ejecución.