
MediaTek MT6993 SoC के साथ Android 16 पर Xiaomi 17T Pro (warhol) के लिए CVE-2026-43499 को लक्षित करने वाला स्थानीय विशेषाधिकार वृद्धि शोषण।
CVE-2026-43499 (IonStack) स्थानीय विशेषाधिकार वृद्धि, Xiaomi 17T Pro (warhol) पर पोर्ट किया गया — MediaTek MT6993, Android 16.
केवल GKI 6.12 / android16। वेटर ओवरले,
pselectस्टैक लेआउट और हर स्ट्रक्ट ऑफ़सेट उसी ब्रांच से बंधे हैं, इसलिए यहाँ की कोई भी चीज़ 6.6, 6.1 या 5.10 पर स्थानांतरित नहीं होती — उनके लिए उन पर बनाया गया बेस चाहिए।generate_target.pyकिसी अन्य बैनर को अस्वीकार कर देता है, बजाय ऐसा हेडर निकालने के जो डिवाइस पर फॉल्ट करे।
स्थिति: कार्यशील। 2026-07-26 को क्लीन बूट से डिवाइस पर सत्यापित, दोनों प्रीलोडर वेरिएंट के तहत —
uid=0(root) context=u:r:kernel:s0, SELinux परमिसिव,suस्थापित। रूट स्थायी नहीं है: हर बूट के बादLD_PRELOADलाइन दोबारा चलाएँ। pselect रेस 100% नहीं है: असफल रन फ़ोन को पैनिक कर सकता है, और रीबूट के बाद पुनः प्रयास सामान्य है। पूरा विवरणWARHOL_PORT.mdमें देखें।
इस डिवाइस को kernel-MTE फिक्स चाहिए। स्टॉक अपस्ट्रीम popsicle इसे रूट नहीं कर सकता — यह हमेशा के लिए
kernel page retry N/12में घूमता रहता है। देखें Kernel MTE।
बिल्ड दो प्रीलोडर स्वादों में आते हैं, PRELOADER=retail (डिफ़ॉल्ट) और PRELOADER=eng। वे अलग-अलग टारगेट निर्देशिकाओं का चयन करते हैं ताकि कोई एक दूसरे के पते अधिलेखित न कर सके — देखें Preloader variant।
फ़ैक्ट्स नीचे रिटेल पूर्ण-OTA पैकेज से पढ़े गए हैं — मेटाडेटा, A/B पेलोड मैनिफेस्ट, और payload.bin से निकाली गई boot/vendor_boot/dtbo इमेज।
| आइटम | मान | स्रोत |
|---|---|---|
| कोडनेम | warhol | pre-device=warhol |
| बिल्ड फ़िंगरप्रिंट | Xiaomi/warhol_global/warhol:16/BP2A.250605.031.A3/OS3.0.304.0.WPSJPXM:user/release-keys | post-build |
| इन्क्रीमेंटल | OS3.0.304.0.WPSJPXM (JP ग्लोबल) | post-build-incremental |
| Android | 16, SDK 36 | post-sdk-level=36 |
| सुरक्षा पैच | 2026-05-01 | post-security-patch-level |
| OTA प्रकार | A/B (payload.bin, CrAU, 39 पार्टीशन) | ota-type=AB |
| SoC | MediaTek MT6993 (Dimensity 9500) | vendor_boot DTB में mediatek,mt6993-* कम्पेटिबल; कमांडलाइन bootopt=64S3,32N2,64N2 |
| कर्नेल | 6.12.38-android16-5-g1d46253471dd-ab15048002-4k, clang 19.0.1 | निकाली गई boot.img से पढ़ा गया बैनर |
| बूट इमेज | हेडर v4, कर्नेल 18,898,125 B, LZ4-legacy कम्प्रेस्ड, कोई ramdisk नहीं | tools/bootinfo.py |
यहाँ SoC से ज़्यादा कर्नेल मायने रखता है: warhol उसी GKI ब्रांच पर है जिस पर अपस्ट्रीम popsicle (android16-5, 4K पेज), एक पैचलेवल के अंतर पर — 6.12.38 बनाम popsicle का सत्यापित 6.12.23। स्ट्रक्ट ऑफ़सेट अभी भी हर बिल्ड के लिए पुनः उत्पन्न करने पड़ते हैं; केवल एक्सप्लॉइट का आकार आगे बढ़ता है। किसी अलग OS3.0.x बिल्ड को अपना target.h चाहिए।
एक्सप्लॉइट चेन कर्नेल के बजाय GKI ब्रांच के संस्करण-लॉक है। rt_mutex वेटर लेआउट, pselect स्टैक ओवरले और pipe_buffer स्ट्रक्ट ऑफ़सेट सभी कर्नेल पर निर्भर हैं, इसलिए पुरानी ब्रांच पर समान-विक्रेता बेस की तुलना में 6.12/android16 बेस बेहतर है।
x-spy/CVE-2026-43499-popsicle एकमात्र सार्वजनिक इम्प्लीमेंटेशन है जो 6.12/android16 GKI लाइन (Xiaomi 17 / Pro / Ultra, कर्नेल 6.12.23-android16-5) पर सत्यापित है, इसलिए source/ उसी से लिया गया है और जितना संभव हो उतना अपस्ट्रीम के करीब रखा गया है — दो लाइनों को छोड़कर (देखें Kernel MTE, जिसकी इस डिवाइस को काम करने के लिए ज़रूरत ही है)।
MediaTek डेल्टा का अधिकांश भाग बिल्ड-टाइम टारगेट जनरेशन तक सीमित है, दो हिस्सों में:
p0_phys_offset और p0_kernel_phys_load को Qualcomm xbl_config पार्टीशन से पढ़ता है, जो warhol के पास नहीं है। generate_target.py --dtb इसके बजाय vendor_boot FDT के /memory नोड से DRAM बेस पढ़ता है, और उसी तरह नीचे की ओर पूर्णांकित करता है जैसे arm64_memblock_init करता है। कर्नेल का भौतिक लोड पता प्रीलोडर की mb_kernel रिज़र्वेशन से --preloader के साथ पढ़ा जाता है; इस डिवाइस के लिए डेल्टा 0 निकलता है, और lk कर्नेल को कहीं और रखे जाने पर बूट करने से इनकार कर देता है।Image के बजाय boot.img के अंदर LZ4-legacy कम्प्रेस्ड भेजता है, इसलिए जनरेटर विश्लेषण से पहले उसे डिकम्प्रेस करता है।WARHOL_PORT.md §2 में पूरी व्युत्पत्ति है।
MediaTek lk कर्नेल को कम्पाइल-टाइम कॉन्स्टेंट पर लोड नहीं करता — यह mb_kernel नामक DRAM रिज़र्वेशन खोजता है और जाँच करता है कि वह ठीक उसी पर उतरा है। वह रिज़र्वेशन प्रीलोडर द्वारा की जाती है, यही वजह है कि फ़ोन पर चल रहा प्रीलोडर बिल्ड यहाँ एक बिल्ड इनपुट है: यह वह एकमात्र चीज़ है जो boot.img के बाहर P0_KERNEL_PHYS_LOAD को स्थानांतरित कर सकती है, और वहाँ गलत मान का अर्थ गलत लीनियर-मैप एलियास और एक मृत फ़ोन है, न कि एक असफल एक्सप्लॉइट।
इसलिए दो वेरिएंट, अलग-अलग टारगेट निर्देशिकाओं के रूप में रखे गए:
PRELOADER= | फ़ोन पर प्रीलोडर | टारगेट निर्देशिका | स्थिति |
|---|---|---|---|
retail (डिफ़ॉल्ट) | शिपिंग preloader_<device>.bin, फास्टबूट ROM के preloader_raw.img से बाइट-समान | targets/warhol-OS3.0.304.0.WPSJPXM/ | ✅ डिवाइस पर 2026-07-26 को सत्यापित |
eng | इंजीनियरिंग बिल्ड, preloader_<device>_eng.bin | targets/warhol-OS3.0.304.0.WPSJPXM-eng/ | ✅ डिवाइस पर 2026-07-26 को सत्यापित |
make preload # PRELOADER=retail -> out/preload-<device>.so
make PRELOADER=eng preload # -> out/preload-<device>-eng.so
make both # both of the above, and print their sha256
दोनों आर्टिफैक्ट अलग-अलग नामों से साथ-साथ रखे जाते हैं। इस फर्मवेयर पर वे बाइट-समान निकलते हैं — यह नीचे दिए गए विश्लेषण का परिणाम है, उसका शॉर्टकट नहीं, इसलिए दोनों अभी भी अलग-अलग बनाए और नामित किए जाते हैं।
दोनों प्रीलोडरों की मेमोरी-लेआउट तालिकाएँ स्वयं पढ़ें:
python3 tools/preloader_memlayout.py <preloader.bin> --diff <preloader_eng.bin>
इस फर्मवेयर के लिए दोनों तालिकाएँ समान हैं, दोनों में mb_kernel.start = 0x80000000, इसलिए eng target.h रिटेल वाले से बाइट-समान निकलता है और दोनों बिल्ड एक ही preload.so उत्पन्न करते हैं।
एक्सप्लॉइट तक जो पहुँचता है वह अंतर P0_KERNEL_PHYS_LOAD − P0_PHYS_OFFSET है, और इसके दोनों पद समान रूप से ठोस साक्ष्य पर नहीं टिकते:
P0_KERNEL_PHYS_LOAD — eng इमेज से मापा गया, ऊपर mb_kernel.start के रूप में।P0_PHYS_OFFSET — तर्क द्वारा स्थापित किया गया, मापा नहीं गया, क्योंकि यह memstart_addr है, जिसे कर्नेल उस /memory नोड से लेता है जिसे lk रनटाइम पर उत्सर्जित करता है उसके अनुसार जो प्रीलोडर DRAM इनिशियलाइज़ेशन के बाद रिपोर्ट करता है, न कि उस स्थिर FDT से जिसे जनरेटर पढ़ता है। तर्क यह था कि दोनों बिल्ड पूरे DRAM पथ को साझा करते हैं — समान dramc/emi/mblock/memory_layout स्रोत, eng-केवल स्ट्रिंग्स के बीच कोई मेमोरी-लेआउट कोड नहीं — और eng प्रीलोडर का एक अतिरिक्त रिज़र्वेशन, 3 MiB गतिशील security_fe_rsv जिसमें mapping=1 है, न तो निश्चित-पते वाली प्रविष्टि को विस्थापित कर सकता है और न ही DRAM के नीचे एक छेद बना सकता है। डिवाइस रन ने इसे सुलझा दिया: लीनियर-मैप एलियास eng के अंतर्गत उतरे, इसलिए पद सही है।पूरी व्युत्पत्ति targets/warhol-OS3.0.304.0.WPSJPXM-eng/NOTES.md में।
डिवाइस रन के नोट्स:
ro.boot.verifiedbootstate=green और flash.locked=1 अपरिवर्तित रहे। यह तब तक एक खुला प्रश्न था जब तक इसे आज़माया नहीं गया; यह efuse'd रूट कुंजी हैश द्वारा तय होता है, और यह बिल्ड रिटेल जैसी ही कुंजियों से हस्ताक्षरित है।%s META DIS केवल-रिटेल स्ट्रिंग है, जबकि eng में एक पूर्ण META इम्प्लीमेंटेशन है जो रिटेल में नहीं है (Enable fastmeta., FAST META GPIO: %d, META_COM PORT: %d, read_meta_proinfo, …)। यह यहाँ सीधे Android में बूट हुआ, लेकिन यदि eng-फ्लैश किया गया फ़ोन कभी कहीं और पहुँचे, तो यही संभावित कारण है और कोई target.h मान इसमें शामिल नहीं है।mb_kernel को स्थानांतरित करता है, तो दोनों टारगेट अलग हो जाते हैं — उन्हें अलग निर्देशिकाओं में रखना ही उसे मौन होने से रोकता है।