
CVE-2026-50416: विंडोज़ 11 KASLR बायपास
Windows 11 Insider build 10.0.28020.2149 में, win32k डेस्कटॉप हीप का यूज़र मोड मैपिंग, ऑफसेट 0x100 पर एक कच्चा कर्नेल सेशन पूल पॉइंटर सौंपता है। कोई भी प्रोसेस जिसके पास डेस्कटॉप है, उसे पढ़ सकती है। इसमें Low इंटीग्रिटी प्रोसेस, AppContainer, शून्य क्षमताओं वाला LPAC, और एक Low इंटीग्रिटी AppContainer शामिल है जो बिल्कुल ब्राउज़र रेंडरर जैसा दिखता है। उस एक लीक हुए QWORD से आप कर्नेल डेस्कटॉप हीप बेस प्राप्त कर सकते हैं, और वहाँ से, gSharedInfo की मदद से, डेस्कटॉप पर हर विंडो, मेनू और क्लास ऑब्जेक्ट का सटीक कर्नेल वर्चुअल पता निकाल सकते हैं।
यह एक KASLR बायपास है, एक सूचना प्रकटीकरण (information disclosure)। यह अपने आप में कोड एक्ज़ीक्यूशन नहीं है। मैं यह शुरू में ही स्पष्ट कर देना चाहता हूँ क्योंकि किसी बग को बढ़ा-चढ़ाकर पेश करना किसी राइटअप को अपठनीय बनाने का सबसे तेज़ तरीका है। यह, वास्तव में, उन जगहों से एक बहुत साफ़ लीक है जहाँ से उसे कभी नहीं पहुँचना चाहिए था।
CVE-2026-50416। यह पोस्ट बग, प्रूफ ऑफ कॉन्सेप्ट, और उस हिस्से का दस्तावेज़ीकरण करती है जिसने मुझे सच में चौंकाया: वे सैंडबॉक्स सीमाएँ जिनके पार कर्नेल पॉइंटर उजागर रहता है।
हर प्रोसेस जो किसी डेस्कटॉप से जुड़ती है, उसे डेस्कटॉप हीप का रीड-ओनली मैपिंग अपने एड्रेस स्पेस में मिलता है। कर्नेल इस हीप में विंडो और मेनू ऑब्जेक्ट लिखता है और यूज़र मोड उन्हें वापस पढ़ता है, जो सामान्य और आवश्यक है। जो आवश्यक नहीं है वह यह कि उस मैपिंग के ऑफसेट 0x100 में सेशन पूल का एक जीवित कर्नेल पॉइंटर मौजूद है। यूज़र मोड को दिखाई देने से पहले कोई इसे सैनिटाइज़ नहीं करता। Windows पहले से जानता है कि इस हीप में कर्नेल पॉइंटर्स को कैसे सैनिटाइज़ करना है, वह अन्य फ़ील्ड्स के लिए 0x6000000000 सेंटिनल का उपयोग करता है। यह वाला बस छूट गया।
win32k कर्नेल घटक है जो विंडो, मेनू, कर्सर, हुक, पूरी ग्राफिकल ऑब्जेक्ट दुनिया का मालिक है। win32k की बहुत सारी स्थिति प्रति डेस्कटॉप एक संरचना में रहती है जिसे डेस्कटॉप हीप कहा जाता है। विंडो ऑपरेशन को तेज़ बनाने के लिए, कर्नेल इस हीप का एक हिस्सा हर उस प्रोसेस में मैप करता है जो डेस्कटॉप से जुड़ती है, एक रीड-ओनली साझा सेक्शन के रूप में। आपकी प्रोसेस किसी विंडो का टेक्स्ट या स्टाइल सीधे इस मैपिंग से बिना सिस्कॉल के पढ़ती है। वह मैपिंग ही साझा भ्रम है जो GUI को तुरंत महसूस कराता है।
इसे यूज़र मोड से खोजना तुच्छ और प्रलेखित है। TEB में एक ClientInfo संरचना होती है। डेस्कटॉप हीप का यूज़र मोड पता ClientInfo[5] में होता है। कोड में:
PVOID teb = (PVOID)__readgsqword(0x30);
PVOID* clientInfo = (PVOID*)((BYTE*)teb + 0x800);
BYTE* desktopHeap = (BYTE*)clientInfo[5];
यह पूरा हैंडल है। अभी कोई भेद्यता नहीं। यह हिस्सा डिज़ाइन द्वारा है।
भेद्यता वह है जो उस हीप के ऑफसेट 0x100 पर बैठी है।
ULONG64 leaked = *(ULONG64*)(desktopHeap + 0x100);
परीक्षण किए गए बिल्ड पर वह QWORD कुछ इस तरह रखता है 0xFFFFA804DDE00040। यह एक कैननिकल कर्नेल पता है। यह आठ बाइट संरेखित है। यह सेशन पूल की ओर इशारा करता है, इसी डेस्कटॉप हीप के कर्नेल पक्ष में 0x40 बाइट्स के भीतर।
मैंने इसे मानने से पहले सामान्य सत्यापन जाँचें कीं।
तो यह एक वास्तविक, स्थायी, बूट सत्र भर का कर्नेल पता है, न कि कचरा और न ही कोई पुराना मान। गुण चार और पाँच एक साथ KASLR यादृच्छिकृत पते की पहचान हैं जो एक बूट के भीतर स्थिर रहता है। यह बिल्कुल वही चीज़ है जिसे KASLR को छिपाना चाहिए।
जिज्ञासुओं के लिए, मेरे वास्तविक परीक्षण सत्र में मान 0xFFFFC600DCC00040 था। वही आकार, वही व्यवहार। आपका अलग होगा क्योंकि KASLR। यही तो मुद्दा है।
एक अकेला लीक हुआ पता अच्छा है। इसे किसी विशिष्ट ऑब्जेक्ट के पते में बदलना वह जगह है जहाँ यह उपयोगी हो जाता है।
दो तथ्य काम करते हैं।
पहला, लीक हुआ मान कर्नेल डेस्कटॉप हीप में 0x40 बाइट्स के भीतर इंगित करता है। तो कर्नेल डेस्कटॉप हीप बेस = लीक हुआ मान घटा 0x40।
kernel desktop heap base = leaked minus 0x40
दूसरा, user32 gSharedInfo नामक एक संरचना निर्यात करता है। अन्य चीजों के अलावा यह आपको वैश्विक हैंडल टेबल (aheList) और एक हैंडल टेबल प्रविष्टि का आकार (HeEntrySize, x64 पर 0x20) देता है। हर HWND वास्तव में उस तालिका में एक सूचकांक है। HWND के निचले 16 बिट्स लें, उस प्रविष्टि तक पहुँचें, और प्रविष्टि का पहला QWORD डेस्कटॉप हीप के अंदर उस विंडो ऑब्जेक्ट का ऑफसेट है।
उन्हें एक साथ रखें:
offset = gSharedInfo.aheList[HWND & 0xFFFF].offset
kernel addr = kernel desktop heap base plus offset
यह किसी दिए गए HWND का समर्थन करने वाले विंडो ऑब्जेक्ट का सटीक कर्नेल वर्चुअल पता है। कोई अनुमान नहीं। कोई स्प्रे नहीं। वास्तविक पता।
प्रूफ ऑफ कॉन्सेप्ट विभिन्न क्लासेस (STATIC, BUTTON, EDIT, LISTBOX, SCROLLBAR, COMBOBOX) की छह विंडो बनाता है और सभी छह को हल करता है। यह हीप को स्कैन भी करता है और 0x100 के अलावा आधा दर्जन अन्य असैनिटाइज़्ड कर्नेल पॉइंटर्स ढूंढता है, इसलिए 0x100 केवल सबसे विश्वसनीय है, अकेला नहीं।
यह वह हिस्सा है जिसने इसे दिलचस्प से वास्तव में चिंताजनक बना दिया।
मैंने एक दूसरा प्रोग्राम लिखा जो चाइल्ड प्रोसेस को क्रमिक रूप से कठिन संदर्भों में चलाता है और प्रत्येक से पूछता है कि क्या वह उसी पॉइंटर को पढ़ सकता है। संदर्भ, उनके करने की क्षमता के अनुसार:
CreateDesktop के साथ बनाया गया एक बिल्कुल अलग डेस्कटॉप। लीक करता है, एक अलग मान के साथ क्योंकि उसका अपना डेस्कटॉप हीप है।संदर्भ एक से पाँच सभी ने एक ही कर्नेल पता लौटाया, क्योंकि वे डिफ़ॉल्ट डेस्कटॉप हीप साझा करते हैं। संदर्भ छह ने एक अलग पता लौटाया क्योंकि यह एक अलग हीप है, लेकिन तकनीक समान रूप से काम की।
पाँचवाँ वह है जिसे घूरना चाहिए। Low इंटीग्रिटी पर चलने वाली, AppContainer के अंदर, शून्य क्षमताओं वाली प्रोसेस, उतनी ही लॉक-डाउन है जितनी Windows पर कोई यूज़र मोड प्रोसेस हो सकती है। यह वह सैंडबॉक्स है जिसमें ब्राउज़र अपना रेंडरर रखता है। वह सैंडबॉक्स एक कर्नेल पता पढ़ सकता है।
इसके चुभने का एक कारण है। ब्राउज़रों का पूरा डिफेंस-इन-डेप्थ मॉडल मानता है कि भले ही कोई हमलावर किसी अलग इंजन बग के माध्यम से रेंडरर के अंदर कोड एक्ज़ीक्यूशन प्राप्त कर ले, सैंडबॉक्स नुकसान को सीमित करता है और कर्नेल को छिपाता है। KASLR उस छिपाने का एक बड़ा हिस्सा है। यह लीक सैंडबॉक्स के अंदर पहुँचता है और हमलावर को बिना किसी अतिरिक्त सिस्कॉल और बिना विशेषाधिकार वृद्धि के एक कर्नेल पता सौंप देता है। सैंडबॉक्स ने अपना काम पूरी तरह से किया। सूचना फिर भी उसके नीचे से बाहर लीक हो गई, एक साझा सेक्शन के माध्यम से जिसे सैंडबॉक्स मॉडल हानिरहित मानता है।
मैं पकड़ का इंतज़ार करता रहा। निश्चित रूप से आपको पहले एक विंडो बनानी होगी, या कोई GUI सिस्कॉल कॉल करना होगा जिसे सैंडबॉक्स नोटिस करेगा। नहीं।
इस बिल्ड पर डेस्कटॉप हीप मैपिंग user32.dll लोड होने से पहले ही प्रोसेस एड्रेस स्पेस में मौजूद होती है। प्रूफ user32 लोड करने से पहले और बाद में desktop_heap[0x100] पढ़ता है, और दोनों रीड्स एक ही कर्नेल पॉइंटर लौटाते हैं। कोई CreateWindow नहीं। कोई GetDesktopWindow नहीं। कुछ भी नहीं। किसी भी प्रोसेस में डेस्कटॉप संबद्धता, जिसमें हेडलेस सेवाएँ और बैकग्राउंड वर्कर शामिल हैं, शुरू होते ही उसे पढ़ सकती है।
रेंडरर प्रूफ इसी पर निर्भर करता है। यह एक चाइल्ड को Low इंटीग्रिटी पर AppContainer में बिना किसी क्षमता के चलाता है, उसे user32 और कुछ नहीं लोड कराता है, और वह शुरू होते ही पॉइंटर पढ़ता है। शब्दशः आउटपुट:
RENDERER|LEAKED|0xffffc600dcc00040|AC=1|IL=0x1000|NoWindowCreated
IL=0x1000 Low इंटीग्रिटी है। AC=1 AppContainer के अंदर है। NoWindowCreated बिल्कुल वैसा ही है जैसा सुनने में लगता है।
जब मैं वहाँ था, तो मैंने हीप पर एक स्कैनर लगाया ताकि देख सकूँ कि और क्या पड़ा है। यह पॉइंटर्स से अधिक है।
अन्य प्रोसेस की विंडो टाइटल। स्कैनर को अन्य प्रोसेस के बीस अद्वितीय टाइटल हीप में UTF16 में मिले, प्रत्येक को EnumWindows के विरुद्ध क्रॉस-चेक किया गया ताकि पुष्टि हो कि मालिकाना PID वास्तव में उस टाइटल वाली विंडो का मालिक था। Chrome टैब, Discord, Explorer, Spotify, ट्रे विंडो। हीप एक छोटा सा बुलेटिन बोर्ड है कि डेस्कटॉप पर हर कोई क्या कर रहा है, और कोई भी सैंडबॉक्स्ड प्रोसेस इसे बिना एक भी IPC के पढ़ सकती है।
प्रोसेस आईडी। हीप में छह सौ से अधिक DWORD मान वास्तविक चल रहे PID होने की पुष्टि करते हैं, प्रत्येक की दो तरह से पुष्टि हुई, OpenProcess के सफल होने से और PID के EnumWindows सूची में दिखने से। तो एक सैंडबॉक्स्ड प्रोसेस चुपचाप पता लगा सकती है कि डेस्कटॉप पर विंडो किसके पास हैं।
अधिक कर्नेल पॉइंटर्स। प्रति रन छह से दस, डेस्कटॉप गतिविधि के साथ बदलते हुए, सिर्फ 0x100 वाला ही नहीं।
एक चीज़ जो यह उजागर नहीं करता, जिसे मैंने जानबूझकर जाँचा: पासवर्ड एडिट कंट्रोल टेक्स्ट। मैंने ES_PASSWORD के साथ एक EDIT बनाया, उसका टेक्स्ट SecretPassword123 सेट किया, और हीप खोजा। वहाँ नहीं था। Windows डेस्कटॉप हीप में पासवर्ड फ़ील्ड सामग्री को मास्क करता है। अच्छा। छोटी सी दया।
तो हीप पते और विंडो टाइटल लीक करता है, लेकिन कम से कम आपके पासवर्ड सुरक्षित रखता है। जो विन मिले, ले लूँगा।
KASLR कर्नेल शोषण को संभाव्य बनाने के लिए मौजूद है। पता ज्ञान के बिना, एक win32k उपयोग-के-बाद-मुक्त (use after free) अंधा पूल स्प्रेइंग बन जाता है। आप कर्नेल आवंटक को भर देते हैं, उम्मीद करते हैं कि आपकी प्रतिस्थापन वस्तु वहीं गिरे जहाँ डैंगलिंग पॉइंटर इंगित करता है, और प्रार्थना करते हैं। आधुनिक पूल कार्यान्वयन पर यह सफलता दर कम है और कम होती जा रही है।
अब हमलावर को यह लीक दे दें। वे मुक्त विंडो ऑब्जेक्ट का सटीक कर्नेल पता जानते हैं। वे ठीक उसी पते पर एक प्रतिस्थापन आवंटन तैयार कर सकते हैं और नियंत्रित कर सकते हैं कि डैंगलिंग पॉइंटर वास्तव में क्या संदर्भित करता है। अंधा भ्रष्टाचार नियतात्मक हो जाता है। यह एक साफ़ KASLR बायपास का वास्तविक मूल्य है, यह एक असंबंधित मेमोरी भ्रष्टाचार बग पर विश्वसनीयता गुणक है, न कि अपने आप में एक बग।
यह लीक जिन शमनों को कमज़ोर करता है, ठोस रूप से:
gSharedInfo पढ़ता है, फिर सीधे कर्नेल पते की गणना करता है।इस एक रीड से जिन ऑब्जेक्ट्स का कर्नेल पता निकलता है: विंडो ऑब्जेक्ट (tagWND), मेनू ऑब्जेक्ट (tagMENU), क्लास ऑब्जेक्ट (tagCLS), और हैंडल टेबल के माध्यम से डेस्कटॉप हीप पर पहुँचने योग्य बाकी सब। एक रीड, पूरा डेस्कटॉप मैप।
PoC निर्देशिका में एक compile.bat है जो विज़ुअल स्टूडियो ढूंढता है और आपके चुने हुए लक्ष्य को बनाता है।
compile.bat then choose a number from the menu
लक्ष्य, वे आपको कितना दिखाते हैं उस क्रम में:
kaslr_bypass_poc.exe। मुख्य। लीक के माध्यम से कदम बढ़ाता है, कर्नेल बेस की गणना करता है, जीवित विंडो पते हल करता है, अतिरिक्त पॉइंटर्स के लिए स्कैन करता है, और पूरी श्रृंखला प्रिंट करता है। इसे पहले चलाएँ।kaslr_sandbox_proof.exe। चाइल्ड को Low IL, AppContainer, LPAC, Low IL प्लस AppContainer, और एक वैकल्पिक डेस्कटॉप पर चलाता है, फिर रिपोर्ट करता है कि क्या प्रत्येक ने लीक किया और क्या वे सभी सहमत हैं।supporting_proof_no_window.exe। साबित करता है कि लीक किसी भी विंडो के अस्तित्व से पहले काम करता है।supporting_proof_no_caps_lpac.exe। इसे AppContainer और LPAC से शून्य क्षमताओं के साथ साबित करता है।supporting_proof_sensitive_data.exe। क्रॉस-प्रोसेस डेटा एक्सपोज़र, टाइटल और PID, और पासवर्ड टेक्स्ट का एक्सपोज़ न होना साबित करता है।supporting_proof_exploitability.exe। छह विंडो क्लासेस के लिए कर्नेल पते हल करता है और शमन विफलता दिखाता है।supporting_proof_remote_trigger.exe। रेंडरर समकक्ष संदर्भ।स्वयं को आश्वस्त करने के लिए तीन त्वरित जाँचें:
एक और दो पुष्टि करते हैं कि यह एक स्थिर, साझा, वास्तविक पता है। तीन पुष्टि करता है कि यह KASLR यादृच्छिकृत है। साथ में वे बग हैं।
अपेक्षित। यूज़र मोड डेस्कटॉप हीप मैपिंग को कभी भी कच्चे कर्नेल पॉइंटर्स उजागर नहीं करने चाहिए। डेस्कटॉप हीप मेटाडेटा में किसी भी कर्नेल पॉइंटर को यूज़र मोड द्वारा देखे जाने से पहले सैनिटाइज़ किया जाना चाहिए।
वास्तविक। ऑफसेट 0x100 एक कर्नेल सेशन पूल पॉइंटर उजागर करता है। डेस्कटॉप वाली हर प्रोसेस इसे पढ़ती है, जिसमें Low इंटीग्रिटी, AppContainer, और LPAC प्रोसेस शामिल हैं। परीक्षण किए गए सत्र में हर संदर्भ ने 0xFFFFC600DCC00040 लौटाया।
सबसे सस्ता फिक्स वही है जो Windows इस हीप में पहले से कहीं और करता है। डेस्कटॉप हीप हेडर में कर्नेल पॉइंटर्स को यूज़र मोड मैपिंग तक पहुँचने से पहले सैनिटाइज़ करें, वही 0x6000000000 सेंटिनल उपचार जो अन्य फ़ील्ड्स के लिए उपयोग किया जाता है।
मजबूत फिक्स, यदि यूज़र मोड को वास्तव में हेडर पेज की आवश्यकता नहीं है, तो उस पेज को यूज़र मोड मैपिंग के माध्यम से उजागर करना पूरी तरह बंद कर देना है। Microsoft किसे चुनता है, इस पर मेरी कोई प्रबल राय नहीं है। मेरी प्रबल राय यह है कि एक कच्चा कर्नेल पॉइंटर एक __readgsqword और एक पॉइंटर dereference की दूरी पर शून्य क्षमता वाले रेंडरर से नहीं होना चाहिए।
मैं बार-बार संदर्भ पाँच पर लौटता हूँ। बिना किसी क्षमता वाला Low इंटीग्रिटी AppContainer वह बॉक्स माना जाता है जिससे कुछ भी दिलचस्प बाहर नहीं निकलता। डेस्कटॉप हीप मैपिंग उस तरह का साझा संसाधन है जिसे सैंडबॉक्स मॉडल ने बहुत पहले मैप किया छोड़ना सुरक्षित मान लिया था, क्योंकि यह केवल पढ़ने के लिए है और क्योंकि विंडो टेक्स्ट पढ़ना हानिरहित है। वे दोनों बातें अभी भी सत्य हैं। जो बदला वह यह है कि एक हेडर फ़ील्ड ने उस हानिरहित रीड-ओनली विंडो को सीधे सेशन पूल में एक झाँकने का छेद बना दिया।
सैंडबॉक्स विफल नहीं हुआ। सैंडबॉक्स के नीचे की धारणा विफल हुई। ये अलग-अलग विफलताएँ हैं, और दूसरी को आते हुए देखना कठिन है, शायद इसीलिए किसी ने इसे नहीं पकड़ा। जब तक आप ऑफसेट 0x100 नहीं पढ़ते।