
Puerto de investigación de CVE-2026-98260 de Android específico para el objetivo Xiaomi 17 (pudding)
Cadena de explotación de Android AArch64 para una compilación de kernel exacta de Xiaomi 17 (nombre en clave
pudding): partiendo de un shell ADB normal, pone SELinux en Permissive, lleva a cero el UID/GID del proceso actual, completa todas las capabilities de Linux y ofrece un servicio root durante el ciclo de arranque actual.
Este proyecto se basa y porta desde CVE-2026-98260-popsicle-android (Xiaomi 17 Pro Max / popsicle), no es una investigación independiente implementada desde cero.
Un execve() en un hilo no principal ejecuta primero de_thread() para intercambiar la identidad del hilo. Si el exec falla después de esto (por ejemplo, exec_mmap() se interrumpe por SIGKILL y devuelve -EINTR), la tarea termina antes de limpiar los temporizadores POSIX: se libera el k_itimer, pero su nodo timerqueue permanece en el árbol de la tarea viva. Cuando el proceso padre recolecta posteriormente esa tarea, cleanup_timerqueue() vuelve a operar sobre este nodo ya liberado, formando un use-after-free.
El exploit convierte este UAF en una escritura de cero de ocho bytes restringida y luego avanza en tres pasos: SELinux → UID/GID → capabilities completas. Para una explicación completa orientada a principiantes, véase docs/VULNERABILITY.md.
| Elemento | Valor medido |
|---|---|
| Dispositivo | Xiaomi 17, modelo 25113PN0EC, nombre en clave pudding |
| Sistema | Xiaomi HyperOS 4 OS4.0.5.0.XPCCNXM, Android 17 |
| Kernel | 6.12.69-android16-6-g43b5ab7e247d-abogki561813157-4k |
| Tamaño de página | 4 KiB |
| Punto de partida | Shell ADB normal autorizado, UID 2000, SELinux Enforcing, bootloader bloqueado |
| Resultado exitoso | UID/GID 0, SELinux Permissive, CapPrm/CapEff/CapBnd=000001ffffffffff |
| Ciclo de vida | El servicio root está disponible para nuevos shells ADB posteriores en este arranque; tras reiniciar hay que volver a ejecutarlo |
Verificación independiente tras el éxito (shell ADB recién abierto):
uid=0(root) gid=0(root)
CapInh: 000001ffffffffff
CapPrm: 000001ffffffffff
CapEff: 000001ffffffffff
CapBnd: 000001ffffffffff
getenforce: Permissive
Esto no es un root persistente. No se escribió en boot/init_boot ni en ninguna partición, el bootloader permanece bloqueado; tras reiniciar, SELinux vuelve a Enforcing y el servicio root desaparece.
No se garantiza la tasa de éxito. En pruebas reales, se logró completar un ciclo completo A→B→C en 9 arranques. Las condiciones de aprobación de las tres fases son independientes entre sí:
| Fase | Programa en el dispositivo | Condición de aprobación |
|---|---|---|
| 1. SELinux | exploit-android-perf | getenforce es Permissive, y los demás campos de la palabra de estado se mantienen según lo esperado |
| 2. Identidad | exploit-android-perf-chain | El UID/GID del mismo proceso cambia a 0, y luego se ejecuta exec fullcap |
| 3. Permisos completos | exploit-android-key-ptr + pc98260_rootd.c | Capabilities completas, el servicio root puede ser accedido por un nuevo shell |
Si cualquiera de los pasos no se cumple, se mostrará [PARKED] y se conservará el estado peligroso; es obligatorio reiniciar, no se puede reintentar forzosamente dentro de este mismo arranque.
--aio 16384 es el valor de ajuste de esta máquina (en la ocasión exitosa se acertó en la página de metadatos de perf número 3903).
| Ubicación | popsicle | pudding |
|---|---|---|
LINK_SELINUX_STATE | 0xffffffc08289aba0 | 0xffffffc0827f9ba0 |
LINK_GETPID | 0xffffffc080074eac | 0xffffffc080102e10 |
| Fuga de dirección del timer | alloc_posix_timer devuelve la sonda | Ese símbolo está inlineado, se usa la entrada $arg1 de posix_cpu_timer_create |
PERF_MAX | 8192 | 16384 (aumenta la probabilidad de acierto de página) |
La base y las evidencias se encuentran en docs/ADAPTATION.md.
Se requiere un Android NDK. El directorio bin/ del repositorio fue compilado con NDK 26.3.11579264 / API 34:
powershell -NoProfile -ExecutionPolicy Bypass -File .\build.ps1 -NdkHome "$env:LOCALAPPDATA\Android\Sdk\ndk\26.3.11579264" -ApiLevel 34
-VerifyReference compara el SHA-256 de los nuevos artefactos con bin/ uno por uno; solo será idéntico byte a byte si se usa la misma cadena de herramientas que bin/. Cambiar la versión de NDK seguirá funcionando, pero el hash será diferente.
Artefactos:
| Artefacto | Código fuente de entrada |
|---|---|
exploit-android-perf | src/exploit-android-perf.c |
exploit-android-perf-chain | src/exploit-android-perf-chain.c |
exploit-android-key-ptr | src/exploit-android-key-ptr.c + src/pc98260_rootd.c |
pc98260-su | src/pc98260_su.c |
La compilación no se conecta al teléfono ni escribe en el dispositivo.
Conecte un dispositivo con la depuración USB habilitada y autorizada, y añada platform-tools al PATH:
.\开始98260临时ROOT.bat
O especificar explícitamente el dispositivo:
powershell -NoProfile -ExecutionPolicy Bypass -File .\run_98260.ps1 -Serial <ADB序列号> -Reboot
El script del host verifica el modelo del dispositivo, la versión incremental del sistema, la cadena completa del kernel, el boot ID y los hashes de los cuatro binarios; además, antes de iniciar cada fase, establece /sys/kernel/tracing/tracing_on en 0 (en algunas compilaciones de HyperOS, miuibpf lo mantiene en 1, por lo que la cadena de exploits se negará a ejecutarse). Cuando aparece [PARKED], el script se detiene y solicita reiniciar.
Tras el éxito, se puede usar root de la siguiente manera:
adb shell -t /data/local/tmp/pc98260-su
adb shell /data/local/tmp/pc98260-su -c "id"
La cadena de explotación requiere recuperar con precisión "la página donde se encuentra el temporizador objetivo". Si no se observa la misma página objetivo, o si la verificación tras la escritura del puntero resulta inconsistente, el programa conserva el zombie/mapeo correspondiente y se detiene, evitando una recuperación a ciegas mientras el nodo colgante aún está referenciado por el kernel. En ese caso, debe tomarse un nuevo arranque del teléfono como límite para el siguiente intento.
Este repositorio no incluye: copias de seguridad del dispositivo, imágenes boot/init_boot, imágenes de particiones, payload.bin, kernels/ELF extraídos del firmware, registros de ejecución sin procesar, números de serie de ADB ni boot IDs. logs/ y build/ están ignorados por Git. En bin/ solo hay cuatro binarios temporales de la cadena de escalada de privilegios.