
CVE-2026-43499 adaptador de exploit para MT6985 MediaTek Dimensity 9300 (vivo PD2241)
Conclusión: Gasté 68M tokens, no obtuve root.
Causa: El ataque de temporización KernelSnitch no es fiable en MTK Dimensity 9200,CONFIG_PANIC_ON_OOPS=yno deja margen para prueba y error.
Este artículo documenta el proceso completo de tropiezos, como referencia para que otros eviten estos errores.
| Proyecto | Valor |
|---|
| Dispositivo | vivo PD2241 (Dimensity 9200 / MT6985), Android 15 |
| Firmware | PD2241_A_15.2.10.2.W10.V000L1 |
| Kernel | 5.15.178-android13-8-gfb31f5bdd612-dirty |
| Bootloader | Bloqueado (ro.boot.flash.locked=1) |
| SELinux | Enforcing |
| panic_on_oops | Activado → cualquier OOPS del kernel = reinicio inmediato |
| Exploit | CyberMeowfia — CVE-2026-43499 (IonStack) |
| Árbol de código fuente | android_15.0_kernel_MT6985 (5.15.178) — no coincide con la versión del dispositivo (el código fuente es android15 GKI, el dispositivo ejecuta android13 GKI) |
Extraído de arch/arm64/include/asm/memory.h + include/linux/fs.h + android/abi_gki_aarch64.xml:
KIMAGE_TEXT_BASE = 0xffffffc008000000, VA_BITS=39, DIRECT_MAP=256GBlayout-offset-in-bits)iopoll (android13 GKI), IOCTL=0x48, open=0x68atomic_t usage = 4 bytes, uid=0x04slab_cache=0x18Hallazgo clave: el código fuente es android15 GKI, el dispositivo es android13 GKI — los offsets de task_struct difieren en 0x40~0x88 bytes, no se pueden copiar directamente del código fuente.
OTA zip (8.3GB)
→ payload.bin (8.2GB)
→ payload_dumper → boot.img (96MB, cabecera v4)
→ descompresión LZ4 → Image (50MB ARM64)
→ kallsyms-finder → 187810 símbolos
Símbolos extraídos de dos versiones de firmware (15.2.7.6 / 15.2.10.2) — el mismo símbolo difiere entre versiones en 10KB~200KB, hay que usar la versión correcta.
# En rt_mutex_adjust_pi:
LDR x21, [x19, #0x8b0] → pi_blocked_on = 0x8b0 (valor android15, no frankel)
Esto confirma que la disposición de task_struct es de la rama android15, no la android13 de frankel.
NDK r29, make PROJECT=android_15.0_kernel_MT6985 → preload.so (150KB).
[+] preload starting pid=25414
[+] p0 profile ... todos los símbolos cargados correctamente
[-] KernelSnitch mm_struct leak failed ← a veces no aparece (éxito ocasional)
[+] slide child context route=pselect ← se inicia el proceso hijo para filtrar KASLR slide
[panic del kernel] ← rt_mutex_adjust_prio_chain+0x1b0
Instrucción del fallo (capstone):
ldar w8, [x27] ; x27 = waiter->lock (cargado desde [x28, #0x38])
; el valor de x27 es basura → sin mapeo en tabla de páginas → translation fault
; → die() → panic → reinicio
KernelSnitch es la puerta de entrada de todo el exploit — filtra la dirección de mm_struct mediante la diferencia de temporización en los buckets hash de futex:
MT6985 tiene CONFIG_KASAN_HW_TAGS=y → el kernel etiqueta las asignaciones de slab con tags MTE
el puntero de mm_struct lleva tag KASAN → futex_hash se calcula sobre el puntero etiquetado
pero el escaneo por bruteforce recorre el direct map (direcciones sin tag) → el hash calculado no coincide
incluso añadiendo el recorrido de tags MTE (0-14, 15 en total), en un sistema con VA_BITS=39
los bits de tag (bit56-59) se solapan con los bits de extensión de signo → algunas combinaciones de tag
producen direcciones inválidas → falsos negativos
Los dispositivos Pixel no tienen KASAN_HW_TAGS, este mecanismo funciona allí. En MTK no.
CONFIG_PANIC_ON_OOPS es el asesinoPixel: OOPS del kernel → dump_stack → continúa → el exploit puede reintentar
MT6985: OOPS del kernel → die() → panic() → reinicio inmediato → sin margen de prueba y error
cualquier pequeña desviación en la cadena π rompe todo; en Pixel una desviación solo significa
"esta vez no funcionó, prueba con otro conjunto de direcciones".
Y el bootloader está bloqueado (flash.locked=1) → no se puede flashear un kernel personalizado para eliminar esta opción.
Árbol de código fuente: 5.15.178 android15 GKI
Dispositivo: 5.15.178-android13 (vendor vivo)
Aunque el número de versión mayor es 5.15.178 en ambos, la rama GKI es diferente (android13 vs android15), por lo que la disposición de estructuras clave como task_struct/cred no coincide. Se alternó repetidamente entre los dos conjuntos de offsets (frankel/android15), y solo se pudo confirmar mediante desensamblado.
| Cambio | Propósito | Resultado |
|---|---|---|
THRESHOLD_MULT 10→5→3 | Reducir el umbral de detección de colisiones | <5 demasiados falsos positivos |
APPENDED_FUTEXES 4096→8192 | Aumentar la diferencia en la cadena hash | Sin efecto |
REPEAT_MEASUREMENT/AVERAGE | Aumentar la precisión del muestreo | Sin efecto |
MTE=1 | Hacer que el bruteforce recorra las etiquetas | Más lento, incluso reduce los fallos |
MM_STRUCT_SZ 0x500→0x400 | Corregir el paso de mm_struct | Necesario, el ABI real es 992 bytes |
IDENTITY_END 64GB→256GB | Ampliar el rango de escaneo | Demasiado lento (recorrido MTE), sigue sin coincidir |
| Offsets TASK: android15↔frankel | Fijar el offset correcto | El desensamblado confirma android15 |
| Offsets FOPS: android15↔frankel | android13 no tiene iopoll | Usar frankel |
En exploit/targets/android_15.0_kernel_MT6985/target.h:
| Categoría | Fiabilidad | Método de verificación |
|---|---|---|
| Disposición de memoria | Correcta | Cálculo con memory.h + verificación con kallsyms _text |
| Offsets de símbolos (22) | Correctos | Extraídos de boot.img 15.2.10.2 |
| Offsets de task_struct | Correctos | ABI XML + desensamblado con capstone (pi_blocked_on=0x8b0) |
| Offsets de FOPS | Erróneos (corregidos el 2026-07-31) | El valor original se copió de frankel (android13 sin iopoll); el ABI del árbol de código fuente realmente tiene iopoll@0x30, ioctl=0x50, open=0x70 — ver VERIFICATION.md |
| Offsets de CRED | Correctos (verificados) | ABI XML: uid=0x04, securebits=0x24, caps=0x28, security=0x78 |
El ensamblado compila, se ejecuta, pero se pierde en el último kilómetro.
CONFIG_PANIC_ON_OOPS — o flashear un kernel personalizado (requiere desbloquear el bootloader), o encontrar un dispositivo MT6985 que tenga esta opción desactivada por defecto/proc/self/pagemap — restringido en este dispositivo (devuelve todo ceros)/proc/mtk_*) — existen pero requieren más análisissymbols/kallsyms_PD2241_15.2.10.2.txt — tabla de símbolos completa de 15.2.10.2, utilizable directamente por otrosdevice_config.txt — configuración real del kernel del dispositivo, se puede ver qué cambió el vendorexploit/targets/android_15.0_kernel_MT6985/target.h — offsets de estructuras verificadosscripts/server_compile.py — compilación automatizada, recompilar tras cambiar parámetros es rápidotar -xf puede fallar con zips grandes, usar Python zipfile o descomprimir primero manualmenteupdate_metadata_pb2.py generado requiere protobuf 5.x, hay que eliminar manualmente la línea de importación de runtime_version./preload.so directamente causa segfault: hay que usar /system/bin/linker64 /data/local/tmp/preload.solayout-offset-in-bits lo calcula el compilador, es 100 veces más preciso que contar 5000 bytes a manoCONFIG_ANDROID_VENDOR_OEM_DATA=y, CONFIG_SCHED_INFO=y, CONFIG_RSC_* → se desvía del GKI estándarboot.img → kernel.bin → descompresión LZ4 → Image → kallsyms-finder → tabla de símbolos07/28 Descarga del repositorio CyberMeowfia + código fuente MT6985
07/29 Análisis del código fuente (memory.h, fs.h, ABI XML, varias structs)
Desempaquetado del firmware (payload.bin → boot.img → Image)
Extracción de símbolos (kallsyms-finder → 187810 símbolos)
Múltiples rondas de compilación + múltiples fallos + verificación por desensamblado
5 reintentos automáticos → todos fallidos
Redacción de este documento
-------------------------------------------
Total: ~68M tokens, 0 shells root
2026-07-29, viví para contarlo
Tras obtener android_15.0_kernel_MT6985.tar.gz (árbol de código fuente 5.15.178 + ABI XML) se realizó
una validación cruzada completa, ver VERIFICATION.md. Resumen:
target.h,
y se utiliza el leak_kernel_base() integrado en el exploit como verificación automática en el dispositivo real.KSNITCH_MTE_ENABLED=1 surta
efecto real (el util.c original tenía mte=0 hardcodeado);rt_mutex_adjust_prio_chain+0x1b0 se encuentra en la fase de la cadena pselect/pi, antes de la
autoverificación de FOPS; tras las correcciones anteriores merece la pena volver a probar en el dispositivo.Dispositivo (PD2241, compiler251203103903) — 8+ rondas de pruebas en hardware real:
MM_STRUCT_SZ=0x400 (el 0x500 original causaba una cuadrícula de escaneo
desalineada y solo producía falsos positivos) + recorrido de tags MTE 0..15 (el tag del puntero mm del dispositivo cambia) + direcciones
falsas sin tag. Ahora encuentra de forma estable el mm_struct real.rt_mutex_adjust_prio_chain+0x1b0: carrera de temporización en la cadena pselect/pi
(el waiter falso no cae en el offset correcto de la pila del kernel). El planificador RSC de vivo modificó la ruta futex/pi,
lo que probablemente rompe esta carrera de raíz. La alineación de la pila slide se ha parametrizado como variable de entorno
SLIDE_SHIFT, el escaneo no se ha completado.Decisión final: optar por desbloquear el bootloader de pago (ya no se depende de esta ruta).