Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2022-37969 — استغلال إثبات المفهوم لثغرة CVE-2022-37969، وهي تصعيد صلاحيات محلي في برنامج تشغيل نظام ملفات السجل المشترك في ويندوز. يوضح رش الكومة، وسرقة الرموز المميزة، والكتابة التعسفية في النواة لتحقيق صلاحيات النظام. | Kitploit
أدوات/GitHubGitHub/fortra/cve-2022-37969
تصعيد الامتيازاتتحليل الذاكرة الجنائيتحليل الثغرات الأمنيةالاستغلالاستغلال الملفات الثنائية
GitHubfortra/cve-2022-37969

CVE-2022-37969

استغلال إثبات المفهوم لثغرة CVE-2022-37969، وهي تصعيد صلاحيات محلي في برنامج تشغيل نظام ملفات السجل المشترك في ويندوز. يوضح رش الكومة، وسرقة الرموز المميزة، والكتابة التعسفية في النواة لتحقيق صلاحيات النظام.

عرض المستودع
135381منذ 3 سنواتتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2022-37969 إثبات مفهوم تصعيد الامتيازات المحلية في Windows

المؤلفون: Ricardo Narvaja و Daniel Kazimirow (Solid)

لأغراض العرض التوضيحي فقط. يعمل الاستغلال الكامل على أنظمة Windows 11 21H2 الضعيفة.

إثبات مفهوم وظيفي مبني على المعلومات المنشورة سابقًا بواسطة Zscaler

اطلع على المقال فهم تصعيد الامتيازات المحلية في برنامج تشغيل Windows Common Log File System Driver المرتبط بـ CVE-2022-37969.

الاستخدام

فهم تصعيد الامتيازات المحلية لبرنامج تشغيل Windows Common Log File System Driver المرتبط بـ CVE-2022-37969.

شرح خطوات الاستغلال:

  • إنشاء ملف سجل BLF الأولي
  • إنشاء عدة ملفات سجل BLF عشوائية
  • صياغة ملف السجل الأولي
  • تنفيذ Heap Spray مُتحكم به
  • تجهيز الدالتين CreatePipe() / NtFsControlFile()
  • بمجرد تجهيز الذاكرة سيتم تشغيل الثغرة
  • قراءة رمز النظام (System Token)
  • التحقق من الرمز
  • استبدال رمز العملية لدينا برمز النظام
  • تنفيذ عملية بصلاحيات النظام
  • تحليل التصحيح (Patch): تحليل البنى
  • إفساد مؤشر “pContainer”
  • إعادة النظر في التصحيح
  • إفساد SignatureOffset
  • إفساد المزيد من القيم
  • التحكم في الدوال التي تسمح بقراءة رمز SYSTEM
  • كتابة العملية الخاصة بنا لتحقيق تصعيد الامتيازات المحلية
  • الكود المصدري لإثبات المفهوم PoC

السيناريو المستخدم هنا هو Windows 11 21H2 (OS Build 22000.918) و clfs.sys v10.0.22000.918

إنشاء ملف سجل BLF الأولي

الخطوة الأولى هي إنشاء ملف باسم MyLog.blf في المجلد العام (%public%)، باستخدام الدالة CreateLogFile():

إنشاء عدة ملفات سجل BLF عشوائية

ثم يقوم بإنشاء عدة ملفات سجل بأسماء عشوائية باستخدام حلقة 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 من الملف.

تنفيذ Heap Spray مُتحكم به

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

تجهيز الدالتين CreatePipe() / NtFsControlFile()

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

يمكن العثور على مزيد من التفاصيل حول هذه الطريقة هنا

هناك نرى المخزن المؤقت للإدخال وهو السمة التي نضيفها، وإذا استدعينا NtFsControlFile() مرة أخرى مع الوسيط 0x11038، فسيعيد في الإخراج السمة نفسها.

ابحث في التجمع عن وسم السمة التي تم إنشاؤها (NpAt)

وعندما يعثر عليه، يحفظ في v30.Pointer العنوان VirtualAddress لهذا التجمع.

يشير V30.pointer+24 إلى AttributeValueSize في تجمع النواة ويحفظه في أحد عمليات HeapSpray التي قمنا بها سابقًا.

الفكرة هي الكتابة إلى عنوان النواة +8، لاستبدال AttributeValue.

نص، وصف مولّد تلقائيًا

تحتوي بنية PipeAttribute في حقلها الأول على LIST_ENTRY بحجم 16 بايت، ثم مؤشر إلى اسم السمة بحجم 8 بايت، ثم يأتي عند الإزاحة 0x18 (24 بالنظام العشري) الحقل AttributeValueSize، وهو الحقل الذي نخزنه في HeapSpray.

بعد ذلك، نقوم بتحميل CLFS.sys و ntoskrnl في وضع المستخدم (usermode)، وباستخدام GetProcAddress() نعثر على عناوين الدالتين ClfsEarlierLsn() و SeSetAccessStateGenericMapping().

ثم نستدعي الدالة FindKernelModulesBase() التي ستجد العنوان الأساسي للنواة لكلا الوحدتين نفسهما باستخدام NtquerySystemInformation() هذه المرة مع الوسيط SystemModuleInformation لإرجاع معلومات حول جميع الوحدات.

بهذه الطريقة، يمكننا حساب الإزاحة (offset) لكل دالة، ثم الحصول عليها في النواة

بمجرد أن تكون الذاكرة جاهزة، سيتم تشغيل الثغرة:

يتم استدعاء الدالة pipeArbitraryWrite() مرتين، وهناك علم (flag) يكون صفرًا في البداية للاستدعاء الأول، وعندما يكون في الاستدعاء الثاني قيمته 1، فإنه سيغير قيم HeapSpray.

نص، وصف مولّد تلقائيًا

في الاستدعاء الأول، توجد القيم التالية في عنوان الذاكرة 0x5000000

تذكر أن هذه القيمة، بالإضافة إلى تخصيصها في ذلك العنوان، تُخزَّن في HeapSpray الخاص بنا.

هذه هي حالة الذاكرة بعد الاستدعاء الأول، كما قلنا، في العنوان القريب من 0x5000000

وفي HeapSpray من الذاكرة 0x10000، سيخزن المؤشر إلى AttributeValueSize كل 0x10 بايت، بالإضافة إلى المؤشر إلى 0x5000000.

قراءة رمز النظام (System Token):

هذا التسلسل سيؤدي إلى تشغيل الثغرة:

يتم استدعاء CreateLogFile() مرة أخرى على الملف المعدل وعلى ملف آخر باسم عشوائي.

ثم يتم استدعاء AddLogContainer() باستخدام مقابض (handles) تلك الملفات.

يتم استدعاء NtSetinformationFile()، ثم تُغلق المقابض مما يؤدي إلى إفساد المؤشر (سيتم شرح ذلك لاحقًا)

واجهة مستخدم رسومية، تطبيق، وصف مولّد تلقائيًا

يمنع HeapSpray حدوث شاشة زرقاء (BSOD) في هذه المرحلة:

عند تعيين نقطة توقف (breakpoint) هناك، يمكننا أن نرى أن المؤشر تالف ويشير إلى HeapSpray الخاص بنا، وبه يمكننا التحكم في استدعائي الدالتين التاليتين من جدول vtable.

واجهة مستخدم رسومية، نص، تطبيق، وصف مولّد تلقائيًا

نص، وصف مولّد تلقائيًا

يأخذ RAX القيمة 0x5000000 ويقفز أولاً إلى الدالة الموجودة عند 0x5000000+18، ثم إلى 0x5000000+8.

نص، وصف مولّد تلقائيًا بثقة متوسطة

صورة تحتوي على واجهة مستخدم رسومية، وصف مولّد تلقائيًا

إذن، القفزة الأولى إلى fnClfsEarlierLsn() ثم إلى fnSeSetAccessStateGenericMapping().

نتتبع التنفيذ من نقطة التوقف ونرى أنه يصل إلى CLFS!ClfsEarlierLsn().

نص، وصف مولّد تلقائيًا

تُستدعى هذه الدالة حصريًا لأنه عند عودتها، تقوم بتعيين EDX إلى 0xFFFFFFFF

واجهة مستخدم رسومية، وصف مولّد تلقائيًا بثقة متوسطة

في العنوان 0xFFFFFFFF كنا قد خزّنا نتيجة SYSTEM _EPROCESS & 0xFFFFFFFFFFFFFFF000

واجهة مستخدم رسومية، نص، تطبيق، وصف مولّد تلقائيًا

كما ذكرنا، عند العودة من CLFS!ClfsEarlierLsn()، تكون قيمة RDX هي 0x00000000FFFFFFFF

نص، وصف مولّد تلقائيًا

نصل إلى الدالة الثانية nt!SeSetAccessStateGenericMapping()

واجهة مستخدم رسومية، تطبيق، وصف مولّد تلقائيًا

هذه الدالة مفيدة، لأن RCX يشير إلى HeapSpray الخاص بنا، وقيمة RDX هي 0xFFFFFFFF، ونحن نتحكم في محتواها

واجهة مستخدم رسومية، تطبيق، Teams، وصف مولّد تلقائيًا

نص، وصف مولّد تلقائيًا

محتوى RCX+0x48 يحوي المؤشر إلى AttributeValueSize الذي تم تخزينه في v30.Pointer+24

واجهة مستخدم رسومية، نص، وصف مولّد تلقائيًا

واجهة مستخدم رسومية، نص، وصف مولّد تلقائيًا بثقة متوسطة

واجهة مستخدم رسومية، نص، وصف مولّد تلقائيًا

تُنقل قيمة المؤشر تلك إلى RAX، ثم يقرأ محتويات العنوان 0xFFFFFFFF حيث كنا قد خزّنا عنوان SYSTEM _EPROCESS & 0xFFFFFFFFFFFFFFF000.

واجهة مستخدم رسومية، نص، تطبيق، وصف مولّد تلقائيًا

ثم يستبدل في RAX+8 الحقل التالي وهو AttributeValue()

نص، وصف مولّد تلقائيًا

نص، وصف مولّد تلقائيًا

بالطبع، كان AttributeValue يشير عادةً في النواة إلى السمة التي أضفناها.

تقويم، وصف مولّد تلقائيًا

والآن سنستبدله بمؤشر إلى نتيجة system _EPROCESS & 0xFFFFFFFFFFFFFFF00.

وهذا يعني أنه عندما نستدعي الدالة NtFsControlFile() مرة أخرى، هذه المرة مع الوسيط 0x110038 لقراءة السمة، فبدلاً من إرجاع الأحرف "A” التي كان يشير إليها مؤشر AttributeValue، سيقرأ الآن من _EPRROCESS & 0xFFFFFFFFFFFFFFFFF000 عدد البايتات المطلوب ويعيدها في المخزن المؤقت للإخراج، ومن ثم يمكننا في الاستدعاء الأول الحصول على قيمة SYSTEM TOKEN.

نص، وصف مولّد تلقائيًا

v9b هو عنوان بداية مخزن الإخراج المؤقت حيث تم نسخ محتوى نتيجة System EPROCESS & 0xFFFFFFFFFFFFFFF000.

إلى ذلك، يضيف v14 وهي آخر 3 بايتات من EPROCESS الخاص بالنظام، ثم يضيف 0x4b8 وهي إزاحة Token لهذا الإصدار من Windows 11، ثم يجد محتويات ذلك العنوان التي ستكون قد خزّنت قيمة System Token.

واجهة مستخدم رسومية، نص، تطبيق، وصف مولّد تلقائيًا

نص، وصف مولّد تلقائيًا

التحقق من الرمز (Token)

نص، وصف مولّد تلقائيًا بثقة منخفضة

تذكر أن آخر 4 بتات تم تغييرها، وهذا غير مهم، لذا تظل القيمة متطابقة.

استبدال رمز العملية لدينا برمز النظام

في الاستدعاء الثاني تكون قيمة Flag هي 1 لأنه تمت زيادتها في نهاية الاستدعاء الأول.

واجهة مستخدم رسومية، نص، تطبيق، بريد إلكتروني، وصف مولّد تلقائيًا

هناك نرى الترتيب الذي تُخزَّن به القيم

واجهة مستخدم رسومية، نص، وصف مولّد تلقائيًا

يحتوي العنوان 0xFFFFFFFF على القيمة التي وجدناها للتو لـ System Process Token.

واجهة مستخدم رسومية، نص، وصف مولّد تلقائيًا

نص، وصف مولّد تلقائيًا

وفي HeapSpray توجد قيمة عنوان Token الخاص بالعملية مطروحًا منها 8. ستُستخدم هذه القيمة مضافًا إليها ثمانية كهدف، وتذكر أنك كتبت على العنوان المشار إليه بواسطة RAX+8.

واجهة مستخدم رسومية، نص، وصف مولّد تلقائيًا

نص، وصف مولّد تلقائيًا

في عنوان الذاكرة الذي يبدأ عند 0x5000000

واجهة مستخدم رسومية، وصف مولّد تلقائيًا

نرى أيضًا أنه يستخدم اسم حاوية أخرى، لأن الحاوية السابقة قيد الاستخدام من قبل عملية النظام ولا يمكن فتحها مرة أخرى أو حذفها.

ثم يتم تشغيل الثغرة للمرة الثانية بنفس الطريقة التي كانت عليها في المحاولة الأولى.

يصل مرة أخرى إلى CLFS!ClfsEarlierLsn().

واجهة مستخدم رسومية، نص، تطبيق، وصف مولّد تلقائيًا

ويقوم بتعيين RDX إلى 0xFFFFFFFF

لقطة شاشة لهاتف محمول، وصف مولّد تلقائيًا

ثم يصل إلى nt!SeSetAccessStateGenericMapping()

نص، وصف مولّد تلقائيًا

يقرأ عنوان Token الخاص بالعملية مطروحًا منه 8 حيث سيكتب

نص، وصف مولّد تلقائيًا

ثم يقرأ SYSTEM TOKEN

نص، وصف مولّد تلقائيًا

ويكتب في عنوان Token الخاص بالعملية (يضيف 8) قيمة System Token

وبهذه الطريقة، تمتلك العملية لدينا System Token

نص، وصف مولّد تلقائيًا

بمجرد كتابة الرمز، نبدأ عملية للتحقق من الصلاحيات، وفي هذه الحالة، نقوم بتشغيل Notepad.exe

تنفيذ العملية بصلاحيات النظام:

نص، وصف مولّد تلقائيًا

واجهة مستخدم رسومية، تطبيق، Word، وصف مولّد تلقائيًا

نص، وصف مولّد تلقائيًا بثقة منخفضة

تذكر أن إثبات المفهوم هذا يعمل فقط على Windows 11، أما على Windows 10 فسيؤدي إلى شاشة زرقاء (BSOD)، لذا يجب إجراء بعض التعديلات ليعمل بشكل صحيح، وهذا غير مشروح في هذه التدوينة.

هندسة عكسية للتصحيح (Reversing the Patch):

تحليل البنى

لقد أخذنا البنى ومعظم التوثيق حول تنسيق ملفات CLFS من العمل الممتاز لـ IONESCU حول CLFS Internals.

يمكننا أن نرى أنه تمت إضافة فحص في الدالة ClfsBaseFilePersisted::LoadContainerQ

نص، وصف مولّد تلقائيًا بثقة متوسطة

القيم التي تقوم بعملية الجمع تنتمي إلى بنية _CLFS_BASE_RECORD_HEADER.

خط زمني، وصف مولّد تلقائيًا بثقة متوسطة

لاحظ أن Base Block يبدأ عند الإزاحة 0x800 من الملف، وينتهي عند الإزاحة 0x71FF، حيث تقابل أول 0x70 بايت Log Block Header

كممارسة جيدة، يمكننا إضافة بنية _CLF_LOG_BLOCK_HEADER في IDA

struct _CLFS_LOG_BLOCK_HEADER

{

UCHAR MajorVersion;

UCHAR MinorVersion;

UCHAR Usn;

char ClientId;

USHORT TotalSectorCount;

USHORT ValidSectorCount;

ULONG Padding;

ULONG Checksum;

ULONG Flags;

CLFS_LSN CurrentLsn;

CLFS_LSN NextLsn;

ULONG RecordOffsets[16];

ULONG SignaturesOffset;

};

ثم لدينا رأس السجل الأساسي (_CLFS_BASE_RECORD_HEADER) الذي يبدأ عند الإزاحة 0x870 من بداية الملف ويبلغ طوله 0x1338 بايت.

واجهة مستخدم رسومية، نص، تطبيق، بريد إلكتروني وصف مولّد تلقائيًا

إذا كنت تريد استيرادها إلى IDA، يجب عليك أولاً إضافة الأنواع والبنى المفقودة التالية

typedef GUID CLFS_LOG_ID;
typedef UCHAR CLFS_LOG_STATE;

struct _CLFS_METADATA_RECORD_HEADER

{

ULONGLONG ullDumpCount;

};

الآن أصبح جاهزًا للإضافة:

typedef struct _CLFS_BASE_RECORD_HEADER

{

CLFS_METADATA_RECORD_HEADER hdrBaseRecord;

CLFS_LOG_ID cidLog;

ULONGLONG rgClientSymTbl[0x0b];

ULONGLONG rgContainerSymTbl[0x0b];

ULONGLONG rgSecuritySymTbl[0x0b];

ULONG cNextContainer;

CLFS_CLIENT_ID cNextClient;

ULONG cFreeContainers;

ULONG cActiveContainers;

ULONG cbFreeContainers;

ULONG cbBusyContainers;

ULONG rgClients[0x7c];

ULONG rgContainers[0x400];

ULONG cbSymbolZone;

ULONG cbSector;

USHORT bUnused;

CLFS_LOG_STATE eLogState;

UCHAR cUsn;

UCHAR cClients;

} CLFS_BASE_RECORD_HEADER, *PCLFS_BASE_RECORD_HEADER;

واجهة مستخدم رسومية، نص وصف مولّد تلقائيًا

بعد تضمين البنى، نلاحظ أنه يتم إجراء جمع بين cbSymbolZone والعنوان الذي ينتهي عنده _CLFS_BASE_RECORD_HEADER. (البداية + 1338h)نص، تطبيق وصف مولّد تلقائيًا

تذكر أن cbSymbolZone تم تعديله في ملف السجل المعدّ من 0x000000F8 إلى 0x0001114B.

(الإزاحة 0x1b98 من الملف)

0x800(offset of the start of the Base Block) + 0x70 (logBlockHeader) + 0x1328 (cbsymbolZone)

0x800+0x70+0x1328 = 0x1b98

ملف MyLog.blf مع cbsymbolZone المخصص:

جدول وصف مولّد تلقائيًا

صورة تحتوي على نص وصف مولّد تلقائيًا

بما أن التصحيح موجود في دالة CClfsBaseFilePersisted::LoadContainerQ، يجب أن نلقي نظرة على الكائن CClfsBaseFilePersisted.

اضبط نقطة توقف في CLFS!CClfsBaseFilePersisted::LoadContainerQ، وعند استدعاء CreateLogFile بمقبض الملف المعدّ ستتوقف.

واجهة مستخدم رسومية، نص، تطبيق وصف مولّد تلقائيًا

استدعِ الدالة CClfsBaseFile::GetBaseLogRecord للحصول على عنوان Base Log Record (_CLFS_BASE_RECORD_HEADER)

واجهة مستخدم رسومية، تطبيق، خط زمني وصف مولّد تلقائيًا

سيشير RAX إلى عنوان _CLFS_BASE_RECORD_HEADER

واجهة مستخدم رسومية، نص، تطبيق، بريد إلكتروني وصف مولّد تلقائيًا

لاحظ بنية _CLFS_BASE_RECORD_HEADER في الذاكرة وحقل cbsymbolZone الذي يبعد 0x1328 بايت إلى الأمام

صورة تحتوي على نص وصف مولّد تلقائيًا

نص وصف مولّد تلقائيًا

يخزّن r14 البنية المقابلة لـ “this”، وهي CClfsBaseFilePersisted لأنها this الخاصة بالدالة CClfsBaseFilePersisted::LoadContainerQ.

واجهة مستخدم رسومية، تطبيق وصف مولّد تلقائيًا

بنية CClfsBaseFilePersisted في الذاكرة:

نص وصف مولّد تلقائيًا

إذن، لننشئ بنية بطول 0x21c0 لإكمال حقولها أثناء تفكيكها (إنها بنية غير موثقة)، وسنسميها struct_CClfsBaseFilePersisted

جدول وصف مولّد تلقائيًا بثقة متوسطة

داخل الدالة CClfsBaseFile::GetBaseLogRecord() يتم الحصول على المؤشر إلى _CLFS_BASE_RECORD_HEADER. ونعلم أن "this" في تلك الدالة هو البنية: struct_CClfsBaseFilePersisted.

خط زمني وصف مولّد تلقائيًا بثقة متوسطة

اقرأ حقلين (الإزاحة 0x28 و 0x30)

واجهة مستخدم رسومية، تطبيق، جدول وصف مولّد تلقائيًا

الحقل 0x28 هو كلمة (word) وقيمته 6، لذا نغيّر النوع إلى word في البنية.

نص وصف مولّد تلقائيًا

نص وصف مولّد تلقائيًا بثقة متوسطة

واجهة مستخدم رسومية، نص، تطبيق وصف مولّد تلقائيًا

في الوقت الحالي، نعيد تسميته إلى الثابت 6 (const_6)

نص وصف مولّد تلقائيًا

واجهة مستخدم رسومية، نص، تطبيق وصف مولّد تلقائيًا

وفقًا للتوثيق، الرقم 6 قد يكون عدد الكتل CLFS_METADATA_BLOCK_COUNT. قد يشير هذا الحقل إلى هذه القيمة.

وهذا المؤشر يقع عند الإزاحة 0x30.

جدول وصف مولّد تلقائيًا

لاحظ أن الحجم الموضّح هناك يشمل الترويسة بطول 0x10

نص وصف مولّد تلقائيًا

شكل وصف مولّد تلقائيًا بثقة متوسطة

عند استدعاء الدالة ExAllocatePoolWithTag، يتم طلب عدد قليل من البايتات، لكن الترويسة غير مضمنة، لذلك سيتم طلب 0x90 بايت (0xa0 - 0x10) في الاستدعاء.

عند البحث عن النص +30h]، وهي التعليمات التي تكتب عند الإزاحة 0x30، وجدنا قائمة طويلة، لكن تصفية القائمة حسب نوع الكائن CClfsBaseFilePersisted تُبقي لنا نتائج قليلة، ونجد فورًا مكان تخصيص هذا الحجم، وبنفس الوسم. (نصيحة: أسماء الدوال Create و Initialize هي دائمًا أول ما يجب النظر إليه).

نص وصف مولّد تلقائيًا بثقة منخفضة

واجهة مستخدم رسومية، نص، تطبيق وصف مولّد تلقائيًا

بما أننا لا نعرف الاسم بعد، سنسميه pool_0x90، وهو بنية أخرى غير موثقة، وسننشئ بنية بهذا الحجم.

نص وصف مولّد تلقائيًا

واجهة مستخدم رسومية، جدول وصف مولّد تلقائيًا

البنية pool_0x90 في الذاكرة تحتوي على مؤشر آخر عند الإزاحة الخاصة بها 0x30.

تطبيق، جدول وصف مولّد تلقائيًا بثقة متوسطة

يشير هذا المؤشر الآخر إلى الكتلة الأساسية في الملف (تبدأ الكتلة الأساسية عند الإزاحة 0x800)

شكل وصف مولّد تلقائيًا

صورة تحتوي على تقويم وصف مولّد تلقائيًا

صورة مأخوذة من منشور مدونة Zscaler:

واجهة مستخدم رسومية، تطبيق، بريد إلكتروني وصف مولّد تلقائيًا

التخصيص ضخم لأنه يحتوي على الكتلة الأساسية بأكملها.

نص وصف مولّد تلقائيًا

واجهة مستخدم رسومية، نص، تطبيق وصف مولّد تلقائيًا

إذن، سننشئ بنية جديدة بحجم 0x7a00 ونسميها BASE_BLOCK

واجهة مستخدم رسومية، نص، تطبيق وصف مولّد تلقائيًا

البايتات السبعون الأولى، وكما نعلم بالفعل، تتوافق مع _CLFS_LOG_BLOCK_HEADER، والـ 0x1338 التالية مع _CLFS_BASE_RECORD_HEADER.

نص وصف مولّد تلقائيًا

إذن، بجمع بداية الكتلة الأساسية مع الإزاحة إلى السجل التالي (وهي 0x70)، نحصل على _CLFS_BASE_RECORD_HEADER

تطبيق وصف مولّد تلقائيًا بثقة منخفضة

البنية _CLFS_BASE_RECORD_HEADER في الذاكرة.

تقويم وصف مولّد تلقائيًا

بالنظر إلى دوال أخرى لنفس الكائن CClfsBaseFilePersisted، في الدالة CClfsBaseFilePersisted::AddContainer تحصل أيضًا عبر CClfsBaseFile::GetBaseLogRecord على عنوان _CLFS_BASE_RECORD_HEADER.

نص وصف مولّد تلقائيًا

بعد ذلك، استدعِ CClfsBaseFile::OffsetToAddr باستخدام cbOffset، إذ يحصل على عنوان _CLFS_CONTAINER_CONTEXT، ويخزّن cboffset في مصفوفة rgbcontainers التي تقع عند الإزاحة 0x328 من _CLFS_BASE_RECORD_HEADER.

واجهة مستخدم رسومية، تطبيق وصف مولّد تلقائيًا

تُستخدم الدالة CClfsBaseFile::OffsetToAddr للعثور على عناوين البنى من الإزاحة

واجهة مستخدم رسومية، نص، تطبيق وصف مولّد تلقائيًا

عند هذه النقطة، لا تزال إزاحة الحاوية التي سيتم تخزينها عند 0x328 تساوي 0، لأننا لم نضف حاوية بعد.

نمط خلفية وصف مولّد تلقائيًا بثقة منخفضة

يستدعي الـ PoC الدالة CreateLogFile مرتين، المرة الأولى مع الملف التالف MyLog.blf والمرة الثانية مع الملف العادي MyLogxxx.blf، لذا يجب أن نوقف التصحيح مرتين في جميع الأماكن المذكورة أعلاه وندوّن في المفكرة عناوين البنى المذكورة أعلاه لكلا الملفين.

نص وصف مولّد تلقائيًا

لنتقدم سريعًا قليلًا إلى CLFS!CClfsLogFcbPhysical::AllocContainer عن طريق تعيين نقطة توقف عليه وتشغيله هناك.

عند الوصول إلى AddLogContainer() في الـ PoC، نتوقف عند نقطة التوقف.

صورة تحتوي على تطبيق وصف مولّد تلقائيًا

لنقم أيضًا بتعيين نقطة توقف على CClfsBaseFilePersisted::AddContainer+176 حيث رأينا سابقًا أنها ستجد الإزاحة والمؤشر إلى بنية _CLFS_CONTAINER_CONTEXT.

لقطة شاشة لجهاز كمبيوتر وصف مولّد تلقائيًا بثقة متوسطة

واجهة مستخدم رسومية، نص، تطبيق وصف مولّد تلقائيًا

عند توقف المصحح، يمكننا رؤية أن الإزاحة هي 0x1468.

نص وصف مولّد تلقائيًا

سيُرجع RAX عنوان بنية _CLFS_CONTAINER_CONTEXT.

واجهة مستخدم رسومية، نص، تطبيق، جدول وصف مولّد تلقائيًا

البنية ما تزال فارغة لأنه لم تتم إضافة الحاوية بعد.

نص وصف مولّد تلقائيًا

لاحظ أن قيمة SignatureOffset=0x50 التي كتبناها عند الإزاحة 0x868 في الملف التالف، عند طرح 0x800 من بداية الكتلة الأساسية، ستكون في بنية _CLFS_LOG_BLOCK_HEADER عند الإزاحة 0x68.

واجهة مستخدم رسومية، نص، تطبيق وصف مولّد تلقائيًا

نص، حرف وصف مولّد تلقائيًا

عندما يستدعي الـ PoC الدالة AddLogContainer() باستخدام الملف التالف، تكون القيمة عند الإزاحة 0x68 من _CLFS_LOG_BLOCK_HEADER, بدلًا من القيمة 0x50 التي كتبناها هناك، هي حاليًا 0xFFFF0050 في الذاكرة.

صورة تحتوي على نص وصف مولّد تلقائيًا

في وقت ما، تم تغيير تلك القيمة بواسطة البرنامج؛ ولمعرفة متى حدث ذلك، في التنفيذ التالي، سنضع نقطة توقف ذاكرة عند الكتابة.

يتم تخزين الإزاحة عند r15 + 0x328 (r15 يشير إلى بنية _CLFS_BASE_RECORD_HEADER)

نص وصف مولّد تلقائيًا بثقة متوسطة

واجهة مستخدم رسومية وصف مولّد تلقائيًا بثقة منخفضة

يخزّن RBX الإزاحة 0x1468.

صورة تحتوي على تقويم وصف مولّد تلقائيًا

إذن، في عنوان الكتلة الأساسية + 0x70 + الإزاحة 0x1468 التي اكتشفناها، سيكون عنوان حاوية CLFS_CONTAINER_CONTEXT.

نص وصف مولّد تلقائيًا

في بنية CLFS_CONTAINER_CONTEXT، عند الإزاحة 0x18 سيكون مؤشر pContainer الذي سيُخزَّن هناك، ويمكننا تعيين نقطة توقف عند الكتابة لمعرفة متى يُكتب.

نص وصف مولّد تلقائيًا

صورة تحتوي على نص وصف مولّد تلقائيًا

هذا هو المؤشر الذي يجب علينا إفساده؛ لأنه في الدالة التي توجد بها الثغرة، تقوم أولاً بقراءة CLFS_CONTAINER_CONTEXT، ثم تنقله إلى r15، ثم تقرأ قيمة r15+18، وهو هذا المؤشر الذي وضعنا عليه نقطة توقف الكتابة للتو.

واجهة مستخدم رسومية، تطبيق، جدول وصف مولّد تلقائيًا

واجهة مستخدم رسومية، تطبيق، Word وصف مولّد تلقائيًا

يخزّن pContainer عند الإزاحة 0x1c0 من بنية struct_CClfsBaseFilePersisted.

نص، تطبيق، سبورة وصف مولّد تلقائيًا

بعد عدة توقفات، نصل إلى اللحظة التي يحدث فيها الإفساد. الجزء العلوي من عنوان المؤشر تغيّر من FFs إلى صفر.

تقويم وصف مولّد تلقائيًا

يحدث هذا عند استدعاء AddLogContainer() الثاني للملف التالف، فيتم إفساد مؤشر MyLogxxx السابق.

تكمن المشكلة في أن SignaturesOffset، التي يجب أن تكون 0x50، أصبحت الآن 0xFFFF0050، مما يسمح بالكتابة خارج الحدود في memset التالية.

واجهة مستخدم رسومية، نص، تطبيق وصف مولّد تلقائيًا

واجهة مستخدم رسومية، نص وصف مولّد تلقائيًا

إفساد مؤشر “pContainer”:

ستقوم دالة memset() بإفساد بنية _CLFS_CONTAINER_CONTEXT الموجودة بالأسفل، وهذه البنية تتوافق مع ملف MyLogxxx، حيث أنها عند الإنشاء وضعتها على بُعد 0x11000 بايت من بعضها البعض.

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

بنية الكتلة الأساسية للملف التالف تسبق بنية ملف MyLogxxx بمقدار 0x11000 تمامًا.

الملف التالف:

شكل وصف مولّد تلقائيًا

MyLogxxx

صورة تحتوي على نص وصف مولّد تلقائيًا

RCX أصغر من RDX لأنه تمت إضافة 0xFFFF0050 إليه، بدلًا من 0x50 كما ينبغي.

واجهة مستخدم رسومية، نص، تطبيق وصف مولّد تلقائيًا

ووصلنا إلى دالة memset()، لضبط مقدار 0xb0 بايت على أصفار، مع توجيه RCX إلى بنية CLFS_CONTAINER_CONTEXT الخاصة بملف MyLogxxx، وتحديدًا إلى البايتات الخمسة العليا من pContainer.

نص وصف مولّد تلقائيًا

سيتم إفساد هذا المؤشر عن طريق الكتابة فوق البايتات الأولى:

نص وصف مولّد تلقائيًا

فيبقى يشير إلى عنوان ذاكرة تحكمنا به مسبقًا عبر HeapSpray

نص وصف مولّد تلقائيًا

صورة تحتوي على نص وصف مولّد تلقائيًا

بعد ذلك، سيتم إغلاق مقبض ملف MyLogxxx، ويتم الوصول إلى CClfsBaseFilePersisted::RemoveContainer، وعندها تُستغل الثغرة أخيرًا.

نص، تطبيق وصف مولّد تلقائيًا بثقة متوسطة

مراجعة التصحيح

الآن بعد أن أصبح لدينا مزيد من المعلومات، نلاحظ أنه هنا يقرأ Base_Block.LOG_BLOCK_HEADER.SignaturesOffset ويقرأ the Base_Block. .LOG_BLOCK_HEADER.TotalSectorCount

في الجزء الأول من التصحيح، يجب ألا تكون SignaturesOffset أكبر من 0x7a00؛ في ملفنا كانت قيمتها في الأصل 0x50، وإذا وصلت بقيمة أكبر من 0x7a00 فسيُخرجنا من الدالة.

صورة تحتوي على مخطط وصف مولّد تلقائيًا

عند تشغيل الـ PoC على الجهاز المحدّث، يقارن 0x50 مع 0x7a00، وبما أنها أصغر فإنه يستمر.

نص وصف مولّد تلقائيًا

في الكتلة التالية، تتم إضافة cbSymbolZone التالف إلى قيمة العنوان النهائي لـ _CLFS_BASE_RECORD_HEADER، ويُخزَّن هذا المجموع في result_1.

نص وصف مولّد تلقائيًا

ثم يُضاف عنوان Base_Block إلى قيمة SignatureOffset، والتي تكون في الملف العادي 0x7980.

نص وصف مولّد تلقائيًا

العنوان الأقصى لـ base_block هو 0x7a00، والآن يُسمح لـ SymbolZone حتى 0x80 قبل الحد.

سيخزّنها في result_2، أي أن هذا سيكون الحد الأقصى لـ SymbolZone داخل الكتلة الأساسية، ثم يقارن النتيجتين؛ إذا كانت الأولى أكبر من الثانية، فهذا يعني أنها تجاوزت الحدود.

نص وصف مولّد تلقائيًا

نص وصف مولّد تلقائيًا

من الواضح أن العضو الأول سيكون أكبر من الثاني ولن يستمر، لأن المجموع الأول لـ cbSymbolZone + العنوان النهائي لـ _CLFS_BASE_RECORD_HEADER يتجاوز الحد (وهو result_2)، مما يؤدي إلى “خروج عن الحدود”.

واجهة مستخدم رسومية، نص، تطبيق وصف مولّد تلقائيًا

إفساد SignatureOffset

آخر ما علينا اكتشافه هو أين تتحول قيمة SignatureOffset من 0x50 إلى 0xFFFF0050.إذن، لنبدأ من جديد، وأعد التشغيل وتوقف عند CLFS!CClfsBaseFilePersisted::LoadContainerQ حيث لم تتغير القيمة بعد في الذاكرة ولا تزال 0x50.

عيّن نقطة توقف وصول (access breakpoint) عند الإزاحة 0x68 في SignatureOffset.

Calendar Description automatically generated

وبعد عدة توقفات، نكتشف اللحظة المناسبة التي يعدّل فيها القيمة، داخل ClfsEncodeBlockPrivate.

Graphical user interface, text Description automatically generated

هذه الدالة غير مُرقّعة (patched)، لذا قد يكون هذا سلوكًا ناتجًا عن انخفاض قيمة 0x50 وبقية القيم التي تم التلاعب بها.

من بين القيم المصنوعة (crafted)، يمكننا رؤية قيمة ccoffsetArray التي اسمها في بنية _CLFS_BASE_RECORD_HEADER هو rgClients وتمثل مصفوفة الإزاحات التي تشير إلى كائن Client Context.

حقل rgClients يقع عند الإزاحة 0x138 (0x9a8-0x800-0x70) من بنية _CLFS_BASE_RECORD_HEADER.

Graphical user interface, table Description automatically generated

Text Description automatically generated with medium confidence

في الـ PoC، تكون هذه القيمة مشوّهة لتشير إلى كائن سياق عميل مزيف، يُسمى FakeClientContext

Text, whiteboard Description automatically generated

A screenshot of a computer Description automatically generated with medium confidence

هذه هي بنية Client Context _CLFS_CLIENT_CONTEXT

struct _CLFS_CLIENT_CONTEXT

{

CLFS_NODE_ID cidNode;

CLFS_CLIENT_ID cidClient;

USHORT fAttributes;

ULONG cbFlushThreshold;

ULONG cShadowSectors;

ULONGLONG cbUndoCommitment;

LARGE_INTEGER llCreateTime;

LARGE_INTEGER llAccessTime;

LARGE_INTEGER llWriteTime;

CLFS_LSN lsnOwnerPage;

CLFS_LSN lsnArchiveTail;

CLFS_LSN lsnBase;

CLFS_LSN lsnLast;

CLFS_LSN lsnRestart;

CLFS_LSN lsnPhysicalBase;

CLFS_LSN lsnUnused1;

CLFS_LSN lsnUnused2;

CLFS_LOG_STATE eState;

union

{

HANDLE hSecurityContext;

ULONGLONG ullAlignment;

};

};

قيمة eState موجودة عند الإزاحة 0x78 من بداية البنية، وفي الملف المشوّه عند 0x23a0+0x78.

Text Description automatically generated with medium confidence

Chart, scatter chart Description automatically generated

توضح هذه القيمة حالة السجل (log).

typedef UCHAR CLFS_LOG_STATE, *PCLFS_LOG_STATE;
const CLFS_LOG_STATE CLFS_LOG_UNINITIALIZED = 0x01;
const CLFS_LOG_STATE CLFS_LOG_INITIALIZED = 0x02;
const CLFS_LOG_STATE CLFS_LOG_ACTIVE = 0x04;
const CLFS_LOG_STATE CLFS_LOG_PENDING_DELETE = 0x08;
const CLFS_LOG_STATE CLFS_LOG_PENDING_ARCHIVE = 0x10;
const CLFS_LOG_STATE CLFS_LOG_SHUTDOWN = 0x20;
const CLFS_LOG_STATE CLFS_LOG_MULTIPLEXED = 0x40;
const CLFS_LOG_STATE CLFS_LOG_SECURE = 0x80;

هذه القيمة مضبوطة على CLFS_LOG_STATE CLFS_LOG_SHUTDOWN =0x20

القيمة المشوّهة الأخرى هي fAttributes والتي تقابل مجموعة علامات FILE_ATTRIBUTE المرتبطة بملف السجل الأساسي (مثل System و Hidden).

Graphical user interface Description automatically generated with low confidence

Text, letter Description automatically generated

بما أن الحقل يبدأ قبل ذلك ببايت واحد عند 0xa ويمتد على بايتين، فإن قيمة fAttributes هي 0x100.

A picture containing table Description automatically generated

Graphical user interface, text, application Description automatically generated

أخيرًا، هناك قيمة blocknameoffset التي تشير إلى الإزاحة 0x1bb8، أي بإضافة 0x78 و 0x800 تشير إلى الإزاحة 0x2428 من الملف.

Text Description automatically generated

Text, letter Description automatically generated

لاحظ أن الإزاحة إلى Client Context هي 0x1b30

Table Description automatically generated

إذن، Client Context موجود عند الإزاحة 0x23a0.

Text Description automatically generated with medium confidence

Table Description automatically generated

وقبلها مباشرة بـ 0x10، توجد القيمة المقابلة لـ blocknameoffset.

Text, letter Description automatically generated

Table Description automatically generated with low confidence

والتي تشير إلى السلسلة النصية التي تحمل الاسم.

آخر قيمة هي blockattributeoffset التي تقع قبل Client Context بمقدار 0xC عند 0x2394.

Table Description automatically generated

هاتان القيمتان الأخيرتان تنتميان إلى بنية تسبق Client Context بطول 0x30 بايت، تُسمى _CLFSHASHSYM

typedef struct _CLFSHASHSYM
{
CLFS_NODE_ID cidNode;
ULONG ulHash;
ULONG cbHash;
ULONGLONG ulBelow;
ULONGLONG ulAbove;
LONG cbSymName;
LONG cbOffset;
BOOLEAN fDeleted;
} CLFSHASHSYM, *PCLFSHASHSYM;

A picture containing diagram Description automatically generated

Text Description automatically generated

تقعان عند 0x20 و 0x24 بايت من بداية بنية _CLFSHASHSYM، إذن في بنية _CLFSHASHSYM، القيمة المسماة blockNameOffset في الـ PoC هي الحقل cbSymName، و blockAttributteoffset هو الحقل cbOffset.

A picture containing text Description automatically generated

Text Description automatically generated with medium confidence

تلك هي القيم المشوّهة، الآن نحتاج إلى معرفة كيف تؤثر على تغيير SignaturesOffset من القيمة 0x50 إلى 0xFFFF0050.

لنلقِ نظرة على الدالة CClfsBaseFile::AcquireClientContext()، التي يجب أن تعيد سياق العميل (client context).

Graphical user interface, text, application, email Description automatically generated

إنها تستدعي CClfsBaseFile::GetSymbol مع الوسيطة الرابعة التي ستكون من النوع _CLFS_CLIENT_CONTEXT ** حيث سيُخزَّن المؤشر إلى Client Context.

Graphical user interface, text, application Description automatically generated

داخل الدالة CClfsBaseFile::GetSymbol نمرر إزاحة ccoffsetArray المشوّهة إلى CClfsBaseFile::OffsetToAddr ونحصل على عنوان سياق العميل، لنضع نقطة توقف هناك بحيث تتوقف عند استدعاء الملف الذي أُنشئ باستخدام CreatelogFile.

Graphical user interface, application Description automatically generated with medium confidence

هناك تتوقف عند الوسيطة المصنوعة ccoffsetArray.

Graphical user interface, application Description automatically generated

Table Description automatically generated

الدالة CClfsBaseFile::OffsetToAddr تعيد سياق العميل المزيف.

A picture containing graphical user interface Description automatically generated

وتتحقق من أن قيمة cbOffset ليست صفرًا، حيث يوجد 0xC قبل بنية _CLFS_CLIENT_CONTEXT الموجودة في RAX.

A screenshot of a computer Description automatically generated

A picture containing text Description automatically generated

ثم تقارن cbOffset مع ccoffsetArray (الموجودة في RSI)، يجب أن تكونا متساويتين، وإلا سنحصل على خطأ.

A screenshot of a computer Description automatically generated

وتتحقق أيضًا من أن cbSymName تساوي cbOffset+0x88، وإلا سنحصل على خطأ أيضًا.

Graphical user interface, text, application Description automatically generated with medium confidence

وأخيرًا، تقارن بايت cidClient مع الصفر.

A picture containing diagram Description automatically generated

إذا نجحت كل هذه الفحوصات، سيتم حفظ client context.

A screenshot of a computer Description automatically generated

مخرج الدالة r14 يشير إلى Client Context

Graphical user interface, text, application Description automatically generated

عند الخروج من CClfsLogFcbPhysical::Initialize سيكون لدينا عنوان CLFS_CLIENT_CONTEXT.

Text Description automatically generated

الآن تقرأ قيمة fAttributes (0x100)

Graphical user interface, text, application Description automatically generated

هذه الدالة تنتمي إلى الفئة CClfsLogFcbPhysical

Text Description automatically generated

Graphical user interface, text, application Description automatically generated

والتي تم تخصيصها هنا، وحجمها 0x15d0 ووسمها “ClfC”

Text Description automatically generated with medium confidence

لننشئ بنية لتخزين ما نقوم بفهمه عكسيًا (reversing)، وسنسميها: struct_CClfsLogFcbPhysical.

A picture containing table Description automatically generated

لاحظ أنه عند 0x2b0 يحفظ عنوان بنية CClfsBaseFilePersisted.

A picture containing application Description automatically generated

بعد حفظ العديد من القيم في البنية، ينتقل إلى جزء مهم، حيث يختبر eState مع 0x20.

Graphical user interface, text, application Description automatically generated

Graphical user interface, text, application, table Description automatically generated

بما أن القيمة المشوّهة كانت 0x20، فإن الاختبار سيعيد 1.

Table Description automatically generated

Graphical user interface, text, application Description automatically generated

نرى أنه في المُنشئ (constructor) داخل vtable يوجد:

Text Description automatically generated

سيتحقق مما إذا كان الملف multiplexed.

Graphical user interface, application Description automatically generated

لذا، يسير عبر المسار المطلوب، ليصل إلى CClfsLogFcbPhysical::ResetLog.

Graphical user interface, application Description automatically generated

Text, application, table Description automatically generated with medium confidence

يتم تهيئة عدة حقول إلى الصفر باستثناء حقل واحد يُهيأ إلى 0xFFFFFFFF00000000.

Graphical user interface Description automatically generated with low confidence

هنا يسترجع Client Context

Graphical user interface, application Description automatically generated with medium confidence

يخزن القيمة 0xFFFFFFFF00000000.

Graphical user interface, application Description automatically generated

Graphical user interface, application Description automatically generated

A picture containing calendar Description automatically generated

يكتب 0xFFFFFFFF عند الإزاحة 0x5c وهي الجزء العلوي من CLFS_LSN lsnRestart.ullOffset

Text Description automatically generated with medium confidence

Graphical user interface, text, letter Description automatically generated

الآن ننفذ الدالة ClfsEncodeBlockPrivate()، وهي المسؤولة عن الكتابة فوق 0x50 بالقيمة 0xFFFF0050 كما رأينا سابقًا.

هناك يقرأ قيمة SignatureOffset = 0x50 التي لا تزال كما وضعناها في الملف المشوّه ويضيفها إلى بداية CLFS_LOG_BLOCK_HEADER.

Text Description automatically generated

هذه حلقة تكتب بايتين، ونظرًا لأن SignatureOffset بدلًا من أن يشير إلى قيمة صحيحة، وهي في الملف العادي قيمة مرتفعة مثل 0x3f8 تجعله يكتب في موضع أبعد، فإنه هنا سيكتب داخل نفس CLFS_LOG_BLOCK_HEADER

الفكرة هي تغيير وجهة الكتابة لمحاولة إفساد قيمة SignatureOffset.

الملف العادي

Table Description automatically generated

عند هذه النقطة، سيبدأ في التكرار وكتابة بايتين.

Graphical user interface, application Description automatically generated

يجب أن يصل العداد إلى القيمة 0x3d للخروج من الحلقة.

Graphical user interface, application Description automatically generated

RCX يتزايد من 0x200، نحن الآن في الدورة الثالثة، وقيمته 0x600

Graphical user interface, text, application Description automatically generated

في التكرار 0xe، تكون RCX هي 0x1a00

Graphical user interface, text Description automatically generated

A picture containing calendar Description automatically generated

هذا هو المكان الذي كتب فيه 0xFFFFFFFF000000.

Graphical user interface, text, application Description automatically generated

Table Description automatically generated with medium confidence

إنه يقرأ آخر بايتين FFFF

Text Description automatically generated

ثم سينسخهما إلى R8

A picture containing calendar Description automatically generated

A picture containing calendar Description automatically generated

كما رأينا، هذه القيمة حرجة لأنها تسمح بتجاوز الفحص والكتابة خارج الحدود لإفساد مؤشر pContainer للملف الذي يلي memset() وكتابة أصفار في الأعلى وتركه يشير إلى الذاكرة التي نتحكم بها (HeapSpray).

في CClfsBaseFilePersisted::AllocSymbol، نفس المجموع الذي سيحصل على وجهة الـ memset وهي cbSymbolZone + العنوان النهائي لـ CLFS_BASE_RECORD_HEADER يقارنه مسبقًا مع Base_block + 0xFFFF0050، إذن لدينا قيم مشوّهة على جانبي المعادلة.

CbSymbolZone= 0x1114B

إنها القيمة المشوّهة التي عند إضافتها إلى العنوان النهائي لـ CLFS_BASE_RECORD_HEADER ستجعلها تكتب خارج الحدود، والعضو الآخر في المقارنة الذي يفترض أن يكون عنوان Base Block + SignatureOffset يظل SignatureOffset =0xFFFF0050 مما يسمح باجتياز هذا الفحص والكتابة خارج الحدود في memset() وتصفير الجزء العلوي من المؤشر الذي سيظل يشير إلى HeapSpray الخاص بنا.

Graphical user interface, application, table Description automatically generated

بما أن RCX أصغر من RDX.

Graphical user interface, application Description automatically generated

كما رأينا سابقًا. (قد تختلف القيم لأنها تعود إلى تنفيذ سابق)

سيُفسد المؤشر، مع ضبط البايتات الأعلى على 0

Table Description automatically generated

تاركًا إياه يشير إلى منطقة ذاكرة نتحكم فيها عبر HeapSpray

A screenshot of a computer Description automatically generated with low confidence

Table Description automatically generated

إذن، عند تشغيل الثغرة، نصل إلى CClfsBaseFilePersisted::RemoveContainer

A picture containing graphical user interface Description automatically generated

سيكون هناك المؤشر الفاسد بالفعل ويمكن استغلاله كما رأينا سابقًا.

Graphical user interface, application Description automatically generated

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

نأمل أن تجدوه مفيدًا، إذا كان لديكم أي استفسار يمكنكم التواصل معنا على [email protected] و [email protected]

استمتعوا!

تنزيل الأداة