
CVE-2017-8570 Exp तथा शोषण नमूनों का विश्लेषण
कारण: जब Office दस्तावेज़ खोला जाता है, तो FLTLDR.EXE का उपयोग उस एम्बेडेड EPS फ़ाइल को रेंडर करने के लिए किया जाता है जिसमें यह भेद्यता होती है। यह फ़ाइल PostScript भाषा में लिखी जाती है, और हमलावर इसे "save-restore" ऑपरेशन के माध्यम से शोषित कर सकता है; इसका स्वरूप एक UAF भेद्यता है। जब उपयोगकर्ता किसी दूषित ग्राफ़िक छवि वाली फ़ाइल खोलता है, या जब उपयोगकर्ता किसी दूषित ग्राफ़िक छवि को Office फ़ाइल में सम्मिलित करता है, तो यह भेद्यता शोषित हो सकती है।
प्रभावित संस्करण: Microsoft Office 2010 Service Pack 2, Microsoft Office 2013 Service Pack 1, Microsoft Office 2016
POC: kcufId's Github
मैंने काफी समय तक ऑनलाइन खोजा, लेकिन EPSIMP32.FLT युक्त Office इंस्टॉलेशन पैकेज नहीं मिला। सौभाग्य से, kcufId ने EPS फ़ाइलें लोड करने के लिए LoadEps.exe प्रदान किया। kcufId को धन्यवाद।
LoadEps.exe पहले EPSIMP32.FLT लोड करता है:

फिर यह ImportGr को कॉल करके EPS फ़ाइल लोड करना शुरू करता है:

यहाँ सीधे F7 के साथ आगे बढ़ें, और फिर EPSIMP32.FLT के अंदर सेट किए गए ब्रेकपॉइंट पर सफलतापूर्वक रुक सकते हैं।
मुख्य विषय में जाने से पहले, आइए PostScript ऑब्जेक्ट संरचना की व्याख्या करें।
// PostScript Object
struct PostScript object
{
dword type;
dword attr;
dword value1;
dword value2; // if array, point to userdict where store the array object
}ps_obj;
जहाँ विभिन्न type मान निम्नानुसार हैं:
0x0 nulltype
0x3 integertype
0x5 realtype
0x8 booleantype
0x10 operatortype
0x20 marktype
0x40 savetype
0x300 nametype
0x500 stringtype
0x900 filetype
0x30000 arraytype
0x0B0000 packedarraytype
0x70000 packedarraytype
0x110000 dicttype
0x210000 gstatetype
उदाहरण के तौर पर स्ट्रिंग लेते हुए, इसकी भंडारण संरचना समझाई गई है। forall फ़ंक्शन पर ब्रेकपॉइंट सेट करके, आप देख सकते हैं कि यह स्ट्रिंग को कैसे संभालता है (forall फ़ंक्शन का पता कैसे लगाएँ, https://paper.seebug.org/368/ देखें)।

चित्र 1 ps_obj के अनुरूप है; इसका value2 फ़ील्ड इंडेक्स सूची में संबंधित आइटम (चित्र 2) की ओर इंगित करता है; इंडेक्स आइटम 0x30 आकार की संरचना की ओर इंगित करता है, जिसके ऑफ़सेट 0x24 पर 0x28 आकार की संरचना (चित्र 5) की ओर इंगित करने वाले पॉइंटर का पॉइंटर संग्रहीत होता है, और ऑफ़सेट 0x2C पर स्ट्रिंग का आकार (चित्र 3) संग्रहीत होता है; चित्र 5 में संरचना के ऑफ़सेट 0x4 पर इंडेक्स सूची में संबंधित आइटम का पता (अर्थात् चित्र 4 का 0x01DB5E94) संग्रहीत होता है, ऑफ़सेट 0x20 स्ट्रिंग के अंतिम भंडारण स्थान (चित्र 6) की ओर इंगित करता है, और ऑफ़सेट 0x24 वास्तव में आवंटित मेमोरी आकार है — स्ट्रिंग आकार + 1।
0x30 आकार की संरचना:
+0x0 dword
+0x4 dword
+0x8 dword
+0xc dword
+0x10 dword
+0x14 dword
+0x18 dword
+0x1c dword
+0x20 dword
+0x24 dword pp_struct //指向大小为0x28结构的指针的指针
+0x28 dword
+0x2c dword size //字符串实际大小
0x28 आकार की संरचना (यदि यह एक सरणी हो, तो संरचना का आकार 0x2C होता है, और ऑफ़सेट 0x28 सरणी तत्वों की ओर इंगित करता है, जिनमें से प्रत्येक ps_obj होता है):
+0x0 dword
+0x4 dword //存储该结构于索引列表中对应项的地址
+0x8 dword
+0xc dword
+0x10 dword
+0x14 dword
+0x18 dword
+0x1c dword
+0x20 dword ptr_object //指向字符串最终存储位置
+0x24 dword size //实际所占内存大小,字符串实际大小+1
भेद्यता की पहली ट्रिगरिंग:

पहले VM स्थिति को l62 चर में सहेजा जाता है, फिर l63 चर के प्रत्येक अक्षर के लिए l61 —>> l59 —>> l56 प्रक्रिया को कॉल किया जाता है; l62 restore पिछली स्थिति को पुनर्स्थापित करता है, जिससे /l62 save def कथन के बाद l63 द्वारा आवंटित मेमोरी स्थान मुक्त हो जाता है, और यह एक डैंगलिंग पॉइंटर बन जाता है।

l95-l99 चर आगे के प्रवाह को निर्धारित करते हैं, और इनके मान सभी 0 (अर्थात 32-बिट) हैं:

भेद्यता की दूसरी ट्रिगरिंग: पहले l63 को संग्रहीत करने के लिए 0x27 आकार (वास्तव में 0x28 घेरता है) की मेमोरी आवंटित की जाती है:

फिर l62 restore पिछली स्थिति को पुनर्स्थापित करता है, जिससे l63 द्वारा आवंटित मेमोरी मुक्त हो जाती है और यह एक डैंगलिंग पॉइंटर बन जाता है; इसके बाद l100 निष्पादित होता है, और पहले l63 द्वारा उपयोग की गई मेमोरी का उपयोग l102 (अर्थात l136) स्ट्रिंग की 0x28 संरचना को संग्रहीत करने के लिए किया जाता है (यही बताता है कि l63 ने 0x27 आकार की मेमोरी क्यों आवंटित की):

इस संरचना के ऑफ़सेट 0x4, 0x20 और 0x24 पर मान क्रमशः प्राप्त करें:

अंत में, l136 स्ट्रिंग की सामग्री संशोधित की जाती है (चित्र केवल आंशिक संशोधन दिखाता है):

ये संशोधन सावधानीपूर्वक तैयार किए गए हैं और भेद्यता की तीसरी ट्रिगरिंग के दौरान उपयोग किए जाएँगे।
भेद्यता की तीसरी ट्रिगरिंग: 0x37 तत्वों वाली एक सरणी आवंटित की जाती है, और फिर लूप में 0x34वें तत्व पर l62 restore निष्पादित होता है:

restore निष्पादित होने के बाद, सरणी की 0x30 संरचना l193 स्ट्रिंग की सामग्री द्वारा अधिलेखित हो जाती है:

इस प्रकार, अंतिम (0x36) forall प्रक्रिया द्वारा निष्पादित ऑब्जेक्ट उपरोक्त चित्र में 0x30 संरचना बन जाता है, और इसके 0x36वें तत्व को प्राप्त करने पर हम उस स्ट्रिंग तक पहुँचते हैं जो दूसरी ट्रिगरिंग के दौरान सावधानीपूर्वक तैयार की गई थी:

और प्राप्त सरणी तत्व 4 आकार की एक सरणी है, जिसका पहला तत्व एक स्ट्रिंग है जिसका प्रारंभिक पता 0 और आकार 0x7FFFFFFF है:

यह सरणी l159 चर में संग्रहीत होगी, और इसका पहला तत्व — प्रारंभिक पता 0 और आकार 0x7FFFFFFF वाली स्ट्रिंग — l201 चर में संग्रहीत होगा। उसके बाद l201 चर के माध्यम से किसी भी पते का मान प्राप्त किया जा सकता है।
kernel32.dll का बेस पता प्राप्त करना:


इस प्रकार, l314 चर में EPSIMP32.FLT का बेस पता संग्रहीत होता है।

नोट: search कमांड का सिंटैक्स इस प्रकार है:

निर्दिष्ट गैजेट ढूँढना:


file प्रकार की संरचना बनाना:
l199 l201 get_dword
/l487 exch def
l487 l201 get_dword
/l488 exch def
l488 36 my_add l201 get_dword
/l489 exch def
l489 l201 get_dword
/l490 exch def
l490 32 my_add l201 get_dword
/l491 exch def
l199 l491 l201 put_data_to_array
l199 12 my_sub 2304 l201 put_data_to_array

l492 पते (l491+0x32) पर निर्मित डेटा लिखें:
l492 0 l201 put_data_to_array %% 0x00 0
l492 4 my_add l375 l201 put_data_to_array %% 0x04 Address of <5E C3>
l492 8 my_add l373 l201 put_data_to_array %% 0x08 Address of <94 00 00 00 00 5E C3>
l492 12 my_add l377 l201 put_data_to_array %% 0x0C Address of <C2 0C 00>
l492 16 my_add l370 l201 put_data_to_array %% 0x10 Address of VirtualProtect()
l492 20 my_add 0 l201 put_data_to_array %% 0x14 0
l492 24 my_add 0 l201 put_data_to_array %% 0x18 0
l492 28 my_add 0 l201 put_data_to_array %% 0x1C 0
l492 32 my_add l368 l201 put_data_to_array %% 0x20 Address of Shellcode
l492 36 my_add l368 l201 put_data_to_array %% 0x24 Address of Shellcode——lpAddress
l492 40 my_add l349 l201 put_data_to_array %% 0x28 Size of Shellcode——dwSize
l492 44 my_add 64 l201 put_data_to_array %% 0x2C PAGE_EXECUTE_READWRITE——flNewProtect
l492 48 my_add l493 l201 put_data_to_array %% 0x30 lpflOldProtect
अंत में, closefile निर्देश निष्पादित करते समय Shellcode पर कूदें:

EPS शोषण स्क्रिप्ट \word\media निर्देशिका में स्थित है, और अनज़िप करने के बाद इसे देखा जा सकता है। इस भेद्यता के शोषण नमूने Shellcode भाग को छोड़कर लगभग समान हैं, इसलिए विश्लेषण के लिए Patchword संगठन के एक नमूने को उदाहरण के रूप में लिया गया है।
फ़ाइल नाम: Cyber_Secure_Pakistan.docx
MD5: DD89BBB916A2C909630EC78CBB0E13E5
Shellcode पर कूदें और स्टैक पुनर्स्थापित करें:

मेमोरी आवंटित करना:

फ़ंक्शन कॉल पता प्राप्त करना:

डीबगिंग के दौरान, संभवतः पर्यावरणीय समस्या के कारण CreateToolhelp32Snapshot फ़ंक्शन का कॉल पता सफलतापूर्वक प्राप्त नहीं हो सका:

पता मैन्युअल रूप से भरें और विश्लेषण जारी रखने के लिए Word खोलें। प्रक्रियाओं की सूची बनाकर WINWORD.exe खोजें:

C:\ProgramData\Microsoft\DeviceSync निर्देशिका में MSBuild.exe नामक एक प्रोग्राम बनाएँ:

फ़ाइल सामग्री लिखें; यह सामग्री EPS स्क्रिप्ट के payload_32 चर में संग्रहीत है:


vmtools.dll फ़ाइल बनाना:

फ़ाइल सामग्री लिखें; यह सामग्री EPS स्क्रिप्ट के payload_32_f2 चर में संग्रहीत है:


VMwareCplLauncher.exe फ़ाइल बनाना:

इसकी सामग्री EPS स्क्रिप्ट के payload_32_f1 चर में संग्रहीत है:

यह फ़ाइल VMware हस्ताक्षर वाली एक श्वेत-सूचीबद्ध (whitelisted) फ़ाइल है:

explorer.exe में निम्नलिखित सामग्री इंजेक्ट करें:

इसका कार्य VMwareCplLauncher.exe प्रक्रिया बनाना है:

आगे की प्रक्रिया का उल्लेख 360 की इस रिपोर्ट में किया गया है; यह लेख फिलहाल उसके विश्लेषण भाग पर चर्चा नहीं करता:

इच्छुक पाठक उस रिपोर्ट को आगे पढ़ सकते हैं।
नोट: इस भेद्यता के शोषण नमूने मूल रूप से समान हैं; अंतर अंतिम MSBuild.exe पेलोड में है, जो EPS स्क्रिप्ट के payload_32 चर में संग्रहीत होता है। इसे सीधे डंप किया जा सकता है, और DOS फ़ाइल हेडर को भरने के बाद इसे IDA में खींचकर विश्लेषण किया जा सकता है।