
Android GKI 6.12 kernel exploit for CVE-2026-43499, rt_mutex rollback bug को pselect stack overwrite के साथ जोड़कर Samsung और Pixel devices पर root प्राप्त करने के लिए।
Android GKI 6.12 लाइन (Samsung और Pixel डिवाइस) के लिए एक Linux कर्नल एक्सप्लॉइट जो CVE-2026-43499 को लक्षित करता है: rt_mutex_start_proxy_lock() में एक बगी remove_waiter() रोलबैक जो waiter::task के बजाय current का उपयोग करता है, जिससे एक waiter का pi_blocked_on उसके (बाद में पॉप किए गए) कर्नल स्टैक rt_mutex_waiter पर लटका रह जाता है। एक pselect() fd_set स्टैक ओवरराइट और एक consumer sched_setattr-संचालित नकली rb_tree वॉक के साथ मिलकर, यह एक नियतात्मक कर्नल राइट प्रिमिटिव और पूर्ण रूट देता है।
एक्सप्लॉइट एक LD_PRELOAD शेयर्ड लाइब्रेरी (preload.so) के रूप में चलता है और रूट के बाद के आर्टिफैक्ट के रूप में एक रूट su डेमन और वॉलपेपर इंस्टॉल करता है।
स्थिति: सक्रिय विकास। m1q (ZF1) लक्ष्य ब्रिंग-अप का फोकस है। pipei tmp_page-uname bootstrap वर्तमान सक्रिय मार्ग है: वॉक डिवाइस पर साफ़ सिद्ध है (बिल्ड #33: प्रति-चाइल्ड सीडेड rt_mutex क्षेत्र) और पूर्ण 168-उम्मीदवार स्वीप प्रगति पर है। configfs CFI मार्ग m1q पर एक डेड एंड है (Rust ashmem — कोई इंजेक्टेबल fops स्लॉट नहीं) और C-ashmem लक्ष्यों के लिए मार्ग बना हुआ है। प्रति-लक्ष्य ऑफ़सेट डिवाइस के अनुसार भिन्न होते हैं — उन पर भरोसा करने से पहले वास्तविक कर्नल बाइनरी के विरुद्ध सत्यापित करें।
जब FUTEX_CMP_REQUEUE_PI एक waiter को रीक्यू करता है और रीक्यू की डेडलॉक-डिटेक्शन चेन वॉक -EDEADLK लौटाती है, तो __rt_mutex_start_proxy_lock() remove_waiter() (rtmutex.c:1535) के माध्यम से रोलबैक करता है। रोलबैक waiter को वेट ट्री से सही ढंग से डीक्यू करता है लेकिन रीक्यू कॉलर के pi_blocked_on को साफ़ करता है, waiter->task->pi_blocked_on के बजाय। WAITER का pi_blocked_on उसके स्टैक rt_mutex_waiter पर लटका रहता है, जो futex टाइमआउट होने पर पॉप हो जाता है।
टाइमआउट के बाद, waiter का कर्नल स्टैक क्षेत्र पुनः उपयोग किया जाता है: core_sys_select() तीन fd_sets को उस स्टैक बफ़र में कॉपी करता है (ZF1 पर nfds < 344 स्टैक पथ)। एक तैयार किया गया fd_set वर्ड एरे एक नकली rt_mutex_waiter / नकली task / नकली rt_mutex (जिसका rb_tree रूट पॉइंटर नियंत्रित होते हैं) को पुनः भौतिक रूप देता है। फिर एक consumer थ्रेड sched_setattr_tid(waiter) → rt_mutex_adjust_pi() → rb_erase_cached को कॉल करता है, जो एक नियंत्रित मान का मनमाना पता राइट उत्पन्न करता है।
पूरे प्रिमिटिव के लिए PI चेन साइकल आवश्यक है: owner f_pi_target रखता है और f_pi_chain (जो waiter के पास है) पर भी ब्लॉक करता है, इसलिए चेन वॉक owner → chain → waiter → target → owner तक पहुँचती है और -EDEADLK के साथ विफल होती है। साइकल हटाने पर रीक्यू शून्य कर्नल प्रभाव के साथ सफलता लौटाता है।
sched_blocked_reason (m1q प्राथमिक, डिवाइस पर सिद्ध): एक ब्लॉक्ड kworker का सहेजा गया रिटर्न PC (stack_trace_save_tsk) रिंग बफ़र से पढ़ा जाता है और कंपाइल-इन worker_thread ऑफ़सेट के विरुद्ध तुलना की जाती है। यह किसी भी boot_id-मार्ग शब्दों के उपयोग से पहले चलता है ताकि डेटा-एलियास राइट लक्ष्य (data_addr() = p0 एलियास + slide_p0_offset) गैर-शून्य slide पर सही रहें।SLIDE_LOGGERS_0_1 रखता है; लीक किया गया मान stext का पुनर्निर्माण करता है। m1q पर पार्क किया गया: इसका W1 लक्ष्य (SLIDE_RANDOM_BOOT_ID_DATA_OFF, निम्न भौतिक RAM) पेज-निर्भर राइटेबिलिटी रखता है — slide प्रति बूट लक्ष्य को पेज सीमाओं के पार ले जाता है, इसलिए ~6/7 रन फ़ॉल्ट करते हैं। केवल SLIDE_FORCE_BOOTID (मेकैनिज़्म टेस्ट) के माध्यम से स्पष्ट रूप से प्रयोग किया जाता है, जिसमें tree_pc/tree_left स्प्रे किए गए पेज पर रीडायरेक्ट होते हैं।mm_struct-आकार की वस्तुओं को स्प्रे करें और हीप पर एक का पता लगाने के लिए futex-hash कोलिज़न का उपयोग करें, जो नकली-ऑब्जेक्ट स्प्रे बेस के रूप में उपयोग किया जाने वाला कर्नल हीप पेज पता लीक करता है। क्लीनअप पर waiters डिटैच (जॉइन नहीं) किए जाते हैं ताकि कर्नल-स्टैक OOM से बचा जा सके जिसने #26 से पहले के बिल्ड्स को क्रैश किया।pselect fd_set शब्द waiter के कर्नल स्टैक पर एक नकली rt_mutex_waiter को पुनः भौतिक रूप देते हैं; एक consumer थ्रेड का sched_setattr_tid(waiter) इसे rt_mutex_adjust_pi() के माध्यम से वॉक करता है। ZF1 वर्ड मैप (disasm-सत्यापित):
PSELECT_WAITER_WORD_SHIFT = 0, वर्ड 12 = task (@+0x50), वर्ड 13 = lock (@+0x58), वर्ड 14 = wake_state (@+0x60 = 3)। एक सीडेड नकली rt_mutex के साथ वॉक साफ़-सुथरे ढंग से पूरा होता है: [7] के rb_erase W1 *(tree_left)=tree_pc / W2 *(tree_pc&~3+8)=tree_left ही एकमात्र कर्नल राइट हैं; [11] (setprio / dequeue_pi) और [9] wake दोनों छोड़ दिए जाते हैं (prio 100 पर लोकल नकली waiter स्टैक नोड को top-waiter से बाहर रखता है)। बिल्ड #19 के बाद से हर वॉक-एंटर्ड कम्प्लीशन डिवाइस पर जीवित रहता है; उससे पहले के नियतात्मक क्रैश एक 8-बाइट पेलोड प्लेसमेंट त्रुटि (SKB_DATA_DELTA) थे, वॉक-बॉडी फ़ॉल्ट नहीं।
वैकल्पिक STAGE3=1 चरण जो एक ब्रिज चाइल्ड को फ़ोर्क करता है, उसके mm->pgd को एक स्टेज्ड नकली पेज टेबल (3-लेवल: PGD→L1→L2, RX लूप लीफ + RW स्टैक लीफ) में स्वैप करता है, और चाइल्ड को एक रजिस्टर-ओनली असेंबली ब्लॉब चलाने देता है जो 2MB भौतिक स्कैन विंडो के माध्यम से अपने स्वयं के cred को पैच करता है — configfs/pipe प्रिमिटिव के बिना all-RAM phys R/W। वर्तमान में केवल m1q लक्ष्य में वायर्ड है और डिवाइस पर नहीं चलाया गया है।
src/targets/<codename>-<build>/ में 41 लक्ष्य, प्रत्येक को न्यूनतम एक target.h (कर्नल सिंबल ऑफ़सेट, KIMAGE_TEXT_BASE, direct-map बेस, struct ऑफ़सेट) की आवश्यकता होती है। Pixel लक्ष्य (comet, tokay, tegu, caiman, komodo, frankel, mustang, rango, stallion, blazer) शेयर्ड सोर्स को ओवरराइड करते हैं; Samsung लक्ष्य (m1q-*) डिवाइस-विशिष्ट लॉजिक जोड़ते हैं।
make list-projects # पूरी सूची
कर्नल struct लेआउट प्रति डिवाइस भिन्न होते हैं — ऑफ़सेट प्रत्येक लक्ष्य के लिए वास्तविक कर्नल बाइनरी / kallsyms के विरुद्ध सत्यापित किए जाने चाहिए।
आउटपुट: build/<PROJECT>/bin/preload.so (LD_PRELOAD के माध्यम से लोड की गई शेयर्ड लाइब्रेरी)।
# डिफ़ॉल्ट प्रोजेक्ट
CC=clang make
# विशिष्ट डिवाइस लक्ष्य (डिफ़ॉल्ट: blazer-CP2A.260605.012)
CC=clang make PROJECT=m1q-BP4A.251205.006
# CC=clang के बिना: यदि मिले तो NDK का उपयोग करता है ($NDK_ROOT / $ANDROID_NDK_HOME /
# $ANDROID_NDK_ROOT), अन्यथा होस्ट clang + Android sysroot
make PROJECT=m1q-BP4A.251205.006
# बिल्ड कॉन्फ़िगरेशन दिखाएँ
make info
# क्लीन
make clean
बिल्ड एक PIE su_daemon बाइनरी (src/su_daemon.c, build/embed/su_daemon_aarch64_pie में बिल्ट) और assets/wallpaper.webp को src/su_blob.S / src/wallpaper_blob.S के माध्यम से preload.so में एम्बेड करता है।
बिल्ड आउटपुट को /data/local/tmp में पुश करें, फिर एक्सप्लॉइट को LD_PRELOAD के अंतर्गत चलाएँ। लाइव ब्रिंग-अप लक्ष्य पर tee के बिना चलाएँ — tee अपनी stdio बफ़र करता है और कर्नल पैनिक पर लॉग का अंत खो देता है (एक्सप्लॉइट stdout अनबफ़र्ड है, इसलिए सीधे फ़ाइल में रीडायरेक्ट करने से हर लाइन सुरक्षित रहती है):
adb push build/m1q-BP4A.251205.006/bin/preload.so /data/local/tmp/preload.so
adb push build/embed/su_daemon_aarch64_pie /data/local/tmp/su_daemon_aarch64_pie
adb shell "chmod 755 /data/local/tmp/preload.so /data/local/tmp/su_daemon_aarch64_pie"
adb shell "LD_PRELOAD=/data/local/tmp/preload.so \
/data/local/tmp/su_daemon_aarch64_pie \
> /data/local/tmp/output.log 2>&1"
सफलता पर डेमन /data/local/tmp/temp_su.sock पर सुनता है और su /apex/com.android.virt/bin के अंतर्गत इंस्टॉल हो जाता है।
tools/run_pipei.sh बाइनरी को पुश करता है, डिवाइस पर sha256-सत्यापित करता है (एक पुराना preload.so चुपचाप अपठनीय निर्णय उत्पन्न करता है — लैडर ने एक बार बिल्ड < #22 चलाया था जिसका लॉग फ़ॉर्मेट भिन्न है), फिर पूर्ण 168-उम्मीदवार स्वीप को 3x56 में चंक करके चलाता है ताकि एक पैनिक अधिकतम एक चंक (~10 मिनट) खो दे। एक्सप्लॉइट stdout adb के माध्यम से सीधे स्ट्रीम होता है (कोई डिवाइस-साइड रीडायरेक्ट नहीं — डिवाइस-साइड tee पैनिक पर अंत खो देता है); प्रत्येक चंक की होस्ट-साइड कॉपी /tmp/m1q_pipei_chunk{1..3}_stream.log पर रखी जाती है:
sh tools/run_pipei.sh
PASS = कोई भी चंक tmp_page uname changed + after uname='CatOS दिखाता है।
PIPEI_CHILD_REGIONS को कम से कम चंक आकार पर सेट किया जाना चाहिए अन्यथा प्रति-चाइल्ड rt_mutex क्षेत्र अनसीडेड रहते हैं और स्वीप उम्मीदवार 0 पर पैनिक करता है। पूर्ण कवरेज के लिए TMP_UNAME_PIPEI_SPLIT_ORDER_PAGES=1 भी आवश्यक है — इसके बिना मुक्त 8-पेज mm ब्लॉक का केवल एक पेज स्वीप होता है। run_pipei_slim.sh सिंगल-उम्मीदवार डायग्नोस्टिक वेरिएंट है; run_m1q_ladder.sh वॉक/आइसोलेशन बैटरी है (B16-2 ओरेकल, slide मेकैनिज़्म, स्वीप चरण)।
exploit/
Makefile
assets/ # wallpaper.webp (एम्बेडेड पोस्ट-रूट आर्टिफैक्ट)
src/
main.c # run_exploit() एंट्री पॉइंट (शेयर्ड)
preload.c # LD_PRELOAD कंस्ट्रक्टर, su/wallpaper इंस्टॉल
util.c # addr ट्रांसलेशन, पेज प्रेप, configfs हेल्पर्स
pipe.c # pipe-आधारित भौतिक रीड/राइट
root.c # cred पैचिंग / task वॉक
slide.c # KASLR slide (tracefs + boot_id मार्ग)
fops.c # pselect करप्शन + fops मैनिपुलेशन
faketables.c # स्टेज-3 नकली पेज-टेबल बिल्डर (3-लेवल)
stage3.c # pgd-swap ब्रिज चाइल्ड (कॉन्फ़िगर न होने पर no-op)
stage3_loop.S, stage3_poll.S # ब्रिज ब्लॉब + एंटर ट्रैम्पोलिन
su_daemon.c # रूट डेमन (अलग PIE के रूप में कंपाइल्ड)
su_blob.S, wallpaper_blob.S # एम्बेडेड बाइनरी
offset.h # -DTARGET_CONFIG_H के माध्यम से प्रति-लक्ष्य target.h खींचता है
kernelsnitch/ # mm_struct futex-hash लीक लाइब्रेरी
targets/<codename>-<build>/ # प्रति-डिवाइस target.h + वैकल्पिक ओवरराइड
poc/poc.c # स्टैंडअलोन POC (एक्सप्लॉइट से अलग)
लक्ष्य Makefile pick_src मेकैनिज़्म के माध्यम से शेयर्ड सोर्स को ओवरराइड करते हैं:
यदि src/targets/<PROJECT>/<file>.c मौजूद है तो शेयर्ड src/<file>.c के बजाय इसका उपयोग किया जाता है; अन्यथा शेयर्ड संस्करण का उपयोग किया जाता है।
-EDEADLK (errno 35) के साथ विफल होने पर निर्भर करता है। साइकल गायब होने पर success=1 शून्य कर्नल प्रभाव के साथ दिखाई देता है।PSELECT_WAITER_WORD_SHIFT 0 होना चाहिए (ZF1 स्टैक गहराई ELF से सत्यापित)। अन्य लक्ष्य अभी भी डिफ़ॉल्ट रूप से 1 पर हैं — उन पर भरोसा करने से पहले प्रति डिवाइस सत्यापित करें।p0_data_alias() DIRECT_MAP_BASE का उपयोग करता है (लीनियर मैप), जो ZF1 पर P0_PAGE_OFFSET से भिन्न है। डीरेफ़रेंस किए गए कर्नल ऑब्जेक्ट के लिए डेटा एलियास को लीनियर मैप का उपयोग करना चाहिए।pr_error() घातक है (exit(-1)), चेतावनी/सूचना मैक्रो के विपरीत।SLIDE_LOGGERS_0_1 का उपयोग करता है, SLIDE_NFULNL_LOGGER का नहीं; यह शब्द एक गैर-रैंडमाइज़्ड लीनियर-मैप एलियास है, इसलिए boot_id मार्ग slide-स्वतंत्र है (इसका उपयोग केवल slide=0 पर करें — पहले tracefs लीक चलाएँ)।यह Android डिवाइस के लिए एक्सप्लॉइट कोड है और सुरक्षा अनुसंधान, अधिकृत भेद्यता परीक्षण, और उस हार्डवेयर पर रक्षा कार्य के लिए है जिसका आप स्वामी हैं या जिसका परीक्षण करने की आपके पास स
mm_structwrite_iter स्लॉट नहीं है और ASHMEM_SET_NAME हीप ऑब्जेक्ट एक KVec है जिसका लेआउट configfs_bin_write_iter के private_data के साथ मेल नहीं खाता। m1q इसके बजाय kmalloc राइट की ओर मुड़ता है: लीक किए गए mm order-3 ब्लॉक को pipe_inode_info ऑब्जेक्ट के रूप में रीक्लेम करें और pselect W1 राइट (*(tree_left) = tree_pc) का उपयोग करके एक उम्मीदवार के tmp_page स्लॉट (+0x90) को UTS namespace पेज (init_uts_ns) से ओवरराइट करें। एक fanout राइट sysname ऑफ़सेट पर एक मार्कर नाम रखता है और uname() "CatOS" रिपोर्ट करता है — बिना किसी स्टैटिक-ऑब्जेक्ट निर्भरता के एक सत्यापन योग्य कर्नल राइट। C-ashmem लक्ष्य configfs पथ रखते हैं।preload.c एम्बेडेड su डेमन इंस्टॉल करता है (tmpfs-माउंटेड /apex/com.android.virt/bin में, साथ ही adbd-namespace और लोकल वेरिएंट) और वॉलपेपर बदल देता है।| वेरिएबल | डिफ़ॉल्ट | उद्देश्य |
|---|
PSELECT_ROUTE_SHIFT | कंपाइल-टाइम | मुख्य मार्ग (m1q) के लिए A/B fd_set वर्ड शिफ्ट |
SLIDE_SHIFT | कंपाइल-टाइम | slide मार्ग के लिए A/B fd_set वर्ड शिफ्ट |
PSELECT_ROUTE_DELAY_USEC | 50000 | sched_setattr से पहले consumer विलंब (> 0 होना चाहिए) |
SLIDE_CONSUME_USEC, SLIDE_ENTER_DELAY_USEC, SLIDE_CONSUMER_CORE | — | Slide मार्ग टाइमिंग/पिनिंग |
SKIP_SLIDE | 0 | direct-map फ़ॉलबैक का उपयोग करें (slide = 0) |
SLIDE_ONLY | 0 | केवल KASLR slide चलाएँ और बाहर निकलें |
SLIDE_P0_OFFSET, SLIDE_BOOTID_OFF | — | slide p0-एलियास / boot_id ऑफ़सेट ओवरराइड करें |
SLIDE_FORCE_BOOTID | 0 | tracefs slide के बाद boot_id pselect मार्ग को बाध्य करें (मेकैनिज़्म टेस्ट) |
KSNITCH_COLLISIONS | 4 | KernelSnitch कोलिज़न गणना |
STAGE3 | 0 | pgd-swap ब्रिज चाइल्ड चरण सक्षम करें (m1q) |
STAGE3_DRYRUN | 0 | टेबल स्टेज/सत्यापित करें फिर स्वैप से पहले निरस्त करें |
PAGE_RECLAIM_SENDS, PSELECT_SIMPLE_LAYOUT | — | स्प्रे/fd-set लेआउट ट्वीक |
TMP_PAGE_UNAME | 0 | m1q pipei tmp_page-uname bootstrap चलाएँ |
TMP_UNAME_PIPEI_SWEEP, TMP_UNAME_PIPEI_ALIGN_ORDER, TMP_UNAME_PIPEI_SPLIT_ORDER_PAGES | — | स्वीप मोड; split_order मुक्त mm ब्लॉक के सभी 8 पेज स्वीप करता है |
TMP_UNAME_PIPEI_SLOT, TMP_UNAME_PIPEI_SLOT_CANDIDATES | — | स्वीप रेंज (चंक प्रारंभ / गणना) |
PIPEI_CHILD_REGIONS | 0 | प्रति-चाइल्ड सीडेड rt_mutex क्षेत्र; स्वीप चंक से >= होना चाहिए अन्यथा अनसीडेड क्षेत्र उम्मीदवार 0 पर पैनिक करते हैं |
PIPEI_RECLAIM_COUNT, PIPEI_DRAIN_COUNT | — | pipe जोड़े रीक्लेम/ड्रेन करें; रीक्लेम मिस पर बढ़ाएँ |
PIPEI_SPONGE_MB | 3072 | रीक्लेम के दौरान रखा गया Order-0 फ़्लड स्पंज (बिल्ड #29) |
PSELECT_W0_PRIO_OVERRIDE | 130 | Top-waiter prio; सीडेड क्षेत्रों के साथ निष्क्रिय (लेगेसी stale-tree गार्ड, केवल A/B) |
PSELECT_SKB_DELTA_OVERRIDE | 0 | A/B पेलोड इंट्रा-पेज ऑफ़सेट (केवल यदि कोई डिवाइस -0xe78 से भिन्न हो) |
TMP_UNAME_NAME, TMP_UNAME_HOLD_SEC | "CatOS", 5 | सफलता के बाद मार्कर नाम / होल्ड समय |
SKB_DATA_DELTA -0xe78 है (डिवाइस पर मापा गया, बिल्ड #19) — वास्तविक skb frag head disasm-व्युत्पन्न -0xe80 से 8 बाइट कम है; उस 8-बाइट त्रुटि ने बिल्ड #6-18 से हर नियतात्मक pselect क्रैश का कारण बना (walk-entered → garbage deref)। GKI 6.12 शेयर्ड मान; प्रति-लक्ष्य util.c कॉपी में एक SKB_DATA_DELTA env A/B नॉब होता है। बेक्ड कॉन्स्टेंट के ऊपर PSELECT_SKB_DELTA_OVERRIDE को स्टैक न करें।ASHMEM_SET_NAME KVec एनवेलप configfs_bin_write_iter के साथ मेल नहीं खाता। pipei tmp_page bootstrap का उपयोग करें। m1q target.h में PSELECT_WRITE_RIGHT_SPRAYED / PSELECT_PI_TOP_TASK_SELF गार्ड वॉक के [11] राइट को RO स्टैटिक fops से दूर रीडायरेक्ट करते हैं।payload+0x6000 + idx*0x40) पर रीडायरेक्ट करता है। शेयर्ड-fake_lock स्वीप उम्मीदवार ~9-12 पर पैनिक हुए (पुराने कर्नल-स्टैक नोड्स रीसाइकल), और बिल्ड #32 के खाली क्षेत्र उम्मीदवार 0 पर पैनिक हुए (0x41-भरे fake_task पर [9] owner==NULL wake)।
PIPEI_CHILD_REGIONS सेट होना चाहिए अन्यथा क्षेत्र अनसीडेड रहते हैं।