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-uaf — CVE-2026-98260 (posix-cpu-timers exec UAF) exploit research: S9280 Android 14 GKI 6.1 — UAF trigger + zero-write primitive + SELinux Permissive. Handoff doc in HANDOFF.md | Kitploit
Tools/GitHubGitHub/nanoturtle1145/cve-2026-98260-uaf
Android SecurityPrivilege EscalationMemory ForensicsVulnerability AnalysisExploitationPapers & ResearchBinary Exploitation
GitHubnanoturtle1145/cve-2026-98260-uaf

cve-2026-98260-uaf

CVE-2026-98260 (posix-cpu-timers exec UAF) exploit research: S9280 Android 14 GKI 6.1 — UAF trigger + zero-write primitive + SELinux Permissive. Handoff doc in HANDOFF.md

View Repository
5376 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

CVE-2026-98260 — trigger-only PoC (posix-cpu-timers UAF)

Vulnerability

exec: Cleanup POSIX timers right after de_thread() (author Hyunwoo Kim, upstream 2026-09-11, Fixes: 55e8c8eb2c7b = since 5.7).

A CLOCK_THREAD_CPUTIME_ID timer is attached to a non-leader thread E; in execve(), after de_thread() swaps the tid, the timer fails to look up the task by PID and the node remains in E's posix_cputimers. If exec_mmap()'s mmap_read_lock_killable() returns -EINTR (exec fails and the cleanup code comes later), do_exit() → exit_itimers() frees the k_itimer but the node is not removed; when the parent process reaps via wait4(), cleanup_timerqueue() → timerqueue_del() operates on the already-freed object → UAF.

Patch status (measured 2026-10-27 against fs/exec.c on each stable branch)

Branchposixtimer_execStatus
linux-6.1.yabsentunfixed
linux-5.15.yabsentunfixed
linux-6.6.ypresentfixed (6.6.158+)
linux-6.12.ypresentfixed (6.12.112+)

→ This project's targets (the 5.15 / 6.1 lines, plus 6.6 below 6.6.158) are all in the affected range.

Why this trigger is "deterministic" rather than a timing gamble

  1. Long write-lock window: fragment_vmas() uses memfd + MAP_FIXED to split the address space into --vmas (default 50000) non-mergeable VMAs (each has vm_pgoff = 0, and adjacent addresses have non-contiguous pgoff → vma_merge fails). The locker thread repeatedly calls mprotect over the whole range, and walking tens of thousands of VMAs keeps the mmap write lock held for milliseconds.
  2. Precise synchronization point: the killer uses tgkill(tgid, old tid, 0) returning ESRCH to determine that "de_thread() has completed the tid swap", and only then sends SIGKILL.
  3. The victim thread necessarily blocks at exec_mmap() in mmap_read_lock_killable() (the read lock is blocked by the write lock), and SIGKILL arrives microseconds later → that call returns -EINTR → exec fails. The window (ms) ≫ reaction time (µs), so no luck is involved.

⚠️ Note the distinction: the trigger is deterministic; going from UAF to arbitrary write / cred overwrite depends on orchestrating slab reuse of posix_timers_cache (order, objects/slab, CPU partial, memcg ownership), which is measurable, iterable heap engineering, not a race.

Build

export ANDROID_NDK_HOME=/home/nt/项目/samsung_root_research/06_工具链/android-ndk-r29
make            # 产物 trigger(aarch64 / Android 21+ / 仅依赖 libc、libdl)

Test Steps

adb push trigger /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/trigger"
adb shell "/data/local/tmp/trigger --check"        # 先看环境
adb shell "/data/local/tmp/trigger; echo exit=\$?"

Success indicators (any one of the three suffices):

  • After printing [*] Starting trigger, the device reboots/blacks out (most direct when panic_on_oops=1)
  • The process is killed by a signal (exit=137/139), and the system is still alive afterward
  • dmesg (requires root) shows a call stack containing cleanup_timerqueue / timerqueue_del / rb_erase / rb_next

On failure (not triggered), the program retries --attempts times by itself and prints the wait4 result of each round, then finally suggests tuning parameters: --vmas 120000 --lockers 4 --cpu 0.

Known limitations

  • A successful trigger almost inevitably reboots the device (that is the evidence of "success"); before testing, make sure there is no unsaved work.
  • Samsung's panic_on_oops may be 0: in that case the task is killed and the kernel keeps running with a corrupted rbtree; it is recommended to run 2~3 times in a row to observe whether a subsequent panic occurs.
  • This PoC does not contain privilege-escalation code (no reclaim, no touching cred); it is only used to verify reachability and determinism.

References

  • Upstream patch https://github.com/torvalds/linux/commit/acb03d3881818581052924a9bbbe92b8741ed448
  • Honor MagicPad3 Pro full chain (6.12.38, including uid-0 and KSU boot) https://github.com/woshigenm/CVE-2026-98260
  • vivo/iQOO 6.6.127 porting notes (reclaim choreography, SLUB parameters) https://github.com/janewas/cve-2026-98260-vivo-gki-research

Test record (SM-S9280 / e3q / 6.1.162 / Android 17 / security patch 2026-09-05)

测试环境:S9280ZHU6EZIF,内核 6.1.162-android14-11-34343818-abS9280ZHU6EZIF (构建 2026-09-20),未 root,shell 域(uid 2000),SELinux Enforcing。

Conclusion: the trigger is deterministic (no betting on timing)

RoundObservationMeaning
Every roundParent process tgkill(child_tgid, etid, 0) returns ESRCHde_thread() has completed the tid exchange
Every roundThen kill(child_tgid, SIGKILL), child process rc=-9exec was aborted (not rc=0 of a successful exec)
--no-killer controlexec takes 277 ms from armed to finish (287 ms with killer)The mmap write lock indeed blocks mmap_read_lock_killable() in exec_mmap()

All 5/5 rounds hit "ESRCH → SIGKILL → exec failure"; not once did exec succeed. This proves that the window for aborting exec after de_thread is controllable and stable; the window comes from the locker's mprotect (60k VMAs, 200–700 ms per call), while the parent process reacts at the µs level.

Two key pitfalls encountered (both already fixed in the code)

  1. The killer must be in the parent process: de_thread() calls zap_other_threads() to kill all other threads in the same process; a killer written in the child process dies itself at the moment of de_thread, and never sees ESRCH (manifesting as exec succeeding every time).
  2. Reclaim timing: spraying immediately after SIGKILL is ineffective—at that point the child process is not yet dead and k_itimer has not yet been freed by RCU. The correct order is waitid(WNOWAIT) to wait for the zombie → wait for the RCU callback to free → grab the page → finally wait4 to trigger the reclaim path.

Not yet done: making the UAF visible

At present, the step "node still in the queue after free" has been stably achieved, but the rb_erase at the moment of wait4 produces no oops—indicating that the victim slab page has not yet been grabbed by our spray (the partial slab of posix_timers_cache is still cached, and the page has not returned to the buddy allocator). This belongs to the exploit's reclaim choreography, not a race problem; it needs to follow the approach in the vivo notes: first use k_itimer itself to fill/empty the slab to force whole pages to be returned, then use an order-0 carrier (AIO ring, etc.) to precisely catch the split half-page.

Suggested next step: use this machine as a baseline and do targeted reclaim following the approach in docs/08-reclaim-choreography.md (vivo notes); or use a self-built kernel with KASAN to verify whether the UAF actually occurs.


Full-chain porting feasibility (SM-S9280 / 6.1.162 measured thresholds)

The public chain (Honor uid0.c) relies on tracefs kmem events to do two key things:

PurposeFor Honor 611/6.12This device (Samsung 6.1.162)
Find victim k_itimer address V (to compute payload's in-page offset)kmem:kmem_cache_allocEvent exists, but events/kmem/*/enable is 0644 root:root, so the shell cannot enable it (events/sched/*/enable is writable, kmem is not)
Determine whether the order-1 slab page has been returned to the buddy (precise page-reclaim timing)kmem:mm_page_freeSame as above: exists but cannot be enabled
Read the raw trace bufferper_cpu/cpuN/trace_pipe_rawReadable (-r--r----- root readtracefs) ✓
KASLR slidestatic site of each trace eventDoable: sched events can be enabled, and the project already has a similar technique ✓
Download Tool