CVE-2026-50416: विंडोज़ 11 KASLR बायपास
प्रभावित सिस्टम पर, Win32 डेस्कटॉप हीप के उपयोगकर्ता मैपिंग से यह रीड एक कर्नेल पता लौटाती है:
ULONG64 leaked = *(ULONG64 *)(desktop_heap + 0x100);
यही पूरा खुलासा है। शुरू में यह केवल आठ-बाइट का एक मान था जिसमें विश्वसनीय उच्च बिट्स थे। मैंने इसे पढ़ने से ज़्यादा समय यह जाँचने में बिताया कि यह मान किसके बाद आता है।
Microsoft ने इस मुद्दे को CVE-2026-50416 के रूप में प्रकाशित किया, जो CWE-200 के रूप में वर्गीकृत एक Win32k सूचना प्रकटीकरण है।
PoC x64 TEB से शुरू होता है। GS:[0x30] वर्तमान TEB देता है, TEB + 0x800 उस बिल्ड में Win32 क्लाइंट डेटा तक पहुँचता है जिसे मैंने परखा, और स्लॉट 5 में डेस्कटॉप हीप का उपयोगकर्ता पता होता है:
PVOID teb = (PVOID)__readgsqword(0x30);
PVOID *client_info = (PVOID *)((BYTE *)teb + 0x800);
BYTE *desktop_heap = (BYTE *)client_info[5];
Slot 5 उस क्लाइंट डेटा में एक और 0x28 बाइट्स है। Trellix ने Windows 10 1909 पर tagWND मैपिंग्स का अध्ययन करते हुए उसी TEB + 0x800 + 0x28 मार्ग का दस्तावेज़ीकरण किया। ये ऑफ़सेट कोई ABI नहीं हैं। मैंने इन्हें हार्ड-कोड किया है और मैप किए गए क्षेत्र का उपयोग करने से पहले परिणाम पर VirtualQuery कॉल करता हूँ।
पॉइंटर जाँच में कैनोनिकल x64 साइन एक्सटेंशन, आठ-बाइट एलाइनमेंट, और ज्ञात सेंटिनल पैटर्न शामिल हैं। फिर उस मान को नीचे दिए गए व्यवहार परीक्षणों को पास करना होता है।
मुख्य PoC ऑफ़सेट 0x100 पढ़ता है, एक STATIC, एक BUTTON, और एक EDIT विंडो बनाता है, इसे फिर से पढ़ता है, तीनों विंडो नष्ट करता है, फिर इसे एक बार और पढ़ता है।
ULONG64 before = *(ULONG64 *)(desktop_heap + 0x100);
/* Create STATIC, BUTTON and EDIT windows. */
ULONG64 after_create = *(ULONG64 *)(desktop_heap + 0x100);
/* Destroy all three. */
ULONG64 after_destroy = *(ULONG64 *)(desktop_heap + 0x100);
तीन QWORDs मेल खा गए। वे विंडो आवंटन आए और चले गए; 0x100 पर मौजूद मान नहीं बदला।
kaslr_sandbox_proof.c प्रक्रिया तुलना को संभालता है। पैरेंट प्रत्येक चाइल्ड के stdout को एक पाइप के माध्यम से रीडायरेक्ट करता है, चाइल्ड द्वारा पढ़े गए QWORD को पार्स करता है, और इसकी तुलना बेसलाइन से करता है।
डिफ़ॉल्ट डेस्कटॉप से जुड़े चाइल्ड ने वही मान लौटाया। CreateDesktop के साथ बनाए गए डेस्कटॉप को सौंपे गए चाइल्ड ने एक अलग मान लौटाया। हार्नेस Low integrity, AppContainer, इसके LPAC कॉन्फ़िगरेशन, और Low integrity AppContainer चाइल्ड का भी परीक्षण करता है। ये सभी पैरेंट का मान प्राप्त करने के बजाय अपने स्वयं के TEB और मैपिंग से रीड करते हैं।
supporting_proof_no_window.c CreateWindowEx को कॉल किए बिना रीड करता है। किसी नई USER विंडो की आवश्यकता नहीं है।
मुख्य PoC लीक हुए मान में से 0x40 घटाकर प्राप्त परिणाम को डेस्कटॉप हीप के अनुरूप कर्नेल-साइड बेस मानता है:
ULONG64 candidate_kernel_heap_base = leaked - 0x40;
फिर यह user32!gSharedInfo को खोजता है, HWND के निचले 16 बिट्स को USER हैंडल-टेबल इंडेक्स के रूप में उपयोग करता है, और HeEntrySize द्वारा aheList के माध्यम से आगे बढ़ता है। यदि उस एंट्री में पहला QWORD गैर-शून्य है और मैप किए गए हीप से छोटा है, तो PoC इसे सापेक्ष डेस्कटॉप-हीप ऑफ़सेट के रूप में उपयोग करता है:
ULONG index = (ULONG)(ULONG_PTR)hwnd & 0xffff;
BYTE *entry = (BYTE *)shared->aheList + index * shared->HeEntrySize;
ULONG64 heap_offset = *(ULONG64 *)entry;
if (heap_offset > 0 && heap_offset < heap_size) {
BYTE *user_candidate = desktop_heap + heap_offset;
ULONG64 kernel_candidate = candidate_kernel_heap_base + heap_offset;
}
यह वही सामान्य संबंध है जो पुरानी डेस्कटॉप-हीप तकनीकों में उपयोग किया जाता था: उपयोगकर्ता और कर्नेल दृश्य किसी ऑब्जेक्ट को उनके संबंधित हीप बेस से समान ऑफ़सेट पर रखते हैं। Unit 42, USER हैंडल तालिका और पुराने gSharedInfo तथा HMValidateHandle एड्रेस लीक पर उपयोगी पृष्ठभूमि देता है।
परिणामी एड्रेस PoC के कर्नेल-ऑब्जेक्ट उम्मीदवार हैं। 0x40 संबंध, हैंडल-एंट्री लेआउट, और TEB ऑफ़सेट परीक्षण के लिए उपयोग किए गए बिल्ड के हैं।
मैं ऑब्जेक्ट उम्मीदवारों को वहीं छोड़ता हूँ। desktop_heap + 0x100 पर स्थित QWORD पहले से ही उपयोगकर्ता-पठनीय मैपिंग में स्थिर कर्नेल-स्पेस एड्रेस जानकारी है। एक अलग Win32k मेमोरी-करप्शन बग इसे एड्रेस अनुमान के स्थान पर उपयोग कर सकता है।