
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 जांचों में विफल हो सकें: