
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 का बेस पता संग्रहीत होता है।