Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-98260 — Android kernel LPE research for CVE-2026-98260 (posix-cpu-timers UAF): trigger PoC, fake-cred and real-cred uid-0 shell chains, plus a Shizuku/KernelSU bootstrap app. | Kitploit
Tools/GitHubGitHub/woshigenm/cve-2026-98260
Android SecurityPrivilege EscalationMemory ForensicsExploitationPost-ExploitationMobile SecurityPayload DevelopmentBinary Exploitation
GitHubwoshigenm/cve-2026-98260

CVE-2026-98260

Android kernel LPE research for CVE-2026-98260 (posix-cpu-timers UAF): trigger PoC, fake-cred and real-cred uid-0 shell chains, plus a Shizuku/KernelSU bootstrap app.

4414 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
View Repository

CVE-2026-98260 — posix-cpu-timers UAF after a failed exec

English | 简体中文

Android local privilege escalation research for a use-after-free in the posix-cpu-timers exit path. The repository ships a trigger-only PoC, a fake-cred verification exploit, a real-cred uid-0 shell stage, a Shizuku-based KernelSU app, and a vulnerability pre-check.

Branch magicos-11.0.0.109-6.12.58: this branch is dual-profile. The APK and native binaries select a profile from the exact uname -r at runtime: MagicOS 10.0.0 / 6.12.38 is the verified profile, while MagicOS 11.0.0.109 / 6.12.58 is statically adapted and its device-side runtime validation is still pending. The APK bundles both 226-symbol ko-syms tables and selects the matching one.

Status at a glance

ItemValue
CVECVE-2026-98260
ClassLPE (use-after-free, timerqueue rb-tree erase)
TargetHonor MagicPad3 Pro 13.3 YLP-W00, MagicOS 10.0.0 / 11.0.0.109 (Android 16), branch magicos-11.0.0.109-6.12.58
Kernel6.12.38-android16-5-gfde7767f6ef6-abogki481467632-4k (verified)
6.12.58-android16-6-g3a7ac1674240-abogki541502914-4k (adapted)
Profile statusdual-profile; 6.12.38 verified, 6.12.58 runtime validation pending
SELinuxEnforcing
Patchesabsent on the tested build
MitigationsKASLR on, SMEP/SMAP/PAN on, KASAN compiled but disabled
Bootloaderlocked and not unlockable; no partition writes
ComponentStatusResult
src/poc.cverified on 6.12.38destructive trigger; reap panics in cleanup_timerqueue
src/exp.cverified on 6.12.38fake cred; getuid()==0 && geteuid()==0; cannot exec
src/uid0.cverified on 6.12.38real cred; uid-0 shell in u:r:shell:s0
app/verified on 6.12.38Shizuku + KernelSU bootstrap; uid 0 in u:r:ksu:s0
src/vuln_precheck.haddedprofile/environment gate; --check / --force
6.12.58 profileadaptedsites, selinux_state and its 226-symbol ko-syms table re-derived; device run pending
Dual-profile APKtested on 6.12.38profile and ko-syms selection by uname -r; 6.12.38 live-KSU path passed

Current release: v1.4.3-app. The v1.4.2-app pre-release was withdrawn on 2026-10-30; its tag is kept for history only. See docs/release-notes.md for the release record and known gaps.

Repository layout

src/poc.c              trigger only; the reap panics on the tested kernel
src/exp.c              fake-cred chain; verifies uid 0 via getuid/geteuid
src/uid0.c             real-cred chain; zeroes cred->uid/gid and execs sh
src/kernel_profile.h   per-release call sites, symbols and structure offsets
src/trace_ids.h        runtime kmem trace event IDs; unprivileged probe
src/vuln_precheck.h    shared profile/environment gate
app/                   Android root app (Shizuku + KernelSU bootstrap)
docs/analysis.md       root cause, exploit chain and device notes
scripts/dump_device.sh  root device dump: all .ko/modinfo, kernel/config/policy, perf/tracefs, vendor mount, harness, active boot.img
Makefile               single build entry (NDK cross-compile, app APK)
README.zh-CN.md        Chinese version of this document

Root cause

A non-leader thread that created a CLOCK_THREAD_CPUTIME_ID posix timer and then fails execve() after de_thread() keeps the timer node queued. The SIGKILL that interrupts the exec makes the thread exit through exit_itimers(), which cannot disarm a timer whose task is already exiting. The k_itimer is freed while its timerqueue_node is still linked in the task's posix_cputimers queue. When the parent reaps the zombie, release_task() -> posix_cpu_timers_exit() -> cleanup_timerqueue() calls timerqueue_del()/rb_erase() on the freed node.

The erase is the write primitive: rb_erase() stores a controlled __rb_parent_color value into a controlled rb_left target. The exploit choreographs slab/page reclaim so the freed node sits in a page filled with the attacker's payload, then chooses the target and value.

See docs/analysis.md for the observed panic trace, the YLP-W00 offsets, the tracefs-based leaks, the SLUB/RCU choreography, the two-stage fake-cred chain and the one-write real-cred chain.

Build

Requirements: Android NDK r29 at /opt/android-ndk-r29 (override with NDK=). Targets are aarch64, API 24 by default. The app additionally needs the Android SDK (build-tools 37.0.0, platform 37), a JDK, and the Shizuku jars under ~/.local/share/android-cve-env/cve98260-app (override with APP_DEPS=). Build artifacts stay in build/ and are git-ignored. The Shizuku jars are user-supplied; scripts/fetch-deps.sh verifies them against deps.lock (SHA-256) and never downloads anything.

TargetOutput / action
make / make pocbuild/poc
make expbuild/exp
make uid0build/uid0 (static; the shell stage re-execs itself)
make build-allbuild poc + exp + uid0 + apk
make hostbuild/poc.static for host-side qemu validation
make emurun build/poc.static under qemu (with --force)
make apkapp/CVE98260Root.apk
make cleanremove build/
make helplist targets

Vulnerability pre-check

poc, exp and uid0 call a profile gate before touching the kernel. It accepts either exact profile release (6.12.38 verified, 6.12.58 adapted), requires arm64 and 4 KiB pages, and for exp/uid0 also checks tracefs/kmem availability and (for the fake-cred chain) panic_on_warn=0. Any other release is unconfirmed and the program exits 2:

build/uid0 --check          # print the verdict, do not trigger
build/uid0 --check --force  # same, overriding an unconfirmed environment

Use --force or CVE98260_FORCE=1 only after verifying the kernel independently. --force relaxes the environment checks only; exp, uid0 and the app still require an exact profile from kernel_profile.h. The gate is deliberately not a runtime probe: a probe that reaches the bug would itself leave a stale timerqueue node behind.

On YLP-W00 an unprivileged shell cannot read /proc/sys/kernel/panic_on_warn or /proc/cmdline; for a known profile the gate assumes the 6.12.38-validated value 0. The same assumption is used for the adapted 6.12.58 profile until its runtime validation completes. Unknown releases stay blocked.

Usage: uid-0 shell

The exploit starts as an ordinary shell process (uid 2000, SELinux u:r:shell:s0) and does not use su. Do not launch it through su: the tested condition is an unprivileged start.

make uid0
adb push build/uid0 /data/local/tmp/uid0
adb shell chmod 755 /data/local/tmp/uid0
adb shell /data/local/tmp/uid0          # runs id and prints the context
adb shell /data/local/tmp/uid0 shell    # interactive sh -i

Before the full chain, use the non-destructive modes:

adb shell /data/local/tmp/uid0 --probe-ids  # discover kmem trace event IDs
adb shell /data/local/tmp/uid0 leak         # timer/cred leak only; no UAF/reap

kmem trace event IDs are assigned at boot and are not stable across kernel builds (6.12.38 uses 318/319/322/324). --probe-ids isolates one event at a time through the writable rss_stat/trigger and discovers the raw record types; normal runs probe automatically. leak prints [base], the full timer_site/slide, and the cred allocation site without reaching run_plant().

root is static, and its child re-executes the binary to obtain a private real cred, so it must stay at the pushed path (it is exec'd through /proc/self/exe).

On success the output ends with:

Download Tool