
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