Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
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.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
GhostLock-GOT-W29 — CVE-2026-43499 (GhostLock) research on HUAWEI MatePad Pro 11 GOT-W29 | Kitploit
Tools/GitHubGitHub/zzzxxxxxxxxxx/ghostlock-got-w29
Privilege EscalationVulnerability AnalysisExploitationReverse EngineeringMobile SecurityBinary Exploitation
GitHubzzzxxxxxxxxxx/ghostlock-got-w29

GhostLock-GOT-W29

CVE-2026-43499 (GhostLock) research on HUAWEI MatePad Pro 11 GOT-W29

View Repository
4110 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-43499 (GhostLock) — HUAWEI MatePad Pro 11 GOT-W29

Privilege escalation research on CVE-2026-43499 (rtmutex/futex-PI UAF, "GhostLock") targeting GOT-W29 (HarmonyOS 4.0, kernel 4.19.157-perf+).

Key findings:

  • The vulnerability's write primitive was successfully verified on real hardware (with KPM assistance, rewriting the kernel sysctl_bootid and deriving the KASLR slide).
  • However, real privilege escalation (shell without KPM and without RT) is not feasible: on the 4.19 kernel, the pselect return path deterministically overwrites the overlay's lock word, and there is no alternative carrier. This is a dead end determined by kernel stack geometry, not an implementation flaw (by comparison, the smt878u/popsicle devices can be fully exploited because their stack geometry differs).

Device

ItemValue
ModelHUAWEI MatePad Pro 11 GOT-W29
SoCQualcomm kona (SM8250, Snapdragon 870)
OSHarmonyOS 4.0 (104.0.0.136)
Kernel4.19.157-perf+
VA39-bit, 4K pages, KASLR on

Vulnerability

In kernel/locking/rtmutex.c, remove_waiter() cleans up using current instead of waiter->task in the rt_mutex_start_proxy_lock() rollback path, leaving a dangling pi_blocked_on (stack UAF). Affects 2.6.39 ~ 7.1 (this kernel is in range). Upstream fix commit 3bfdc63936dd.

Confirmed on this device: source rtmutex.c:1110-1112, boot.elf decompilation, and real-device triggering all verified.

Trigger

Vulnerability trigger mechanism

Create a PI cycle so that FUTEX_CMP_REQUEUE_PI returns -EDEADLK; the rollback triggers the remove_waiter bug, leaving a dangling pi_blocked_on (pointing to the rt_waiter on the waiter thread's kernel stack).

Why the old trigger failed

The old trigger had the waiter self-own the requeue target futex (self-own), which happened to hit this kernel's early owner==task check in task_blocks_on_rt_mutex (boot.elf 0x3808-0x3868), returning before writing pi_blocked_on → no dangling pointer is ever produced → the overlay placement was a misdiagnosis (no crash + boot_id unchanged).

Correct trigger (PI cycle, implemented)

PI cycle: owner FUTEX_LOCK_PI(target) holds the requeue target; waiter holds the chain futex; owner then blocks on the chain (cycle: waiter→target→owner→chain→waiter). During requeue, the chain walk detects rt_mutex_owner(chain)==top_task → -EDEADLK → rollback clears the wrong task's pi_blocked_on → the waiter's pi_blocked_on is left dangling. The owner must lower its priority (nice=10) so that after boost its prio differs from owner_waiter->prio; otherwise rt_mutex_waiter_equal returns early.

Results

KASLR leak (perf_event_open)

Under shell (uid 2000) with perf_event_paranoid=-1, perf_event_open(PERF_SAMPLE_IP, exclude_user=1) samples kernel text address clusters; aligning against known symbol offsets yields the slide.

root@kitploit:~
samples=27651 kernel_ips=1685 lo=0xffffff948728176c hi=0xffffff9488ebfc7c
KASLR slide=0x147f200000    (40/40 IPs mapped into kernel text region verified)
runtime _stext=0xffffff9487280800

Tool: tools/perf_kaslr.c. Prerequisites: shell (Shizuku rish), no seccomp interception.

EDEADLK trigger

root@kitploit:~
[M] CMP_REQUEUE_PI ret=-1 errno=35 (EDEADLK!)
[W] WAIT_REQUEUE_PI ret=-1 errno=110 (ETIMEDOUT)  ← waiter returns
[M] waiter_returned=1                              ← leaves dangling pi_blocked_on

Tool: tools/edeadlk_probe.c (variant 8+2+1 = 11, or 27).

Write primitive mechanism

rt_mutex_adjust_prio_chain step[7] performs rb_erase (single-left-child path) on the fake waiter: *(tree_left) = tree_pc + __rb_change_child incremental write. All offsets in target.h are measured from boot.elf disassembly.

Full offsets

See exploit/ghostlock-source/src/target.h. Key points:

  • task_struct: cred=0x988, prio=0x184, pi_blocked_on=0xa90, usage=0x68, mm=0x728
  • rt_mutex_waiter (HW_FUTEX_PI): tree@0x0, pi_tree@0x18, task@0x30, lock@0x38, major@0x40, prio@0x48, deadline@0x50
  • PAGE_OFFSET=0xffffffc000000000, PHYS_OFFSET=0x80000000 (kona), KIMAGE_TEXT_BASE=0xffffff8008080000

Write primitive verified on real hardware

A custom KPM (tools/kpm-debug/rtmutex-dbg.c, KernelPatch 0.13.5 inline-hook) rebuilds the overlay (tree/task/lock) during the fake walk and rewrites the next_lock parameter to empty_zero_page (KernelPatch _transit8 calls the original function with modified fargs), so that rt_mutex_adjust_prio_chain [3] next_lock==waiter->lock passes, [5] trylock succeeds on the zero lock, [6] is ownerless, and [7] rt_mutex_dequeue (rb_erase single-left-child) executes — sysctl_bootid is rewritten to &loggers[0][1], slide-kaslr-ok.

root@kitploit:~
REPAIR3 waiter=0xffffff801ecdbc00 lock=0xffffffa7c4950000
slide boot_id_leaked_nfulnl_logger value=ffffffa7c4612320
slide-kaslr-ok base=ffffffa7c1280000 slide=00000027b9200000

Overlay design

Stack geometry is confirmed by boot.elf frame sizes: in __arm64_sys_futex(0x70) + do_futex(0x60+0x1a0), rt_waiter is at sp+0xc0 → depth 0x1b0; on the pselect path, stack_fds[0] is at depth 0x210, a difference of 0x60 = 12 words. Therefore word_i lands at stack_fds[12+i]: words 0-2 fall in the ex[2..4] input region (directly controllable), words 6-7 (task/lock) fall in res_in[3..4] (encoded via in[3..4] + POLLIN-ready fd), words 3-5/8-10 are left 0. pselect returns immediately due to the ready fd → the waiter busy-waits in userspace (signals disabled, zero syscalls) until the consumer triggers completion.

Why real privilege escalation is not feasible

1. The lock word in the pselect carrier is overwritten by the return path

The overlay's task/lock words fall in res_in[3]/[4] (encoded by fd readiness). In practice, res_in[4] (lock) is deterministically overwritten on the pselect return path (rt_sigreturn frame residue) and never equals the payload's fake_lock; res_in[3] (task) is occasionally intact. Userspace mitigation (FP operations + sched_yield) can only reduce the do_notify_resume trigger rate to ~21%, but the lock overwrite is nearly certain.

Two other relevant facts:

  • The ownerless path is blocked on 4.19: rt_mutex_adjust_pi() has if (!owner) return 0;; when fake_lock has no owner, adjust_prio_chain is not called. popsicle (6.12) lacks this check. An owner-ful payload has been ported (fake_lock owner=fake_task|1), but cannot be reliably triggered because the lock word is always overwritten.
  • empty_zero_page cannot be used as a forced write target: writing to it corrupts the system-wide shared zero page → oops storm after testing.

2. The alternative carrier (ppoll) fails at the encoding level

After disassembling __arm64_sys_ppoll / do_sys_poll from boot.elf, it is confirmed: pollfd is a 16-byte structure (fd 4B + events 4B + revents 4B + pad 4B) that cannot carry the fake waiter's 64-bit task/lock words — the fd value is constrained (must be a real fd), events is only 4 bytes and non-contiguous, and revents is written by the kernel (not controllable).

3. Difference from other devices

smt878u / popsicle can be fully exploited: their stack geometry allows words to land in user-controllable in/out/ex (pselect's 3 fd_sets). The GOT-W29 waiter position (bits+0x60 → task/lock in res_in[3]/[4]) lacks this window. This is a kernel stack geometry difference, not an implementation flaw.

4. Restrictions in the normal app domain

The app domain (untrusted_app) has no available KASLR channel (perf/kallsyms/pagemap/dmesg are all denied); CMP_REQUEUE_PI returns 1 (requeue success) without going through the EDEADLK rollback; the major_only hard short-circuit in non-root cpuset skips chain walk step[6]. The only entry point for changing QOS, /dev/iaware_qos_ctrl, is denied by SELinux. Therefore, normal app privileges cannot trigger this CVE.

Debug toolchain

A custom KernelPatch module (rtmutex-dbg) is used for real-device observation of the fake waiter and write primitive, compiled against LyraVoid/KernelPatch 0.13.5 (same source as FolkPatch) headers.

Hook set

Compilation

root@kitploit:~
cd <KernelPatch>/kpms/rtmutex-dbg
make TARGET_COMPILE=aarch64-linux-android- \
  CC=$PREFIX/bin/aarch64-linux-android-clang \
  LD=$PREFIX/bin/aarch64-linux-android-ld

Produces rtmutex_dbg.kpm. Compile flags must include -fno-pic -fno-pie -fno-asynchronous-unwind-tables -fno-unwind-tables (already built into the Makefile): clang's default PIC produces GOT relocations, and the default .eh_frame generation produces R_AARCH64_PREL32 — neither is supported by the KPM loader → load fails with -1.

Loading

FolkPatch's superkey is su (not APatch's default KernelPatch). Use the sc_kpm_load tool (source sc_kpm_load.c):

root@kitploit:~
adb shell /data/local/tmp/sc_kpm_load su /sdcard/Download/rtmutex_dbg.kpm  # load
adb shell /data/local/tmp/sc_kpm_load unload rtmutex-dbg su               # unload
adb shell /data/local/tmp/sc_kpm_load ctl rtmutex-dbg counts su           # counters

One-shot test

run_rtmdbg_test.sh (on-device /data/local/tmp/ghostlock-test/): runs GhostLock as shell identity (GOT_SLIDE_NO_RT=1 real path), 0.5s sync loop to prevent log loss, dmesg -w written to disk, auto-collect after 90s (SIGSTOP to prevent soft-lock reboot). Before testing:

root@kitploit:~
adb shell 'su -c "sh /sdcard/ghostlock-test/set_debug_no_reboot.sh"'  # prevent reboot

Observation points (dmesg [RTMDBG])

  • REPAIR3: rebuilds overlay and rewrites next_lock parameter during write primitive verification
  • FAKEWALK skip: covered fake walk is skipped (no write, no crash)
  • prio_chain[N] / prio_chain_ret: walk calls and return values (0=completed; 4294967261=-EDEADLK)
  • do_select n=320: kernel-side res_in[3]/[4]
  • futex op=13/14: CMP_REQUEUE_PI / WAIT_REQUEUE_PI

Huawei oops/panic full stacks are recorded in /data/log/bbox/history.log.

Directory

root@kitploit:~
tools/      verification tools (perf KASLR, EDEADLK probe, KPM toolchain)
target/     all measured offsets
exploit/    ported slide.c (including EDEADLK trigger modifications)

Acknowledgments

  • Upstream PoCs: x-spy/CVE-2026-43499-popsicle, soralis0912/CVE-2026-43499-aristotle, JoinChang/ghostlock-oneplus, Wtrwx/smt878u-ionstack-poc (GPL-3.0)
  • CVE: NVD, Red Hat RHSB-2026-010
Download Tool
hookpurpose
rt_mutex_adjust_pirecords PI adjustments; skips when overlay is present (clears pi_blocked_on)
rt_mutex_adjust_prio_chainunconditionally skips fake walk; full waiter dump
__arm64_sys_pselect6 / __arm64_sys_ppollclears _TIF_WORK_MASK on return path; fd_set observation
do_selectkernel-side read of res_in[3]/[4]
__arm64_sys_futextraces WAIT_REQUEUE_PI / CMP_REQUEUE_PI
rt_mutex_dequeueconfirms step[7] write primitive execution and tree shape