
Target-specific Android CVE-2026-98260 research port for Xiaomi 17 (pudding)
An Android AArch64 exploit chain targeting a precise kernel build of the Xiaomi 17 (codename
pudding): starting from an ordinary ADB shell, it sequentially sets SELinux to Permissive, zeroes the UID/GID of the current process, fills in full Linux capabilities, and provides a root service within the current boot cycle.
This project references and ports from CVE-2026-98260-popsicle-android (Xiaomi 17 Pro Max / popsicle), and is not independent research implemented from scratch.
A non-main-thread execve() first executes de_thread() to swap thread identities. If exec fails after this point (for example, exec_mmap() is interrupted by SIGKILL and returns -EINTR), the task exits before cleaning up POSIX timers: the k_itimer is freed, but its timerqueue node remains in the tree of a live task. When the parent process later reaps that task, cleanup_timerqueue() operates on this already-freed node again, forming a use-after-free.
The exploit turns this UAF into a constrained eight-byte zero write, then proceeds in three steps: SELinux → UID/GID → full capabilities. For a complete beginner-oriented explanation, see docs/VULNERABILITY.md.
| Item | Measured Value |
|---|---|
| Device | Xiaomi 17, model 25113PN0EC, codename pudding |
| System | Xiaomi HyperOS 4 OS4.0.5.0.XPCCNXM, Android 17 |
| Kernel | 6.12.69-android16-6-g43b5ab7e247d-abogki561813157-4k |
| Page size | 4 KiB |
| Starting point | Authorized ordinary ADB shell, UID 2000, SELinux Enforcing, bootloader locked |
| Successful result | UID/GID 0, SELinux Permissive, CapPrm/CapEff/CapBnd=000001ffffffffff |
| Lifecycle | The root service is available to new ADB shells for the remainder of this boot; it must be run again after a reboot |
Independent verification after success (newly opened ADB shell):
uid=0(root) gid=0(root)
CapInh: 000001ffffffffff
CapPrm: 000001ffffffffff
CapEff: 000001ffffffffff
CapBnd: 000001ffffffffff
getenforce: Permissive
这不是持久 root。 没有写入 boot/init_boot 或任何分区,bootloader 保持锁定;重启后 SELinux 回到 Enforcing,root 服务消失。
No success rate is promised. In actual testing, a complete A→B→C sequence was achieved once across 9 boots. The pass conditions for the three stages are independent of one another:
| Stage | On-device program | Pass condition |
|---|---|---|
| 1. SELinux | exploit-android-perf | getenforce is Permissive, and the remaining fields of the status word remain as expected |
| 2. Identity | exploit-android-perf-chain | The UID/GID of the same process becomes 0, followed by exec of fullcap |
| 3. Full privileges | exploit-android-key-ptr + pc98260_rootd.c | Full capabilities, and the root service can be accessed by a new shell |
If any step fails to hit, [PARKED] is output and the dangerous state is retained; a reboot is required, and forced retries within the same boot are not allowed.
--aio 16384 is the tuned value for this machine (the successful run hit at the 3903rd perf metadata page).
| Location | popsicle | pudding |
|---|---|---|
LINK_SELINUX_STATE | 0xffffffc08289aba0 | 0xffffffc0827f9ba0 |
LINK_GETPID | 0xffffffc080074eac | 0xffffffc080102e10 |
| timer address leak | alloc_posix_timer returns the probe | This symbol is inlined; instead use the posix_cpu_timer_create entry $arg1 |
PERF_MAX | 8192 | 16384 (increases the chance of page hits) |
For the rationale and evidence, see docs/ADAPTATION.md.
An Android NDK is required. The bin/ in the repository is built with 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 compares the SHA-256 of each new artifact against bin/ one by one; only when using the same toolchain as bin/ will the output be byte-for-byte identical. Switching NDK versions will still run, but the hashes will differ.
Artifacts:
| Artifact | Input source |
|---|---|
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 |
The build does not connect to a phone and does not write to the device.
Connect a device with USB debugging enabled and authorized, and add platform-tools to PATH:
.\开始98260临时ROOT.bat
Or explicitly specify the device:
powershell -NoProfile -ExecutionPolicy Bypass -File .\run_98260.ps1 -Serial <ADB序列号> -Reboot
The host script verifies the device model, system incremental version, full kernel string, boot ID, and the hashes of the four binaries; it also sets /sys/kernel/tracing/tracing_on to 0 before starting each stage (miuibpf in some HyperOS builds keeps it at 1, which causes the exploit chain to refuse to run). When [PARKED] appears, the script stops and prompts for a reboot.
After success, root can be used as follows:
adb shell -t /data/local/tmp/pc98260-su
adb shell /data/local/tmp/pc98260-su -c "id"
The exploit chain requires precisely reclaiming "the page where the target timer resides." If the same target page cannot be observed, or if verification after the pointer write is inconsistent, the program retains the relevant zombie/mapping and stops, avoiding blind reclamation while dangling nodes are still referenced by the kernel. In this case, a fresh phone boot should serve as the boundary for the next attempt.
This repository does not contain: device backups, boot/init_boot images, partition images, payload.bin, kernels/ELFs extracted from firmware, raw runtime logs, ADB serial numbers, or boot IDs. logs/ and build/ are ignored by Git. bin/ contains only the four temporary privilege-escalation chain binaries.
/data/local/tmp/pc98260-root.sock with full capabilities; the entire device is fully exposed for the duration of this boot.