
CVE-2026-29923 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट, pstrip64.sys में BYOVD विशेषाधिकार वृद्धि। IOCTL के माध्यम से भौतिक मेमोरी रीड/राइट का प्रदर्शन करता है ताकि SYSTEM टोकन चुराया जा सके और उन्नत शेल लॉन्च किया जा सके।
अस्वीकरण: यह कोड केवल शैक्षिक और रक्षात्मक अनुसंधान उद्देश्यों के लिए प्रदान किया गया है। इसे कर्नेल शोषण की समझ को बढ़ाने और रक्षकों को समान कमजोरियों से बचाने में मदद करने के लिए लिखा गया था। इस परियोजना का कोई भी अनधिकृत, अवैध या दुर्भावनापूर्ण उपयोग सख्त वर्जित है।
हैश: ab01485bb7c8bc1a9c86096eeea6d31d8fad557bf4d44072b46373d2203faa6e
ड्राइवर नाम: pstrip64.sys
CVE: CVE-2026-29923
"अपना स्वयं का कमजोर ड्राइवर लाएं" (BYOVD) हमला एक पुराना लेकिन बेहद प्रभावी तरीका है जिसके द्वारा हमलावर एक विरासत ड्राइवर का उपयोग करके आधुनिक विंडोज सुरक्षा सुरक्षा को बायपास कर सकते हैं जिसे ऑपरेटिंग सिस्टम अभी भी आधिकारिक रूप से विश्वसनीय मानता है। एक बार ड्राइवर लोड हो जाने के बाद, हमलावर इसकी खामियों का उपयोग करके एक मानक, अनप्रिविलेज्ड प्रक्रिया और पूर्ण सिस्टम-स्तरीय नियंत्रण के बीच की खाई को पाटता है।
इस सप्ताह की शुरुआत में, pstrip64.sys ड्राइवर में एक नई कमजोरी का खुलासा हुआ, जिसे CVE-2026-29923 के रूप में ट्रैक किया गया है। यह ब्लॉग पोस्ट एक्सप्लॉइट के पूरे जीवनचक्र को विस्तार से बताता है: मेरे प्रारंभिक कमजोरी अनुसंधान और प्रूफ ऑफ कॉन्सेप्ट (PoC) के विकास से लेकर अपने वातावरण को सुरक्षित करने वाले रक्षकों के लिए कार्रवाई योग्य शमन रणनीतियों तक।
pstrip64.sys ड्राइवर EnTech Taiwan PowerStrip (संस्करण 3.90.736 तक) से जुड़ा एक विरासत कर्नेल-मोड घटक है। जबकि इसका वैध उद्देश्य उन्नत ग्राफिक्स कार्ड डिस्प्ले ट्वीकिंग को सक्षम करना है, इसके गहरे सिस्टम विशेषाधिकार इसे हमलावरों के लिए एक अत्यधिक आकर्षक लक्ष्य बनाते हैं।
जब कमजोरी का पहली बार खुलासा हुआ, तो मैंने इसके DriverEntry फ़ंक्शन का विश्लेषण करके शुरू किया। यह कर्नेल ड्राइवर के लिए मुख्य प्रारंभिक रूटीन के रूप में कार्य करता है, \Device\PSTRIP64 डिवाइस ऑब्जेक्ट बनाता है और इसे \DosDevices\PSTRIP64 सिम्बोलिक लिंक के माध्यम से उपयोगकर्ता-मोड अनुप्रयोगों के लिए एक्सपोज़ करता है। इससे भी महत्वपूर्ण बात, यह ड्राइवर की डिस्पैच टेबल को कॉन्फ़िगर करता है। वह प्रविष्टि जिसने तुरंत मेरा ध्यान खींचा, वह इंडेक्स 14 (IRP_MJ_DEVICE_CONTROL) पर थी, जो उपयोगकर्ता द्वारा प्रदान किए गए सभी IOCTL अनुरोधों को सीधे sub_11340 हैंडलर फ़ंक्शन में रूट करती है, जो हमारा प्राथमिक रुचि क्षेत्र है।
sub_11340 फ़ंक्शन प्राथमिक IOCTL डिस्पैचर के रूप में कार्य करता है, जो उपयोगकर्ता-मोड से अनुरोधों की व्याख्या करता है।
सभी एक्सपोज़्ड IOCTL में से, 0x80002008 निस्संदेह सबसे दिलचस्प है। जबकि डिफ़ॉल्ट मामले छोटे I/O पोर्ट इंटरैक्शन को संभालते हैं, 0x80002008 sub_11000 के लिए एक गेटवे के रूप में कार्य करता है, SystemBuffer को सीधे इस फ़ंक्शन में पास करके।
यह sub_11000 रूटीन सबूत का मुख्य टुकड़ा है। पहले, यह हमारे उपयोगकर्ता द्वारा प्रदान किए गए पते को लेने और इसे एक वैध सिस्टम भौतिक पते में अनुवाद करने के लिए HalTranslateBusAddress का उपयोग करता है। फिर, यह \Device\PhysicalMemory खोलता है और ZwMapViewOfSection का उपयोग करके इसे मैप करता है। लक्ष्य प्रक्रिया हैंडल को (HANDLE)0xFFFFFFFFFFFFFFFFLL (जो ZwCurrentProcess() का प्रतिनिधित्व करता है) पर हार्डकोड करके, ड्राइवर इस भौतिक मेमोरी को सीधे हमारी कॉलिंग प्रक्रिया के वर्चुअल एड्रेस स्पेस में मैप करता है। महत्वपूर्ण रूप से, यह फिर इस नए मैप किए गए वर्चुअल पते को SystemBuffer में वापस लिखता है ताकि उपयोगकर्ता को वापस किया जा सके, और आधिकारिक तौर पर हमारे एप्लिकेशन को भौतिक मेमोरी को पढ़ने और लिखने के लिए एक सीधा पॉइंटर सौंपता है।
कमजोरी को पूरी तरह से समझ लेने और एक भौतिक रीड/राइट प्रिमिटिव स्थापित करने के बाद, मेरे पास सभी आवश्यक पहेली के टुकड़े हैं। अब, प्रूफ ऑफ कॉन्सेप्ट लिखना शुरू करने का समय है।
नोट: यह PoC विशेष रूप से Windows 10 22H2 वातावरण पर विकसित और परीक्षण किया गया था। चूंकि एक्सप्लॉइट कच्चे भौतिक मेमोरी हेरफेर पर निर्भर करता है, कर्नेल संरचना ऑफसेट और भौतिक मेमोरी सीमाएं वर्तमान में मेरे सेटअप के लिए हार्डकोडेड हैं। इसे अपनी मशीन पर परीक्षण करने के लिए, आपको विंडोज कर्नेल ऑफसेट को अपडेट करना होगा और भौतिक पता स्कैनिंग रेंज को अपने विशिष्ट OS बिल्ड और RAM कॉन्फ़िगरेशन से मिलाने के लिए समायोजित करना होगा।
मेरे एक्सप्लॉइट में पहला कदम ड्राइवर के साथ संचार स्थापित करना है। मैंने ड्राइवर के सिम्बोलिक लिंक (\\.\PSTRIP64) पर CreateFileA को कॉल करके ऐसा किया। एक बार जब मेरे पास एक वैध हैंडल था, तो मुझे पहले विश्लेषित 0x80002008 IOCTL का दुरुपयोग करने का एक साफ तरीका चाहिए था। मैंने MapPhysicalMemory() नामक एक रैपर फ़ंक्शन बनाया। यह फ़ंक्शन मेरे कस्टम PSTRIP_MAP_REQUEST स्ट्रक्ट को लक्ष्य भौतिक पते और मैं जिस मेमोरी चंक को पढ़ना चाहता हूं उसकी लंबाई से भरता है।
फिर मैं इस स्ट्रक्ट को सीधे DeviceIoControl के माध्यम से ड्राइवर को भेजता हूं। यदि सफल होता है, तो ड्राइवर उस भौतिक मेमोरी को सीधे मेरे उपयोगकर्ता-मोड एप्लिकेशन में मैप करता है और OutputResult फ़ील्ड में वर्चुअल बेस पता लौटाता है। अब मैं इस लौटाए गए पते को एक मानक C++ पॉइंटर में बदल सकता हूं, जिससे मुझे सिस्टम की भौतिक RAM तक कच्ची, अनप्रिविलेज्ड पहुंच मिलती है।
मेरे भौतिक रीड/राइट प्रिमिटिव के पूरी तरह से संचालन में आने के बाद, मेरा लक्ष्य कर्नेल डेटा संरचनाओं को ढूंढना था जिनमें प्रक्रिया विशेषाधिकार होते हैं। विंडोज में, प्रत्येक चल रही प्रक्रिया को एक EPROCESS संरचना द्वारा दर्शाया जाता है।
विंडोज कर्नेल पूल में EPROCESS संरचनाओं को पूल टैग नामक एक विशिष्ट 4-बाइट पहचानकर्ता का उपयोग करके आवंटित करता है। प्रक्रियाओं के लिए, यह टैग स्ट्रिंग Proc है (जो हेक्स में 0x636F7250 में अनुवादित होता है)। सिस्टम की भौतिक RAM को स्कैन करके, मैं इस सटीक स्ट्रिंग की खोज कर सकता था।
मेरा एक्सप्लॉइट 0x10000000 से 0x140000000 तक भौतिक मेमोरी स्पेस के माध्यम से लूप करता है, मेमोरी को 2MB चंक (STEP_SIZE = 0x200000) में मैप करता है। मैं प्रत्येक मैप किए गए चंक को एक कच्चे बाइट सरणी में कास्ट करता हूं और इसे 16-बाइट चंक (sizeof(_POOL_HEADER)) में स्कैन करता हूं।
हालांकि, भौतिक मेमोरी में केवल Proc टैग ढूंढना पर्याप्त नहीं है। मेमोरी गड़बड़ है, वह टैग समाप्त प्रक्रिया का अवशेष हो सकता है, या केवल यादृच्छिक डेटा जो हेक्स मान से मेल खाता है। यदि मैं आंख मूंदकर मान लेता कि प्रत्येक Proc टैग एक वैध EPROCESS संरचना है और मेमोरी को संशोधित करना शुरू कर देता, तो मैं तुरंत BSOD का कारण बनता।
स्थिरता सुनिश्चित करने के लिए, मुझे ह्यूरिस्टिक्स का उपयोग करके संरचना को मान्य करना पड़ा। पहले, मैं EPROCESS संरचना की शुरुआत की गणना करता हूं (जो पूल टैग से थोड़ा ऑफसेट बैठता है)। वहां से, मैं एक चल रही प्रक्रिया के लिए कुछ ज्ञात स्थिरांकों की जांच करता हूं:
0x2 (सामान्य प्राथमिकता) है।0x0 है।यदि ये सभी ह्यूरिस्टिक्स पास हो जाते हैं, तो मैं काफी आश्वस्त हो सकता हूं कि मैं एक वैध, सक्रिय प्रक्रिया देख रहा हूं। फिर मैं इसका अद्वितीय प्रक्रिया आईडी (PID) पढ़ता हूं। यदि PID मेरी अपनी एक्सप्लॉइट प्रक्रिया से मेल खाता है, तो मैं इसके टोकन पॉइंटर का भौतिक पता सहेजता हूं। यदि PID 4 है (विंडोज System प्रक्रिया), तो मैं इसके अत्यधिक विशेषाधिकार प्राप्त टोकन का वास्तविक मान निकालता और सहेजता हूं।
अंत में, मैं अपनी प्रक्रिया के टोकन पॉइंटर के सहेजे गए भौतिक पते को निकटतम 4KB सीमा पर संरेखित करता हूं और केवल उस विशिष्ट पेज को मैप करने के लिए आखिरी बार MapPhysicalMemory() का उपयोग करता हूं।
इसके बाद, मैं सटीक ऑफसेट पर नेविगेट करता हूं और अपने टोकन को सिस्टम टोकन मान से ओवरराइट करता हूं। तुरंत, विंडोज कर्नेल मेरी एक्सप्लॉइट प्रक्रिया को NT AUTHORITY\SYSTEM के रूप में मानता है।
सिस्टम स्थिरता सुनिश्चित करने के लिए पेज को अनमैप करने के बाद, मैं cmd.exe को चलाने के लिए बस CreateProcessA को कॉल करता हूं। चूंकि मेरी वर्तमान प्रक्रिया उन्नत है, नई कमांड प्रॉम्प्ट इन शीर्ष-स्तरीय विशेषाधिकारों को प्राप्त कर लेती है, जिससे हमला सफलतापूर्वक पूरा हो जाता है!
नोट: अपने प्रारंभिक डिबगिंग चरण के दौरान मैंने जो एक महत्वपूर्ण विवरण खोजा, वह यह है कि ड्राइवर मैप किए गए पॉइंटर को कैसे संभालता है। SystemBuffer->LowPart = (unsigned int)BaseAddress; निष्पादित करके, ड्राइवर 64-बिट वर्चुअल बेस पते को वापस लौटाने से पहले इसे 32-बिट मान में डाल देता है। यह ट्रंकेशन पते के उच्च बिट्स को खो देता है, जिसके परिणामस्वरूप जब मैंने इसे अपने 64-बिट एक्सप्लॉइट में डीरेफरेंस करने का प्रयास किया तो तत्काल एक्सेस उल्लंघन हुआ। इस समस्या को सफाई से बायपास करने के लिए, मैंने बस अपने उपयोगकर्ता-मोड PoC को 32-बिट एप्लिकेशन के रूप में संकलित किया, जिससे यह सुनिश्चित हुआ कि लौटाया गया पॉइंटर पूरी तरह से वैध बना रहे।
नोट: अपने प्रारंभिक परीक्षण के दौरान, मुझे एक दिलचस्प किनारे का मामला मिला: मेरा PoC मेमोरी में मेरी एक्सप्लॉइट प्रक्रिया को सफलतापूर्वक ढूंढने में सक्षम था, लेकिन यह System प्रक्रिया (PID 4) को खोजने में विफल रहा।
कारण समझने के लिए, मुझे सीधे भौतिक मेमोरी का निरीक्षण करने की आवश्यकता थी। मैंने एक कर्नेल डीबगर (WinDbg) संलग्न किया और System प्रक्रिया का वर्चुअल पता और डायरेक्टरी बेस प्राप्त करने के लिए कमांड का उपयोग किया। फिर मैंने उस वर्चुअल पते को RAM में उसके सटीक भौतिक पते में अनुवाद करने के लिए !vtop का उपयोग किया।
मैं अपने PoC से जुड़े अपने उपयोगकर्ता-मोड डीबगर पर वापस आ गया। मैंने अपने मेमोरी स्कैनिंग लूप पर एक सशर्त ब्रेकपॉइंट सेट किया, इसे उस समय निष्पादन को रोकने का निर्देश दिया जब मेरा MapPhysicalMemory() फ़ंक्शन System प्रक्रिया के भौतिक पते वाले 2MB चंक को पकड़ ले।
एक बार ब्रेकपॉइंट हिट होने के बाद, मैंने मैप की गई मेमोरी के कच्चे बाइट्स का मैन्युअल रूप से निरीक्षण करना शुरू किया। यहीं पर मैंने विंडोज कर्नेल पूल आवंटन के बारे में एक महत्वपूर्ण विवरण खोजा।
जब विंडोज किसी प्रक्रिया के लिए मेमोरी आवंटित करता है, तो यह एक _POOL_HEADER (जिसमें हमारा Proc टैग होता है) से शुरू होता है, उसके बाद एक _OBJECT_HEADER, और अंत में EPROCESS संरचना ही होती है। मानक उपयोगकर्ता-मोड अनुप्रयोगों के लिए, इन हेडर में अतिरिक्त ट्रैकिंग डेटा होता है, जिसका अर्थ है कि वास्तविक EPROCESS संरचना पूल टैग के 0x80 बाइट बाद शुरू होती है।
हालांकि, System प्रक्रिया की मेमोरी का निरीक्षण करने पर एक अलग लेआउट का पता चला। System प्रक्रिया में इनमें से कुछ मानक ट्रैकिंग हेडर का अभाव है। Proc टैग से EPROCESS संरचना की शुरुआत का ऑफसेट केवल 0x40 बाइट था!
समाधान सीधा था। मैंने अपने PoC को दोनों पूल हेडर आकारों को संभालने के लिए अपडेट किया, जब भी उसे Proc टैग मिलता है तो संभावित ऑफसेट (0x40 और 0x80) की एक सरणी के माध्यम से लूप करके।
साइबर सुरक्षा हमलावरों और रक्षकों के बीच बिल्ली-चूहे का एक अंतहीन खेल है। जहां हमलावर लगातार कमजोर ड्राइवरों की तलाश करते हैं, वहीं आधुनिक सुरक्षा उत्पादों और ब्लू टीमों के पास इस सटीक ऑपरेशन का पता लगाने और इसे ब्लॉक करने के कई मजबूत तरीके हैं।
BYOVD (अपना स्वयं का कमजोर ड्राइवर लाएं) हमले को रोकने का सबसे प्रभावी तरीका ड्राइवर को पहले स्थान पर लोड होने से रोकना है।
यदि ड्राइवर पहले से ही लोड है, तो सुरक्षा उत्पाद टोकन हेरफेर चरण के दौरान एक्सप्लॉइट का पता लगा सकते हैं।
NT AUTHORITY\SYSTEM तक बढ़ाना एक बहुत बड़ा लाल झंडा है।cmd.exe) उत्पन्न करती है, खासकर जब मूल प्रक्रिया को SYSTEM के रूप में चलाने का कोई कारण नहीं है।