
استغلال إثبات المفهوم لثغرة CVE-2022-37969، وهي تصعيد صلاحيات محلي في برنامج تشغيل نظام ملفات السجل المشترك في ويندوز. يوضح رش الكومة، وسرقة الرموز المميزة، والكتابة التعسفية في النواة لتحقيق صلاحيات النظام.
المؤلفون: Ricardo Narvaja و Daniel Kazimirow (Solid)
لأغراض العرض التوضيحي فقط. يعمل الاستغلال الكامل على أنظمة Windows 11 21H2 الضعيفة.
إثبات مفهوم وظيفي مبني على المعلومات المنشورة سابقًا بواسطة Zscaler
اطلع على المقال فهم تصعيد الامتيازات المحلية في برنامج تشغيل Windows Common Log File System Driver المرتبط بـ CVE-2022-37969.
شرح خطوات الاستغلال:
السيناريو المستخدم هنا هو Windows 11 21H2 (OS Build 22000.918) و clfs.sys v10.0.22000.918
الخطوة الأولى هي إنشاء ملف باسم MyLog.blf في المجلد العام (%public%)، باستخدام الدالة CreateLogFile():



ثم يقوم بإنشاء عدة ملفات سجل بأسماء عشوائية باستخدام حلقة Loop.
وداخل الحلقة، يستدعي الدالة getBigPoolInfo() الخاصة بنا:

يستدعي NtQuerySystemInformation()، مع 0x42 (66 بالنظام العشري) كوسيط أول، وسيعيد في v5 المعلومات حول التخصيصات التي تمت في bigpool، والتي تكون بنيتها من النوع SYSTEM_BIGPOOL_INFORMATION.

علينا استدعاء هذه الدالة مرتين. الاستدعاء الأول سيعيد خطأً، لكنه سيعطينا الحجم الصحيح للمخزن المؤقت (buffer) لاستدعاء الدالة مرة ثانية للحصول على المعلومات المطلوبة.

ستستقبل v5 معلومات بنية SYSTEM_BIG_POOL_INFORMATION.

يُخزَّن عدد التخصيصات في bigpool في الحقل الأول المسمى Count, وفي الحقل الثاني توجد مصفوفة من البنى SYSTEM_BIGPOOL_ENTRY.

ثم سنبحث عبر جميع البنى عن وسم "Clfs" والحجم 0x7a00.

يخزّن في مصفوفة تسمى kernelAddrArray العنوان VirtualAddress الذي يمثل الحقل الأول لكل بنية تحمل وسم CLFS وحجم 0x7a00. من الآن فصاعدًا، ستُسمى التجمعات (pools) التي تستوفي الشرطين معًا: "التجمعات الصحيحة".

بالإضافة إلى تخزين كل تجمع صحيح في المصفوفة، فإنه يخزن آخر تجمع صحيح تم العثور عليه في محتوى المتغير a2، الذي يُستخدم كوسيط للدالة.

بهذه الطريقة، يشير a2 دائمًا إلى آخر تجمع صحيح تم إنشاؤه بوسم CLFS وحجم 0x7a00.
المتغير v26 يخزن دائمًا التجمع الصحيح السابق الذي تم العثور عليه لأنه يساوي v24 (v26=v24)، قبل استدعاء getBigPoolinfo()، لكن v24 يُحدَّث عند الخروج من هذا الاستدعاء بآخر تجمع صحيح تم العثور عليه، بينما يبقى v26 مع التجمع الصحيح السابق الذي تم العثور عليه.

ثم يطرح العنوانين من بعضهما، وفي حال كانت النتيجة سالبة، يعكس المعاملين بحيث تكون النتيجة موجبة دائمًا.

بهذه الطريقة، سيخزن v32 الفرق بين VirtualAddress لآخر تجمعين صحيحين تم العثور عليهما.
ثم يقوم بشيء مشابه، في هذه الحالة v23 يساوي صفرًا في البداية، لذلك يقوم بـ v23=v32 في المرة الأولى.

في المرة التالية داخل الحلقة، ما زال v23 يحمل القيمة نفسها وليست صفرًا، لذلك يخرج من الحلقة ويذهب إلى هنا.

يحمل V32 الفرق الأخير وv23 الفرق السابق، وإذا كانا متساويين، فإنه يخرج ويزيد العداد بمقدار واحد، لكنه يعيد تعيين العداد إلى الصفر.
الفكرة هي العثور على 6 مقارنات متتالية لوسوم CLFS وحجم 0x7a00 تكون فروقها متساوية، وسيكون هذا الفرق هو 0x11000. سنرى عند التنفيذ أنه عندما يجد 6 عناصر متتالية بمسافات متساوية (لأنه يبدأ من الصفر)، سيعطي قيمة الفرق بينها.


هناك نرى أنه وجد 6 عناصر متتالية وخرج من حلقة إنشاء ملفات السجل.
في المجلد "public" يمكننا رؤية الملفات التي تم إنشاؤها

تفتح الدالة craftFile() الخاصة بنا الملف الأصلي (MyLog.blf) وتعدّله لتفعيل الثغرة.

بعد تعديل الملف، من الضروري تغيير CRC32، وإلا فسنحصل على خطأ ملف تالف
تقع هذه القيمة عند الإزاحة 0x80C من الملف.

بعد ذلك، يقوم بتنفيذ HeapSpray، مستخدمًا الدالة VirtualAlloc() لتخصيص الذاكرة، في العنوانين الاعتباطيين 0x10000 و 0x5000000 على التوالي، ويحفظ في التخصيص الثاني (0x10000) القيمة 0x5000000 كل 0x10 بايت.

يستخدم CreatePipe() لإنشاء أنبوب مجهول (anonymous pipe) واستدعاء NtFsControlFile() باستخدام 0x11003c كوسيط لإضافة سمة (attribute)، ويمكنك لاحقًا استدعاء الدالة نفسها مع الوسيط 0x110038 لقراءة تلك السمة.