
CVE-2026-43499 (GhostLock) HUAWEI MatePad Pro 11 GOT-W29 पर शोध
GOT-W29 (HarmonyOS 4.0, kernel 4.19.157-perf+) पर
CVE-2026-43499 (rtmutex/futex-PI UAF, "GhostLock") के लिए विशेषाधिकार वृद्धि (privilege escalation) अनुसंधान।
मुख्य निष्कर्ष:
sysctl_bootid को ओवरराइट करना, KASLR slide निकालना)।| आइटम | मान |
|---|---|
| मॉडल | HUAWEI MatePad Pro 11 GOT-W29 |
| SoC | Qualcomm kona (SM8250, Snapdragon 870) |
| सिस्टम | HarmonyOS 4.0 (104.0.0.136) |
| कर्नेल | 4.19.157-perf+ |
| VA | 39-bit, 4K pages, KASLR on |
kernel/locking/rtmutex.c में remove_waiter() रोलबैक पथ में rt_mutex_start_proxy_lock() के दौरान waiter->task के बजाय current का उपयोग करके सफाई करता है, जिससे एक लटकता हुआ (dangling) pi_blocked_on (स्टैक UAF) उत्पन्न होता है। यह 2.6.39 ~ 7.1 को प्रभावित करता है (यह कर्नेल दायरे में है)। अपस्ट्रीम फिक्स commit 3bfdc63936dd।
इस डिवाइस पर पुष्टि: स्रोत कोड rtmutex.c:1110-1112, boot.elf डीकंपाइलेशन, और वास्तविक डिवाइस ट्रिगर — सभी सत्यापित।
PI चक्र (cycle) बनाएं ताकि FUTEX_CMP_REQUEUE_PI -EDEADLK लौटाए, रोलबैक remove_waiter बग को ट्रिगर करता है, जिससे एक लटकता हुआ pi_blocked_on (waiter थ्रेड के कर्नेल स्टैक पर rt_waiter की ओर इशारा करता हुआ) रह जाता है।
पुराना ट्रिगर waiter को requeue लक्ष्य futex को स्वयं धारण करने देता था (self-own), जो इस कर्नेल के task_blocks_on_rt_mutex में पहले से मौजूद owner==task जांच (boot.elf 0x3808-0x3868) से टकराता है, और pi_blocked_on लिखने से पहले लौटता है → कभी भी dangling pointer उत्पन्न नहीं होता → overlay प्लेसमेंट गलत निदान था (कोई क्रैश नहीं + boot_id अपरिवर्तित)।
PI चक्र: owner FUTEX_LOCK_PI(target) के साथ requeue लक्ष्य धारण करता है; waiter chain futex धारण करता है; owner फिर chain पर ब्लॉक होता है (चक्र: waiter→target→owner→chain→waiter)। requeue के दौरान चेन वॉक rt_mutex_owner(chain)==top_task का पता लगाता है → -EDEADLK → रोलबैक गलत व्यक्ति का pi_blocked_on साफ करता है → waiter का pi_blocked_on लटक जाता है। owner को प्राथमिकता घटानी होगी (nice=10) ताकि boost के बाद prio, owner_waiter->prio से भिन्न हो, अन्यथा rt_mutex_waiter_equal जल्दी लौट जाता है।
shell (uid 2000) के अंतर्गत perf_event_paranoid=-1, perf_event_open(PERF_SAMPLE_IP, exclude_user=1) कर्नेल टेक्स्ट एड्रेस क्लस्टर का नमूना लेता है, ज्ञात प्रतीक ऑफसेट के साथ संरेखित करके slide प्राप्त करता है।
samples=27651 kernel_ips=1685 lo=0xffffff948728176c hi=0xffffff9488ebfc7c
KASLR slide=0x147f200000 (40/40 IP कर्नेल टेक्स्ट क्षेत्र में मैप होने पर सत्यापित)
runtime _stext=0xffffff9487280800
टूल: tools/perf_kaslr.c। चलाने की शर्त: shell (Shizuku rish), कोई seccomp अवरोधन नहीं।
[M] CMP_REQUEUE_PI ret=-1 errno=35 (EDEADLK!)
[W] WAIT_REQUEUE_PI ret=-1 errno=110 (ETIMEDOUT) ← waiter लौटता है
[M] waiter_returned=1 ← लटकता हुआ pi_blocked_on छोड़ता है
टूल: tools/edeadlk_probe.c (variant 8+2+1 = 11, या 27)।
rt_mutex_adjust_prio_chain step[7] fake waiter पर rb_erase (एकल-बायाँ-बच्चा पथ) करता है: *(tree_left) = tree_pc + __rb_change_child वृद्धिशील लेखन। target.h में सभी ऑफसेट boot.elf डिसअसेंबली से मापे गए हैं।
देखें exploit/ghostlock-source/src/target.h। मुख्य बिंदु:
स्व-लिखित KPM (tools/kpm-debug/rtmutex-dbg.c, KernelPatch 0.13.5 inline-hook) fake walk के दौरान overlay (tree/task/lock) का पुनर्निर्माण करता है और next_lock पैरामीटर को empty_zero_page में बदलता है (KernelPatch _transit8 संशोधित fargs के साथ मूल फ़ंक्शन को कॉल करता है), जिससे rt_mutex_adjust_prio_chain [3] next_lock==waiter->lock पास होता है, [5] trylock शून्य लॉक पर सफल होता है, [6] ownerless, [7] rt_mutex_dequeue (rb_erase एकल-बायाँ-बच्चा) निष्पादित होता है — sysctl_bootid को &loggers[0][1] में बदल दिया गया, slide-kaslr-ok।
REPAIR3 waiter=0xffffff801ecdbc00 lock=0xffffffa7c4950000
slide boot_id_leaked_nfulnl_logger value=ffffffa7c4612320
slide-kaslr-ok base=ffffffa7c1280000 slide=00000027b9200000
स्टैक ज्यामिति boot.elf फ्रेम आकारों से पुष्ट: __arm64_sys_futex(0x70) + do_futex(0x60+0x1a0) में rt_waiter sp+0xc0 पर → गहराई 0x1b0; pselect पथ stack_fds[0] गहराई 0x210, अंतर 0x60 = 12 शब्द। इसलिए word_i stack_fds[12+i] पर पड़ता है: शब्द 0-2 ex[2..4] इनपुट क्षेत्र में (सीधे नियंत्रणीय), शब्द 6-7 (task/lock) res_in[3..4] में (in[3..4] + POLLIN-ready fd के साथ एन्कोडेड), शब्द 3-5/8-10 शून्य छोड़े गए। pselect ready fd के कारण तुरंत लौटता है → waiter यूज़र-स्पेस में व्यस्त-प्रतीक्षा (busy-wait) करता है (सिग्नल अक्षम, शून्य syscall) जब तक consumer ट्रिगर पूरा नहीं करता।
overlay के task/lock शब्द res_in[3]/[4] पर पड़ते हैं (fd ready द्वारा एन्कोडेड)। मापन से पता चला कि res_in[4] (lock) pselect रिटर्न पथ पर नियतात्मक रूप से ओवरराइट हो जाता है (rt_sigreturn फ्रेम अवशेष), कभी भी payload के fake_lock के बराबर नहीं होता; res_in[3] (task) कभी-कभी बरकरार रहता है। यूज़र-स्पेस से बचाव (FP ऑपरेशन + sched_yield) केवल do_notify_resume ट्रिगर दर को ~21% तक कम कर सकता है, लेकिन lock ओवरराइट लगभग अनिवार्य है।
दो अन्य संबंधित तथ्य:
rt_mutex_adjust_pi() में if (!owner) return 0; है, fake_lock में owner न होने पर adjust_prio_chain कॉल नहीं होता। popsicle (6.12) में यह जांच नहीं है। owner-ful payload पोर्ट किया गया है (fake_lock owner=fake_task|1), लेकिन lock शब्द के अनिवार्य ओवरराइट के कारण विश्वसनीय रूप से ट्रिगर नहीं हो सकता।boot.elf डिसअसेंबली __arm64_sys_ppoll / do_sys_poll के बाद पुष्टि: pollfd एक 16-बाइट संरचना है (fd 4B + events 4B + revents 4B + pad 4B), जो fake waiter के 64-बिट task/lock शब्दों को वहन नहीं कर सकता — fd मान सीमित है (वास्तविक fd होना चाहिए), events केवल 4 बाइट है और सतत नहीं है, revents कर्नेल द्वारा लिखा जाता है (अनियंत्रणीय)।
smt878u / popsicle पूर्ण विशेषाधिकार वृद्धि कर सकते हैं: उनकी स्टैक ज्यामिति शब्दों को यूज़र-नियंत्रित in/out/ex (pselect के 3 fd_set) में पड़ने देती है। GOT-W29 का waiter स्थान (bits+0x60 → task/lock res_in[3]/[4] में) के पास यह विंडो नहीं है। यह कर्नेल स्टैक ज्यामिति का अंतर है, कार्यान्वयन दोष नहीं।
app डोमेन (untrusted_app) के पास कोई तैयार KASLR चैनल नहीं है (perf/kallsyms/pagemap/dmesg सभी अस्वीकृत); CMP_REQUEUE_PI 1 लौटाता है (requeue सफल) और EDEADLK रोलबैक नहीं होता; गैर-root cpuset का major_only चेन वॉक के step[6] को कठोर रूप से शॉर्ट-सर्किट करता है। QOS बदलने का एकमात्र प्रवेश द्वार /dev/iaware_qos_ctrl SELinux द्वारा अस्वीकृत है। इसलिए सामान्य app विशेषाधिकार इस CVE को ट्रिगर नहीं कर सकता।
स्व-लिखित KernelPatch मॉड्यूल (rtmutex-dbg) वास्तविक डिवाइस पर fake waiter और राइट प्रिमिटिव का अवलोकन करने के लिए, LyraVoid/KernelPatch 0.13.5 (FolkPatch समान स्रोत) हेडर पर आधारित संकलित।
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
आउटपुट rtmutex_dbg.kpm। संकलन फ्लैग में -fno-pic -fno-pie -fno-asynchronous-unwind-tables -fno-unwind-tables शामिल होना चाहिए (Makefile में पहले से मौजूद): clang डिफ़ॉल्ट PIC GOT रिलोकेशन उत्पन्न करता है, डिफ़ॉल्ट .eh_frame (R_AARCH64_PREL32) उत्पन्न करता है — KPM लोडर इनमें से किसी का समर्थन नहीं करता → लोड विफल -1।
FolkPatch का superkey su है (APatch के डिफ़ॉल्ट KernelPatch के बजाय)। sc_kpm_load टूल का उपयोग करें (स्रोत sc_kpm_load.c):
adb shell /data/local/tmp/sc_kpm_load su /sdcard/Download/rtmutex_dbg.kpm # लोड करें
adb shell /data/local/tmp/sc_kpm_load unload rtmutex-dbg su # अनलोड करें
adb shell /data/local/tmp/sc_kpm_load ctl rtmutex-dbg counts su # काउंटर
run_rtmdbg_test.sh (डिवाइस पर /data/local/tmp/ghostlock-test/): shell पहचान (GOT_SLIDE_NO_RT=1 वास्तविक पथ) में GhostLock चलाता है, 0.5s sync चक्र लॉग हानि रोकता है, dmesg -w डिस्क पर लिखता है, 90s में स्वतः समाप्त (SIGSTOP सॉफ्ट-लॉक रिबूट रोकता है)। परीक्षण से पहले:
adb shell 'su -c "sh /sdcard/ghostlock-test/set_debug_no_reboot.sh"' # रिबूट रोकें
[RTMDBG])REPAIR3: राइट प्रिमिटिव सत्यापन के दौरान overlay पुनर्निर्माण और next_lock पैरामीटर परिवर्तनFAKEWALK skip: कवर किया गया fake walk छोड़ा गया (लिखता नहीं, क्रैश नहीं)prio_chain[N] / prio_chain_ret: walk कॉल और रिटर्न मान (0=पूर्ण; 4294967261=-EDEADLK)do_select n=320: कर्नेल-साइड res_in[3]/[4]futex op=13/14: CMP_REQUEUE_PI / WAIT_REQUEUE_PIHuawei oops/panic पूर्ण स्टैक /data/log/bbox/history.log में दर्ज है।
tools/ सत्यापन उपकरण (perf KASLR, EDEADLK प्रोब, KPM टूलचेन)
target/ सभी मापे गए ऑफसेट
exploit/ पोर्ट किया गया slide.c (EDEADLK ट्रिगर परिवर्तन सहित)
| hook | कार्य |
|---|
rt_mutex_adjust_pi | PI समायोजन रिकॉर्ड करता है; overlay कवर होने पर skip (pi_blocked_on साफ करता है) |
rt_mutex_adjust_prio_chain | fake walk बिना शर्त skip; पूर्ण waiter dump |
__arm64_sys_pselect6 / __arm64_sys_ppoll | रिटर्न पथ पर _TIF_WORK_MASK साफ करता है; fd_set अवलोकन |
do_select | कर्नेल-साइड res_in[3]/[4] पढ़ता है |
__arm64_sys_futex | WAIT_REQUEUE_PI / CMP_REQUEUE_PI ट्रैक करता है |
rt_mutex_dequeue | step[7] राइट प्रिमिटिव निष्पादन और tree आकार की पुष्टि करता है |