
CVE-2016-3308 का उपयोग करके win32k डेस्कटॉप हीप को भ्रष्ट करें
लेखक : @55-AA, 18 सितंबर, 2016
डेस्कटॉप हीप एक कर्नेल पूल है जिसका उपयोग win32k द्वारा किया जाता है, इसे उपयोग-मोड एप्लिकेशन द्वारा शोषित किया जा सकता है। यहाँ मैं विस्तार से बताऊँगा कि कैसे एक विश्वसनीय शोषण को लागू किया जाए ताकि कर्नेल में मनमाना पता पढ़ा/लिखा जा सके। यह राइटअप और संबंधित विश्लेषण win7_sp1_x86 (बिल्ड 17842) इंस्टॉलेशन पर किया गया है।
9 अगस्त, 2016 को, माइक्रोसॉफ्ट ने MS16-098 जारी किया। कमजोरी कोड फ़ंक्शन win32k!xxxInsertMenuItem के अंदर मौजूद है, फ़ंक्शन प्रोटोटाइप है:
BOOL xxxInsertMenuItem(
PMENU pMenu,
UINT wIndex,
BOOL fByPosition,
LPMENUITEMINFOW lpmii,
PUNICODE_STRING pstrItem
);
पहले xxxInsertMenuItem में छद्म बग कोड को देखते हैं:
if (pMenu->cItems >= pMenu->cAlloced) {
if (pMenu->rgItems) {
pNewItems = (PITEM)DesktopAlloc(
pMenu->head.rpdesk,
(pMenu->cAlloced + CMENUITEMALLOC) * sizeof(ITEM),
DTAG_MENUITEM);
......
pMenu->cAlloced += CMENUITEMALLOC;
pMenu->rgItems = pNewItems;
if (wIndex != MFMWFP_NOITEM)
pItem = MNLookUpItem(pMenu, wIndex, fByPosition, &pMenuItemIsOn);
......
pMenu->cItems++;
if (pItem != NULL) {
RtlMoveMemory(pItem + 1, pItem, (pMenu->cItems - 1) *
sizeof(ITEM) - ((char *)pItem - (char *)pMenu->rgItems));
} else {
उपरोक्त कोड में, जब 9वीं (पहली से) आइटम pMenu में जोड़ी गई, तो DesktopAlloc() को एक नया pMenu->rgItems पुनः आवंटित करने के लिए कॉल किया गया। फिर MNLookUpItem() को pMenu->rgItems में आइटम का स्थान प्राप्त करने के लिए कॉल किया गया। लेकिन MNLookUpItem() द्वारा लौटाया गया pItem, pMenu के बजाय किसी अन्य pSubMenu का rgItems है, इसलिए जब RtlMoveMemory() कॉल किया गया, तो pSubMenu का pItem और उसके बाद के बाइट्स को गलत आकार के कारण अधिलेखित कर दिया गया।
निम्नलिखित बग के बारे में डिसअसेंबली कोड है, जो हीप ओवरराइट को ट्रिगर करेगा, इसका उपयोग एक नकली ट्रंक बनाने के लिए किया जा सकता है:
0: kd> u win32k!xxxInsertMenuItem+0x1f5 l8
win32k!xxxInsertMenuItem+0x1f5:
95d295af 6bc06c imul eax,eax,6Ch
95d295b2 2bc3 sub eax,ebx
95d295b4 034634 add eax,dword ptr [esi+34h]
95d295b7 50 push eax
95d295b8 8d436c lea eax,[ebx+6Ch]
95d295bb 53 push ebx
95d295bc 50 push eax
95d295bd e85ea40100 call win32k!memmove (95d43a20)
बग को ट्रैक करने के लिए, मैं WinDbg में इन ब्रेकपॉइंट्स का उपयोग करता हूँ:
ba e1 win32k!xxxInsertMenuItem
ba e1 win32k!xxxInsertMenuItem+0xf3
95d294e3 e843e70200 call win32k!DesktopAlloc (836d7bf5)
ba e1 win32k!xxxInsertMenuItem+0x129
95d294e3 e80de70200 call win32k!DesktopAlloc (836d7bf5)
ba e1 win32k!xxxInsertMenuItem+0x1f5
95d295af 6bc06c imul eax,eax,6Ch
बग को ट्रिगर करने के लिए, निम्नलिखित चरणों को करने की आवश्यकता है:
डेस्कटॉप हीप एक वैश्विक पूल है जिसका उपयोग सभी GUI प्रक्रियाओं द्वारा किया जाता है। सभी GUI ऑब्जेक्ट, जैसे विंडो, मेनू, डेस्कटॉप हीप में संग्रहीत होते हैं, और कर्नेल हीप आवंटकर्ता द्वारा प्रबंधित होते हैं। कर्नेल हीप आवंटकर्ता परिचित फ़ंक्शन जैसे RtlAllocateHeap और RtlFreeHeap का उपयोग करता है। उपयोग-मोड हीप के विपरीत, डेस्कटॉप हीप किसी भी फ्रंट-एंड आवंटकर्ता का उपयोग नहीं करता है, इसलिए कोई लो फ्रैग्मेंटेशन हीप, कोई लुकासाइड सूची आदि नहीं है। साथ ही Windows 8 और बाद के संस्करणों तक कोई हीप एनकोडिंग नहीं है। win7_sp1_x86 पर ट्रंक की संरचना निम्नलिखित है:
typedef struct _HEAP_ENTRY {
USHORT Size;
UCHAR Flags;
UCHAR SegmentIndex;
USHORT PreviousSize;
UCHAR SegmentOffset;
UCHAR UnusedBytes;
} HEAP_ENTRY, *PHEAP_ENTRY;
Size और PreviousSize फ़ील्ड चंक आकार को दाएँ-शिफ्ट HEAP_GRANULARITY_SHIFT (32-बिट सिस्टम में 3 के रूप में परिभाषित) बिट्स का प्रतिनिधित्व करते हैं, Size फ़ील्ड वर्तमान चंक निर्दिष्ट करता है, और PreviousSize सामने वाले चंक को निर्दिष्ट करता है। Flags के सबसे निचले बिट को आमतौर पर HEAP_ENTRY_BUSY(0x01) पर सेट किया जाता है, यह दर्शाता है कि चंक उपयोग में है, यदि 0x00 नहीं है।
निम्नलिखित आकृति इन फ़ील्ड्स और इन चंक ब्लॉक के बीच संबंध प्रदर्शित करती है। दूसरा हरा अंडरलाइन WORD (0x000f) दर्शाता है कि वर्तमान चंक का आकार 0x78 बाइट्स है, दूसरा काला अंडरलाइन WORD (0x0003) दर्शाता है कि सामने वाले चंक का आकार 0x18 बाइट्स है, और लाल अंडरलाइन WORDs (0x0001) दर्शाते हैं कि वर्तमान चंक उपयोग में हैं। यहाँ, चंक आकार में हेडर आकार शामिल है, हेडर को ऊपर HEAP_ENTRY संरचना के रूप में परिभाषित किया गया है।

हीप भ्रष्टाचार के लिए यह सबसे महत्वपूर्ण विशेषता है, जहाँ हीप आवंटकर्ता हमेशा हाल ही में जारी किए गए चंक को प्राप्त करता है। इसका मतलब है कि हम वास्तव में किसी भी आकार और किसी निश्चित स्थान पर एक चंक आवंटित कर सकते हैं।
बग का लाभ उठाकर, मैं डेस्कटॉप हीप में कुछ बाइट्स को अधिलेखित कर सकता हूँ, इस प्रकार मुझे एक नकली चंक मिलेगा, जो एक सामान्य चंक को बदल देगा, फिर मैं बदले गए चंक को जारी करता हूँ, ताकि नकली चंक को मुक्त चंक सूची के शीर्ष पर धकेल दिया जाए। बाद में नकली चंक का पुन: उपयोग किया गया, मैं इसमें कोई भी बाइट लिख सकता हूँ, लिखने योग्य क्षेत्र कई सामान्य चंकों को ओवरलैप करता है, लेकिन यह पूरे कर्नेल-लैंड को कवर नहीं कर सकता। इसलिए मुझे ओवरलैप क्षेत्र में एक और R/W प्रिमिटिव बनाने की आवश्यकता है, जो मनमाना पता लिखने के लिए tagWND.strName का लाभ उठाता है। strName.Buffer का पॉइंटर हमें कर्नेललैंड और यूज़रलैंड दोनों सहित कहीं भी ले जा सकता है। बेशक, हमारा लक्ष्य केवल nt!HalDispatchTable है।
निम्नलिखित आकृति हीप बदलने की प्रक्रिया दर्शाती है:

प्रदर्शन के अनुसार, मैं चरण दर चरण डेस्कटॉप हीप को दूषित करता हूँ, और निम्नलिखित चरणों द्वारा शोषण को लागू करता हूँ: