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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2023-28252 | Kitploit
أدوات/GitHubGitHub/fortra/cve-2023-28252
تصعيد الامتيازاتتحليل الذاكرة الجنائيتحليل الثغرات الأمنيةالاستغلالالهندسة العكسيةمصممي الأخطاءاستغلال الملفات الثنائية
GitHubfortra/cve-2023-28252

CVE-2023-28252

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

الأكثر شعبية

عرض الكل →

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

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

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

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

منذ فبراير 2022 تم الإبلاغ عن برنامج فدية جديد يبدو أنه يستخدم ثغرة يوم-صفر في ويندوز، وفقًا للبحث الذي أجرته شركة تريند مايكرو.
مزيد من المعلومات حول برنامج الفدية هذا متاح على هذا الرابط.
وفقًا لتحليل كاسبرسكي، استخدمت مجموعة فدية Nokoyawa استغلالات أخرى تستهدف برنامج تشغيل نظام ملفات السجل المشترك (CLFS) منذ يونيو 2022، ذات خصائص مشابهة لكنها متميزة، وكلها مرتبطة بمطور استغلال واحد.
في أبريل 2023 عندما أصدرت مايكروسوفت التصحيح، تم تخصيص CVE-2023-28252.
سابقًا، في عام 2022، قمنا بالبحث عن خطأ مشابه في المكوّن نفسه، وتم توثيقه في هذه التدوينة

تنسيق ملف نظام ملفات السجل المشترك (CLFS):

لمواجهة التحليل، من الضروري معرفة تنسيق ملف .blf، الذي يعالجه برنامج تشغيل نظام ملفات السجل المشترك المعرّض للثغرة المسمى CLFS.sys والموجود في مجلد برامج التشغيل داخل system32.

مزيد من المعلومات حول هذا النوع من الملفات موجود في الروابط أدناه:

https://www.zscaler.com/blogs/security-research/technical-analysis-windows-clfs-zero-day-vulnerability-cve-2022-37969-part

https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/introduction-to-the-common-log-file-system

https://github.com/ionescu007/clfs-docs/blob/main/README.md

https://www.coresecurity.com/core-labs/articles/understanding-cve-2022-37969-windows-clfs-lpe

الثغرة:

تم إجراء هذا التحليل على Windows 11 21H2 بإصدار clfs.sys 10.0.22000.1574، على الرغم من أنه يعمل أيضًا على Windows 10 21H2 وWindows 10 22H2 وWindows 11 22H2 وWindows server 2022.

في إصدارات ويندوز السابقة، من الضروري تعديل بعض القيم، وإلا فسوف نسبب شاشة زرقاء (BSOD).

تحديثات الثلاثاء من مايكروسوفت أبريل 2023.

لقطة شاشة لجهاز كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة متوسطةيمكنك التحقق من إصدار برنامج التشغيل كما هو موضح

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

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

من الواضح أن المعلومات المقدمة لم تكن كاملة، ولكن بدون هذه المساعدة لم يكن من المرجح أن نتمكن من بناء الإثبات (PoC) ثم استغلالًا وظيفيًا.

لتسهيل الفهم، سنشرح أولًا كيفية بناء الإثبات (PoC) ثم سنقوم بتحليل الثغرة.

تحتوي هذه التدوينة على قسمين:

بناء الإثبات (PoC):

1-الحصول على عناوين النواة التي نحتاجها للاستغلال

2-تحضير المسار لإنشاء ملفات .blf:

3-إنشاء ملف "trigger blf" باستخدام دالة CreateLogFile()

4-صياغة ملف “trigger blf”

5-الحصول على عنوان النواة للكتلة الأساسية BASE BLOCK لملف trigger blf

6-استدعاء AddLogContainer بمقبض ملف trigger blf

7-تحضير ملفات spray blf

8-تحضير الذاكرة لتنفيذ الرش

9-استثارة الخطأ

التصحيح:

1-التحقق من رش الذاكرة

2-النظر إلى RecordOffset[12] في ملف trigger blf

3-النظر إلى قيمة iFlushBlock في ملف spray blf

4-لماذا يقرأ من BLOCK 1 SHADOW بدلاً من BLOCK 0 CONTROL؟

5-لماذا يكون المجموع الاختباري مساويًا صفرًا في ملفات blf spray؟

6-إنهاء الاستغلال.

7-التصحيح الحقيقي

بناء الإثبات (PoC):

1-الحصول على عناوين النواة التي نحتاجها للاستغلال

سأنشئ دالة باسم InitEnvironment للحصول على بعض عناوين النواة الضرورية.

احصل على عنوان EPROCESS الخاص بعملي وأخزنه في المتغير g_EProcessAddress، ثم عنوان EPROCESS لعملية SYSTEM، وأخزنه في system_EPROCESS، ثم عنوان EHTREAD للـخيط الرئيسي في عملي، وأخزنه في g_EThreadAddress، وأخيرًا عنوان PREVIOUS MODE الذي لن يُستخدم في هذا الإصدار من الإثبات (PoC).

لقطة شاشة لكود برنامج كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة منخفضة

هذه الطريقة معروفة جيدًا، فدالة GetObjectKernelAddress تستدعي NtQuerySystemInformation مرتين مع الوسيط الأول SystemExtendedHandleInformation، وتُمرَّر المكالمة الأولى بحجم غير صحيح وتُرجع خطأً، ولكنها تُرجع أيضًا الحجم الصحيح الذي يُستخدم في المكالمة الثانية وتحصل على معلومات جميع المقابض، ثم تمر في حلقة على معلومات كل مقبض وفي الحقل Object من بنية handleinfo الصحيحة تحصل على العنوان المبحوث عنه في النواة.

صورة تحتوي على نص ولقطة شاشة وخط وسطر، وصف تم إنشاؤه تلقائيًا

أحتاج أيضًا إلى عناوين النواة للدوال التالية المُصدَّرة من CLFS.sys:

• ClfsEarlierLsn

• ClfsMgmtDeregisterManagedClient

والدوال المُصدَّرة من NTOSKRNL.exe

• RtlClearBit/PoFxProcessorNotification

• SeSetAccessStateGenericMapping

للحصول على هذه العناوين تُستخدم طريقة مشابهة لتلك المستخدمة للحصول على قاعدة النواة لكلا الوحدتين، عبر استدعاء NtQuerySystemInformation مرتين، لكن في هذه الحالة ستكون الوسيطة الأولى SYSTEM_INFORMATION_CLASS (في الإثبات PoC نستخدم دالة FindKernelModulesBase لهذا الغرض).

صورة تحتوي على نص وخط ولقطة شاشة وسطر، وصف تم إنشاؤه تلقائيًاثم يقوم بتحميل CLFS.sys وNTOSKRNL.exe كوحدات عادية في وضع المستخدم عن طريق استدعاء LoadLibrary، ويحصل على العناوين في وضع المستخدم باستخدام GetProcAddress، ثم يطرح قاعدة الصورة من كل منهما، مما يحصل على إزاحة الدالة، وأخيرًا يضيف كل إزاحة إلى قواعد النواة المقابلة وبذلك يحصل على عناوين النواة لجميع الدوال الضرورية.

صورة تحتوي على نص وخط وسطر ولقطة شاشة، وصف تم إنشاؤه تلقائيًا

2-تحضير المسار لإنشاء ملفات .blf:

أنشئ دالة تسمى createInitialTriggerBlfFile ستقوم بتوليد وكتابة ملف .blf.

المسار المستخدم كوسيطة في CreateLogFile يختلف عن المسار العادي، على سبيل المثال لفتح الملف 1280.blf الموجود في مجلد C:\Users\Public، يجب تعيين المسار LOG:C:\Users\Public\1280. سيتم حفظ ذلك في المتغير stored_name_CreateLog.

أقوم بذلك باستخدام wsprintfW() لأن stored_env يخزن المسار C:\Users\Public، الذي تم الحصول عليه مسبقًا من متغيرات البيئة. إلى هذه السلسلة سأضيف في البداية السلسلة LOG: واسمًا عشوائيًا في النهاية، بدون امتداد .blf.

لقطة شاشة لجهاز كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة متوسطة

سيكون هذا هو المسار إلى ملفي الأولي الذي سأسميه "trigger blf". بالطبع، يجب أيضًا حفظ المسار العادي لنفس الملف بدون LOG: في المقدمة ومع امتداد BLF لفتحه وتعديله باستخدام CreateFile() وWriteFile() مثل أي ملف آخر، سيكون هذا المسار مثلًا: C:\Users\Public\1280.blf، وسيتم تخزينه في المتغير stored_name_fopen.

صورة تحتوي على نص وخط ولقطة شاشة وسطر، وصف تم إنشاؤه تلقائيًا

بالطبع، كلا المسارين يشيران إلى الملف نفسه، ويجب استخدام أحدهما أو الآخر وفقًا للسياق.

3-إنشاء ملف "trigger blf" باستخدام دالة CreateLogFile().

تقوم دالة CreateLogFile بوظيفة مشابهة جدًا لدالة CreateFile() (إنشاء ملفات جديدة أو فتح ملفات موجودة والحصول على مقبضها)، حتى أن بعض الوسائط متشابهة، لكن CreateLogFile() تعمل فقط مع ملفات blf.

بالإضافة إلى ذلك، عند فتح ملف موجود، تتحقق من أن التنسيق سليم، حتى أن كل كتلة تحتوي على مجموع اختباري وإذا لم يكن صحيحًا فستُرجع خطأً.

سأنشئ نوعين من ملفات BLF:

  1. ملف Trigger blf

  2. ملف Spray blf

كلاهما ملفان blf لكن تم تعديلهما بطريقة مختلفة.

لقطة مقرّبة لكود برنامج كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة منخفضةبهذه الطريقة، يقوم الإثبات (PoC) أولاً بإنشاء ملف "trigger blf" باستخدام CreateLogFile، مع المسار مثلًا: LOG:C:\Users\Public\1280 الذي قمت بإعداده قبل، وتم تخزينه في المتغير stored_name_CreateLog.

الوسيطة الخامسة fCreateDisposition، كما في CreateFileA()، يمكن أن تأخذ القيم التالية:

صورة تحتوي على نص وخط وسطر وإيصال، وصف تم إنشاؤه تلقائيًا

في هذه الحالة سأستخدم الوسيطة OPEN_ALWAYS، لذلك سيتم إنشاء الملف إذا لم يكن موجودًا، وإذا كان موجودًا فسيتم فتحه. وبما أن الملف غير موجود بعد، فسيتم إنشاؤه باسم عشوائي.

logFile = CreateLogFile(stored_name_CreateLog, GENERIC_READ | GENERIC_WRITE, 1, 0, 4, 0);

ستقوم CreateLogFile() بإنشاء ملف "trigger blf" الخاص بنا مع كتلته الست ومجاميعها الاختبارية المقابلة وستُرجع المقبض الذي سيتم تخزينه في المتغير logFile.

صورة تحتوي على نص ولقطة شاشة وخط ورقم، وصف تم إنشاؤه تلقائيًا

كل كتلة ستحتوي، بدءًا من الإزاحة الموضحة في العمود الأيسر، على ترويسة حجمها 0x70 بايت.

لذلك، على سبيل المثال، ترويسة CONTROL BLOCK تمتد من الإزاحة 0x0 إلى 0x70.

لقطة شاشة لجهاز كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة منخفضة

جميع ترويسات كل الكتل لها البنية نفسها المسماة _CLFS_LOG_BLOCK_HEADER.

هذه هي بنية الترويسة:

لقطة شاشة لجهاز كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة متوسطة

عند الإزاحة 0xC من الترويسة يمكنني العثور على المجموع الاختباري، وبما أن CONTROL BLOCK يبدأ عند الإزاحة 0، فسيكون المجموع الاختباري عند الإزاحة 0xC من الملف، وبالتالي سيكون لكل كتلة مجموعها الاختباري عند 0xC من بداية كتلتها.

لقطة شاشة لجهاز كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة منخفضة

4-صياغة ملف “trigger blf”:

لتعديل ملف trigger blf، يجب فتحه كملف عادي إما باستخدام CreateFileA أو fopen ثم تعديله باستخدام WriteFile أو fwrite على التوالي. أقوم بذلك في بداية دالة fun_prepare في الإثبات (PoC).

تذكر أن المسار العادي مخزّن في المتغير stored_name_fopen، لذلك أستخدمه لفتح الملف باستخدام wfopen_s (وهو متغير من fopen يدعم سلاسل Unicode).

يتم تعديل الملف في دالة craftTriggerBlfFile التي تُستدعى من fun_prepare.

صورة تحتوي على نص وسطر وخط ولقطة شاشة، وصف تم إنشاؤه تلقائيًا

ثم أستدعي fseek للإشارة إلى الإزاحة المطلوب تغييرها ثم باستخدام fwrite يتم تعديل الملف.

لقطة شاشة لجهاز كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة متوسطةالتعديلات التي يجب إجراؤها على ملف "trigger blf" هي كما يلي:

بعد إجراء هذه التعديلات، يتم استدعاء FixCRCFile لحساب المجموع الاختباري الجديد وإصلاح المجاميع الاختبارية للكتل الأربع الأولى. الكتلتين التاليتين لا تحتويان على أي تغييرات، لذلك ليس من الضروري إعادة حساب مجاميعهما الاختبارية.

صورة تحتوي على نص وخط ولقطة شاشة ورقم، وصف تم إنشاؤه تلقائيًا

5-الحصول على عنوان النواة للكتلة الأساسية BASE BLOCK لملف trigger blf:

يقوم برنامج تشغيل CLFS.sys بقراءة الكتل الست للملف، ولتخزين محتواها يقوم بتخصيص ذاكرة في تجمع النواة (Kernel pool).

صورة تحتوي على نص ولقطة شاشة وخط ورقم، وصف تم إنشاؤه تلقائيًا

هناك بنية مهمة جدًا بحجم 0x90، وقد وجدت بعض الحقول فيها عبر الفحص العكسي في التدوينة السابقة حول CVE-2022-37969، وأطلقت عليها اسم pool_0x90. بعد قدر أكبر من الفحص العكسي، أعرف الآن أن اسمها الحقيقي هو m_rgBlocks، وبينما تقوم وحدة التحكم بتخصيص ذاكرة لنسخ محتويات كل كتلة من الملف، تحفظ هناك حجم كل كتلة وإزاحة البداية وعنوان النواة حيث تم تخزينها.

صورة تحتوي على نص ولقطة شاشة وخط، وصف تم إنشاؤه تلقائيًا

تحتوي على ست بنى CLFS_METADATA_BLOCK تتوافق مع كل كتلة برقمها.

طول كل بنية CLFS_METADATA_BLOCK هو 0x18 بايت. (0x18*6=0x90)

صورة تحتوي على نص وخط وسطر ورقم، وصف تم إنشاؤه تلقائيًاعند الإزاحة 0 يوجد اتحاد (union)، لكن على الأقل في هذا الاستغلال يُستخدم الحقل pbImage فقط، لذا بتبسيطها ستكون:

يمكن أن يتم تخصيص تلك البنية من موضعين مختلفين في برنامج تشغيل CLFS.sys، اعتمادًا على إنشاء ملف جديد أو فتح ملف موجود. في حالة إنشاء ملف جديد، يقوم برنامج التشغيل بتخصيص 0x90 بايت من CClfsBaseFilePersisted::CreateImage+28A، بينما في حالة ملف موجود فإنه يخصص من CClfsBaseFilePersisted: ReadImage+6E.

بعد ذلك، سأحصل على عنوان بداية الكتلة 2 التي تتوافق مع ملف trigger blf، وتسمى BASE BLOCK وتبدأ عند الإزاحة 0x800 وطولها 0x7a00.

صورة تحتوي على نص ولقطة شاشة وخط ورقم، وصف تم إنشاؤه تلقائيًا

داخل دالة fun_prepare أدناه سيتم العثور على هذا العنوان في النواة باستخدام هذه القطعة من الكود.

لقطة شاشة لكود برنامج كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة منخفضة

أولًا، تقوم دالة getBigPoolInfo بالعثور على جميع التخصيصات في التجمع التي تحمل وسم "Clfs" وحجم 0x7a00، ثم تخزنها في مصفوفة.

بعد ذلك، يفتح مرة أخرى ملف trigger blf المعدّل سابقًا باستخدام CreateLogFile مع الوسيطة OPEN_EXISTING، فيفتح ملفًا موجودًا، وسيؤدي ذلك إلى تخصيص الكتلة الأساسية BASE BLOCK الخاصة به.

عند استدعاء getBigPoolInfo مرة أخرى، سيكون هناك تجمع “Clfs” جديد واحد بحجم 0x7a00، ويتم استرجاع عنوانه عبر استدعاء NtQuerySystemInformation مرتين.

يتم تخزين عنوان BASE BLOCK لملف trigger blf في المتغير CLFS_kernelAddrArray.

لاحظ أنه إذا لم يكن ملف trigger blf المعدّل يحتوي على المجموع الاختباري الصحيح، فستفشل دالة CreateLogFile().

6-استدعاء AddLogContainer بمقبض ملف trigger blf:

الجزء الأخير من دالة fun_prepare يستدعي واجهة AddLogContainer باستخدام مقبض ملف trigger blf.

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

7-تحضير ملفات spray blf:

في آخر دالة من الإثبات (PoC) تسمى to_trigger، سيتم إنشاء نوع ثانٍ من ملفات blf،

سأسميه spray blf.

سيُستخدم هذا النوع من الملفات لملء مساحة ذاكرة (رش)، نحتاج إلى 10 ملفات من هذا النوع، لكن يتم إنشاء ملف واحد فقط في البداية. صورة تحتوي على نص وخط وسطر ولقطة شاشة، وصف تم إنشاؤه تلقائيًا

سيتم إنشاء ثلاث مصفوفات لتخزين الأسماء العشوائية لهذه الملفات:

stored_log_arrays: تخزن عشرة أسماء عشوائية جديدة لملفات .blf التي ستُستخدم مع CreateLogFile.

stored_container_arrays: تخزن أسماء عشوائية لإنشاء عشرة ملفات حاوية جديدة.

stored_fopen_arrays: تخزن أسماء ملفات السجل من المصفوفة الأولى (المتغير stored_log_arrays)، لكن مع مسارها العادي (بدون السلسلة “LOG:”) ومع امتداد .blf.

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

في كل تكرار يتم نسخ ملف blf باستخدام CopyFileW، ويتم تعيين الأسماء المخزنة في المصفوفات.

دالة fun_trigger تستدعي craftSprayBlfFile حيث يتم إجراء التعديلات على كل ملف، وسيقوم FixCRCFile بإصلاح مجاميع CRC.

لقطة شاشة لبرنامج كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة متوسطة

باختصار، لقد أنشأت 10 ملفات متشابهة (spray blf) بأسماء عشوائية مع التعديلات التالية:

لقطة شاشة لجهاز كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة متوسطة

التعديل الأخير هو نسخ الكتلة 0 بالكامل (CONTROL BLOCK) إلى الكتلة 1 (CONTROL BLOCK SHADOW)

لقطة شاشة لجهاز كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة منخفضة

سيتم شرح تأثير هذه التعديلات، بالإضافة إلى تلك التي أُجريت على ملف trigger blf، لاحقًا في فصل التصحيح.

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

عند هذه النقطة تكون الملفات قد أُنشئت وعُدّلت بالفعل، وجاهزة لتنفيذ الرش، فعند فتحها باستخدام CreateLogFile ستتوضع في منطقة الذاكرة التي نريدها، كما سيظهر لاحقًا.

8-تحضير الذاكرة لتنفيذ الرش

في دالة to_trigger، يتم إنشاء مصفوفة من 12 عنصرًا، تحتوي على عنوان BASE BLOCK لملف trigger blf زائد 0x30.

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

ضمن حلقة، يستدعي CreatePipe لإنشاء أنابيب قراءة-كتابة.

بهذه الطريقة، سيتم أولاً إنشاء 0x5000 أنبوب ثم يتم الاستدعاء مرة أخرى لإنشاء 0x4000 أنبوب إضافي.

ثم يستخدم WriteFile لكتابة المصفوفة التي تم إنشاؤها مؤخرًا إلى أول 5000 أنبوب، والتي تحتوي على عناوين BASE BLOCK + 0x30 لملف trigger blf.

لقطة شاشة لكود برنامج كمبيوتر، وصف تم إنشاؤه تلقائيًا بثقة منخفضة

الآن لديه كتلة متراصة أُنشئت في الذاكرة، سيحرر 0x667 أنبوبًا من الرقم 0x2000 وحتى 0x2667، ونظرًا لأن الأنابيب في الذاكرة ليست بالترتيب نفسه الذي أُنشئت به، فإن ما سيحدث هو ظهور مساحات فارغة في هذه الكتلة من الذاكرة.

صورة تحتوي على نص ولقطة شاشة وخط وبرنامج، وصف تم إنشاؤه تلقائيًالاحظ أن تخصيصات الأنابيب لها حجم مستخدم يبلغ 0x90 بايت، لذلك عند تحريرها سيكون لدينا

إنه يحرر مساحات الذاكرة بحجم 0x90 بين الذاكرة الممتلئة بالأنابيب.
ثم يكرر الاستدعاء لـCreateLogFile مع ملفات spray blf العشرة.
عند استدعاء CreateLogFile لفتح ملفات موجودة، يتم تنفيذ تخصيص 0x90 بايت لبنية m_rgBlocks واحدة لكل ملف spray blf، لذلك ستشغل هذه التخصيصات الفجوات التي تُركت عند تحرير الأنابيب لأنها بنفس الحجم.

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

ثم يكرر عملية الكتابة في آخر 0x4000 أنبوب للمصفوفة التي تحتوي على عنوان BASE BLOCK +0x30 لملف trigger blf.

9-استثارة الخطأ

كل هذه التلاعبات تُنشئ مساحة ذاكرة خاضعة للتحكم، وسأريك كيف تكون عند تصحيح الأخطاء، لكن الفكرة هي أن بنى m_rgBlocks لكل ملف spray blf تشغل فجوات الـ 0x90 بايت التي تم تحريرها.

ثم في الجزء الأخير، يتم استثارة الخطأ داخل حلقة while( 1 ) باستخدام استدعاء AddLogContainer لملفات spray blf.صورة تحتوي على نص، خط، سطر، رقم وصف
تم إنشاؤه تلقائيًا

داخل حلقة while هذه يتم تشغيل الثغرة:

لقطة شاشة لبرنامج كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضة

ستخرج حلقة while هذه عندما تجد رمز System المميز (token)، باستخدام دالة NtFsControlFile التي ستقرأ خصائص الأنابيب (pipes).

لقطة شاشة لكود كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضة

ثم باستخدام CreateLogFile، يتم مرة أخرى استبدال رمز العملية الخاص بنا برمز System المميز الذي تم العثور عليه مؤخرًا، وبهذه الطريقة نحقق رفع الصلاحيات.

صورة تحتوي على نص، خط، سطر، لقطة شاشة وصف
تم إنشاؤه تلقائيًا

ثم نقوم باستعادة بعض القيم، وإغلاق مقابض (handles) الأنابيب وملفات blf، وتشغيل Notepad كـ System للتحقق من أننا رفعنا الصلاحيات بشكل صحيح.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

لاحظ ملفات blf التي تم إنشاؤها في مجلد PUBLIC. تذكر أنه إذا أردت المحاولة مرة أخرى، يجب عليك أولاً حذف الملفات التي تم إنشاؤها. بعضها سيكون مقفلاً ولا يمكن حذفه، لكن الـ PoC سيظل يعمل.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

تصحيح الأخطاء:

1- التحقق من حقن الذاكرة (memory spray)

قبل أن أبدأ بتأثير التغييرات على ملفات trigger blf و spray blf لتنفيذ الاستغلال، يجب أن أتحقق من أن m_rgBlocks لملفات spray blf files موجودة في الفجوات التي تحدث في توزيع الذاكرة، بعد تنفيذ حقن الأنابيب (pipe spray) والإطلاق اللاحق لعدد ثابت من الأنابيب.

عندما ينتهي هذا الإجراء، يجب أن يكون هناك أنبوب موجود أسفل بايتات الـ 0x90 الخاصة بـ m_rgBlocks، لذلك عند استخدام m_rgBlocks، سيحدث OUT OF BOUNDS وسيقرأ من ذلك الأنبوب الموجود بالأسفل.

يحتوي الـ PoC على نقطة مثالية لوضع نقطة توقف (breakpoint):

لقطة شاشة لكود كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضة

عند هذه النقطة، يكون فتح ملفات spray blf قد اكتمل ولم يتم استدعاء دالة AddLogContainer بعد.

لتصحيح الأخطاء في وضع المستخدم، سأستخدم x64dbg، وفي وضع النواة، IDA مع إضافة Windbg.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضة

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

سأوقف IDA مؤقتًا للعثور على نقطة مثيرة للاهتمام لوضع نقطة توقف.

سأضع نقطة توقف عند CClfsBaseFilePersisted::AddContainer، والتي يتم استدعاؤها من AddLogContainer وفي البداية، يشير سجل RCX إلى بنية CClfsBaseFilePersisted وعند الإزاحة 0x30 يوجد مؤشر إلى m_rgBlocks.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

عند الوصول إلى نقطة التوقف، أتحقق في مكدس الاستدعاءات (call stack) من أن AddLogContainer يتم استدعاؤها من الـ PoC الخاص بي.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

سجل RCX يشير إلى:

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضة لقطة شاشة لكمبيوتر
وصف تم إنشاؤه تلقائيًا بثقة
منخفضة

الحقل الأول هو المؤشر إلى vtable (CLFS! CClfsBaseFilePersisted::'vftable') وعند الإزاحة 0x30 يوجد المؤشر إلى m_rgBlocks.

لقطة شاشة لكود كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

الكتل 0 و1 و4 و5 لم تقم بحفظ pbImage بعد، بينما الكتلتان 2 (BASE BLOCK) و3 (SHADOW BLOCK) قامتا بذلك.

كل كتلة في جدول m_rgBlocks لها cbOffset وهو الإزاحة التي تبدأ عندها الكتلة في الملف، وcbImage هو حجم الكتلة، وeBlockType هو نوع الكتلة.

إذا كان الحقن صحيحًا، فيجب أن يكون هناك أنبوب أسفل m_rgBlocks وبداخله المؤشرات إلى BASE BLOCK + 0x30 الخاص بـ trigger blf.

لقطة شاشة لكود كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضة

يعرض أمر "!pool" في windbg توزيع الذاكرة:

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

كل m_rgBlocks له وسم "Clfs” وحجمه 0xa0 لأنه حجم المستخدم 0x90 بالإضافة إلى ترويسة 0x10، وتحته يوجد أنبوب بوسم "NpFr" له نفس حجم المستخدم 0x90

  • ترويسة 0x10.

نظرًا لأن التوزيع ليس علمًا دقيقًا، فقد تم وضع بعض "Clfs” بشكل متواصل، وهو أمر غير مرغوب فيه، لكن الذي أعمل عليه موضوع بشكل صحيح ويتبعه أنبوب.

2-النظر إلى RecordOffset[12] الخاص بـ trigger blf

من أول التغييرات المؤثرة هو التغيير الذي تم في ملف trigger blf عند الإزاحة 0x858، حيث يتم تخزين القيمة 0x369.

لقطة قريبة لإشارة وصف تم إنشاؤه تلقائيًا بثقة
منخفضة

تبدأ BASE BLOCK عند الإزاحة 0x800 في الملف.

صورة تحتوي على نص، لقطة شاشة، خط، سطر وصف
تم إنشاؤه تلقائيًا

داخل _CLFS_LOG_BLOCK_HEADER عند الإزاحة 0x800+0x58 (0x58 من بداية ترويسة BASE BLOCK).

صورة تحتوي على نص، لقطة شاشة، خط، شاشة عرض وصف
تم إنشاؤه تلقائيًا

عند الإزاحة 0x28 تبدأ مصفوفة RecordOffsets (DWORD).

بالتحرك 0x30 بايت إلى الأمام، عند الإزاحة 0x58 (0x828+0x30=0x858 من البداية)، يوجد الحقل 12 من RecordOffsets.

صورة تحتوي على نص، خط، لقطة شاشة، رسومات وصف
تم إنشاؤه تلقائيًا

أقوم بتشغيل الـ PoC حتى CreateLogFile كما هو موضح في الصورة أدناه:

لقطة شاشة لكود كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضة لقطة شاشة لكود كمبيوتر
وصف تم إنشاؤه تلقائيًا بثقة
منخفضة

لقطة شاشة لكمبيوتر وصف تم إنشاؤه
تلقائيًا

قبل أن أدخل إلى CreateLogFile سأضع نقطة توقف في مكان لم يتم استخدام القيمة 0x369 فيه بعد.

في حالة قيام CreateLogFile بفتح ملف موجود، يتم تخصيص بنية m_rgBlocks هنا:

CClfsBaseFilePersisted::ReadImage+6E

لذا، سأضع نقطة توقف في IDA هنا تمامًا:

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

عند تفعيل نقطة التوقف**:**

صورة تحتوي على نص، لقطة شاشة، خط، رقم وصف
تم إنشاؤه تلقائيًا

في m_rgBlocks لا تزال هناك بعض البيانات العشوائية (garbage) لأنه غير مهيأ بعد، ولكن بمجرد تخصيص pbImage للكتلة 2، سيتم حفظ العنوان في الإزاحة 0x30 من البداية، نظرًا لأن الحقل الأول داخل كل CLFS_METADATA_BLOCK هو pbImage.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

صورة تحتوي على نص، لقطة شاشة، خط، سطر وصف
تم إنشاؤه تلقائيًا

الآن أضع نقطة توقف عتادية (hardware breakpoint) على الكتابة: ba w1 ffffd003'7f5bea30

بعد التهيئة إلى صفر، يتوقف عندما يحفظ pbImage.

يقول التحليل أنه يتوافق مع block0، لأنه لا يأخذ في الاعتبار الثابت r14*8 الذي يساوي 0x30 بعد ذلك، ونتيجة لذلك فهو في الحقيقة يكتب pbImage الخاص بـ block 2.

صورة تحتوي على نص، لقطة شاشة، خط وصف تم إنشاؤه
تلقائيًا

لاحظ أن CClfsBaseFilePersisted::ReadMetadataBlock يُستخدم لتخصيص أي من الكتل، باستخدام الحجم الذي تم تمريره كوسيط.

صورة تحتوي على نص، خط، لقطة شاشة، سطر وصف
تم إنشاؤه تلقائيًا

الآن أضع نقطة توقف قراءة/كتابة عند 0x58 من الكتلة الأساسية (base block)، لمعرفة متى يستخدم القيمة 0x369.

ba r1 FFFF978A'16ECF000+0x58

عند الوصول إلى نقطة التوقف، يقرأ القيمة 0x369 الموجودة في RecordOffset[12]، ويضيفها إلى مؤشر غريب على r14 ويزيد محتويات RAX+r14.

بضعة أسطر أعلاه في الكود، ESI لديه القيمة 0x13 ويضربها في 0x18، وهو حجم كل كتلة في m_rgBlocks.

WINDBG>? 0x18*0x13

Evaluate expression: 456 = 00000000'000001c8

إذا أضفت قيمة r8= 0x1c8 التي هي أكبر من 0x90، إلى العنوان الأولي لـ m_rgBlocks، فسيكون يقرأ OUT OF BOUNDS.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه
تلقائيًا

أسفل m_rgBlocks، يوجد الأنبوب الذي يحمل المؤشر إلى BASE BLOCK + 0x30، ويقرأ هذا المؤشر الذي تم وضعه بشكل استراتيجي داخل الأنبوب.

لقطة شاشة لبرنامج كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضةالموضع الحالي في الكود تم استدعاؤه من عبارة while(1) الخاصة بالوحدة الرئيسية.

داخل ملف spray blf قمت بوضع القيمة 0x13 بشكل استراتيجي عند الإزاحة 0x48a (iFlushBlock).

صورة تحتوي على نص، خط، سطر، لقطة شاشة وصف
تم إنشاؤه تلقائيًا

3-النظر إلى قيمة iFlushBlock في ملف spray blf.

عند الإزاحة 0x8a من ملف spray blf، يوجد iFlushBlock الخاص بـ BLOCK 0، وقيمته 4، بينما الإزاحة 0x48a تخص iFlushBlock الخاص بـ BLOCK 1، وقيمته 0x13

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

الآن يجب أن أعرف لماذا يقرأ iFlushBlock = 0x13 من BLOCK 1 بدلاً من iFlushBlock = 4 من BLOCK 0.

4-لماذا يقرأ من BLOCK 1 SHADOW بدلاً من BLOCK 0 CONTROL؟

إذا نظرت إلى الوراء لمعرفة من أين جاءت 0x13، أرى في مكدس الاستدعاءات أن WriteMetadataBlock يتم استدعاؤها من CClfsBaseFilePersisted::ExtendMetadataBlock+416، وهناك الوسيط الثاني iFlushBlock هو EDX=0x13، والذي يأتي من r9w.

لقطة شاشة لكود كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضة

صورة تحتوي على نص، لقطة شاشة، خط، سطر وصف
تم إنشاؤه تلقائيًا

لقطة شاشة لبرنامج كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضةقبل سطرين، تم استدعاء CClfsBaseFile::GetControlRecord لاسترجاع عنوان BLOCK 0، ربما تكون المشكلة هنا، لذا سأعيد التشغيل وأضع نقطة توقف عليه.

تستدعي GetControlRecord الدالة CClfsBaseFile::AcquireMetadataBlock التي يجب أن تملأ جدول m_rgBlocks بعنوان block 0، وعندما أتخطى هذه الدالة تحصل على عنوان block 1، لذا، تحدث المشكلة داخل CClfsBaseFile::AcquireMetadataBlock.

بإضافة 0x8A إلى العنوان المسترجع، يمكنني تأكيد أن القيمة 0x13 التي تخص BLOCK 1 موجودة.

لقطة شاشة لكود كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

سأعيد التشغيل وأضع نقطة توقف هناك:

لقطة شاشة لبرنامج كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضة

CClfsBaseFile::GetControlRecord+27 call CClfsBaseFile::AcquireMetadataBlock

لقطة شاشة لكمبيوتر وصف تم إنشاؤه
تلقائيًا

الوسيط الثاني الذي تم تمريره إلى AcquireMetadataBlock هو صفر، وهو يتوافق مع block 0، وسيقوم بالنسخ من الملف وتخزين عنوانه في m_rgBlocks لقطة شاشة لكمبيوتر وصف
تم إنشاؤه تلقائيًا بثقة متوسطة.

في تعداد نوع الكتلة _CLFS_METADATA_BLOCK_TYPE، لديهم أسماء مختلفة عن تلك التي استخدمتها، لكنها نفس الكتل الست.

صورة تحتوي على نص، لقطة شاشة، خط، سطر وصف
تم إنشاؤه تلقائيًا

بعد التحقق من أن نوع الكتلة أقل من الحد الأقصى m_cBlocks=6، يقوم بحفظ قيمة مرجعية (reference) لتجنب قراءة نفس الكتلة مرتين.

لقطة شاشة لكود كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضة

يتم استدعاء ReadMetadataBlock، مشكلة قراءة block 1 بدلاً من block 0 ستكون داخل هذه الدالة.

صورة تحتوي على نص، خط، رقم، سطر وصف
تم إنشاؤه تلقائيًا

إذا كان كل شيء على ما يرام، فإنه يخصص باستخدام cbImage كحجم ويخزن العنوان في الحقل block 0-> pbImage في m_rgBlocks.

صورة تحتوي على نص، خط، سطر، لقطة شاشة وصف
تم إنشاؤه تلقائيًا

يعرض أمر !pool الوسم (tag) والحجم المخصص.

صورة تحتوي على نص، لقطة شاشة، خط، سطر وصف
تم إنشاؤه تلقائيًا

صورة تحتوي على نص، لقطة شاشة، خط، سطر وصف
تم إنشاؤه تلقائيًالذا، لدي بالفعل عنوان pbImage الخاص بـ block 0 مخزن في m_rgBlocks، لذلك أحتاج إلى معرفة لماذا ينسخ بايتات block 1 هناك بدلاً من بايتات block 0.

أصل إلى استدعاء CClfsContainer::ReadSector حيث يتم تمرير مؤشر إلى متغير يحتوي على pbImage، لكتابة البايتات.

لاحظ التغييرات التي حدثت في محتوى pbimage عند تخطي ReadSector.

بإضافة 0x8a إلى pbImage يمكنني العثور على القيمة 4 وهي القيمة الصحيحة، بدلاً من 0x13، لذا يجب أن تحدث المشكلة لاحقًا.

بعد استدعاء ClfsDecodeBlock فإنها ترجع خطأ 0x0C01A000A.

CClfsBaseFilePersisted::ReadMetadataBlock+153 يستدعي ClfsDecodeBlock

بعد هذا الخطأ، يضيف 1 إلى النوع ويستدعي CClfsBaseFilePersisted::ReadMetadataBlock مرة أخرى ولكن بالنوع 1 لقراءة block 1.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه
تلقائيًا

في CClfsBaseFilePersisted::ReadMetadataBlock يقوم بتخصيص وتخزين pbImage جديد في m_rgBlocks لـ block 1.

لقطة شاشة لكود كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضة

الكتلتان 0 و1 لهما عناوين مختلفة، الآن إذا أضفت 0x8a إلى عنوان block 1 فإن قيمته تكون 0x13.

ربما بما أن block 0 أرجع خطأً، فإنه يستخدم block 1 ويعيده إلى GetControlRecord ككتلة تحكم (Control Block).

كما هو موضح سابقًا، عندما يستخدم القيمة 0x13 بدلاً من 4، فإنه يخرج خارج حدود m_rgBlocks ويقرأ قيم حقن الأنابيب (pipe spray) التي أتحكم بها.

صورة تحتوي على نص، لقطة شاشة، خط وصف تم إنشاؤه
تلقائيًاثم يحرر pbImage من block 0 وينسخ المؤشر من block 1 إلى block 0.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه
تلقائيًا

سيكون من الضروري العثور على القيمة التي تسبب الخطأ 0x0C01A000A داخل ClfsDecodeBlock.

داخل ClfsDecodeBlock، المجموع الاختباري (checksum) للكتلة الأولى هو صفر، وهذا هو الخطأ 0xC01A000A.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

5-لماذا المجموع الاختباري يساوي صفرًا في ملفات blf spray؟

قبل استدعاء AddLogContainer، عند فتح أي ملف spray blf بمحرر سداسي عشري، تم تغيير المجموع الاختباري إلى صفر.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه
تلقائيًا

كان يجب أن يتغير من قبل عندما تم فتحه باستخدام CreateLogFile.

صورة تحتوي على نص، لقطة شاشة، شاشة عرض، خط وصف
تم إنشاؤه تلقائيًا

لسبب ما، تنتهي ملفات spray blf بعد الخروج من CreateLogFile بمجموع اختباري للـ block 0 يساوي 0 وتُرجع مقبضًا (handle) صالحًا، دعنا نرى لماذا يحدث هذا.

أتوقف عند CreateLogFile قبل فتح بعض ملفات spray blf.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

لاحظ أنه قبل استدعاء CreateLogFile، تحتوي ملفات spray على المجموع الاختباري الصحيح في block 0 وبعد اكتمال الدالة، تتغير قيمة المجموع الاختباري إلى صفر.

لقطة شاشة لكود كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة منخفضة

لذا، أضع نقطة توقف على CClfsBaseFile::GetControlRecord، للنظر إلى الداخل.

صورة تحتوي على نص، لقطة شاشة، خط، سطر وصف
تم إنشاؤه تلقائيًابعد تجاوز CClfsContainer::ReadSector، المجموع الاختباري ليس صفرًا.

قبل الدخول لحساب CRC32، يقوم بوضع حقل المجموع الاختباري على صفر في الذاكرة لحساب CRC، والنتيجة صحيحة.

صورة تحتوي على نص، خط، لقطة شاشة وصف تم إنشاؤه
تلقائيًا

صورة تحتوي على نص، خط، سطر، لقطة شاشة وصف
تم إنشاؤه تلقائيًا

ثم يتحقق من قيمة eExtendState =2 ويذهب إلى WriteMetadataBlock.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

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

صورة تحتوي على نص، خط، سطر، لقطة شاشة وصف
تم إنشاؤه تلقائيًا

يتحقق من بعض القيم التي تمت هندستها في ملف blf spray للوصول إلى CClfsBaseFilePersisted::ExtendMetadataBlock.

لقطة شاشة لبرنامج كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

بعد حلقة لقراءة الكتل التي لم تتم قراءتها بعد، يستمر block 0 بـ checksum = 0.

مستطيل أبيض به نص أسود وصف تم إنشاؤه تلقائيًا
بثقة منخفضة

الوصول إلى WriteMetadataBlock.

بما أنني أقوم بالتشغيل قبل أن يستبدل block 0 بـ 1، فإن قيمة iFlushBlock لملف blf spray لا تزال 4 القيمة الصحيحة.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

الآن يعمل مع block 4، وسيكتب block 4 في الملف، هنا ليست المشكلة بعد.

ثم يصل إلى CClfsBaseFilePersisted::FlushControlRecord

لقطة شاشة لبرنامج كمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

بداخله يصل إلى WriteMetadataBlock، ولكن مع الوسيط 0، لكتابة block 0 إلى الملف**.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

ثم تُرجع ClfsEncodeBlock الخطأ 0xC01A000A، على الرغم من أنها ستكتب الملف بـ block 0 السيئ في CClfsContainer::WriteSector، في الأسفل مباشرة.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

يقوم المتغير var_54 بتخزين قيمة الخطأ 0xC01A000A وسيتم التحقق منه قبل الخروج من الدالة.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه تلقائيًا
بثقة متوسطة

ولكن بعد استدعاء CClfsContainer::WriteSector التي لا تُرجع أي خطأ، يتم استبدال محتوى var_54 بصفر.

لذا، تُرجع الدالة صفرًا بدون خطأ وتستمر في العمل نظرًا لأن CreateLogFile سيعيد مقبضًا (handle) بدلاً من قيمة خطأ.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه
تلقائيًا

6-إنهاء الاستغلال.

القيمة 0x13 في iFlushBlock تجعله يخرج عن الحدود (out of bounds) و سيقرأ المؤشر الموجود في الأنابيب الذي يشير إلى Base Block +30 الخاص بـ trigger blf.

لقطة شاشة لكمبيوتر وصف تم إنشاؤه
تلقائيًاثم يضيف 0x28 إلى ذلك المؤشر، (0x58 من بداية الكتلة الأساسية لـ trigger blf) التي لها القيمة 0x369.

لقطة شاشة لبرنامج كمبيوتر، الوصف مُنشأ تلقائيًا
بثقة متوسطة

تعليمة INC ستزيد القيمة 0x14 بمقدار 1 وتتكرر 4 مرات، لذا تنتهي 0x14 لتصبح 0x18.

WINDBG>db r14+369

ffffcb82'091e7397 14 00 00 00

بعد ذلك، يتم استدعاء CreateLogFile، ويقرأ القيمة 0x1858.

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
متوسطة

لقطة مقرّبة لبطاقة، الوصف مُنشأ تلقائيًا بثقة
منخفضةتتحقق GetSymbol مما إذا كانت الكتلة المزيفة المُنشأة سابقًا في trigger blf، والمشار إليها بالإزاحة 0x1858، تحتوي على القيم الصحيحة.

صورة تحتوي على نص، خط، سطر، لقطة شاشة، الوصف مُنشأ
تلقائيًا

لو لم تتم زيادة المؤشر عدة مرات، لكانت له القيمة الأصلية 0x1458 وسيشير إلى الكتلة الصحيحة.

بعد الخروج من GetSymbol، سيستخدم تلك الكتلة المزيفة هنا.

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
متوسطة

ثم سيقرأ قيمة الإزاحة 0x18 من الكتلة المزيفة حيث أضع 0x05000000 ويقفز إلى محتوى ما هو موجود هناك.

WINDBG>dps 0x5000000

00000000'05000000 00000000'05001000

يقرأ محتوى 0x05000000 ومحتوى 0x05001000 وهناك يوجد ClfsEarlierLsn.

لقطة شاشة لبرنامج كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
منخفضة

تُستخدم هذه الدالة لإرجاع القيمة 0xFFFFFFFF في RDX على الرغم من أن هذه القيمة لا تُستخدم في المرة الأولى.

المكالمة الثانية تحدث هنا، حيث تستدعي PoFxProcessorNotification التي كانت على 0x501000 +8

صورة تحتوي على نص، خط، لقطة شاشة، سطر، الوصف مُنشأ
تلقائيًا صورة تحتوي على
نص، خط، لقطة شاشة، سطر، الوصف مُنشأ
تلقائيًا

WINDBG>dps 00000000**'05001000**

00000000'05001000 fffff805'7ab13220 CLFS! ClfsEarlierLsn

00000000'05001008 fffff805'769dc3b0 nt! PoFxProcessorNotification

لقطة شاشة لشاشة كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
منخفضة

في هذه الدالة RCX = 0x05000000، تتحقق من أن البايتات بعد 0x40 يجب أن تكون غير صفرية

WINDBG>dps rcx+40

00000000'05000040 00000000'05000000

عنوان القفز سيكون بعد 0x68.

WINDBG>dps rcx+68

00000000'05000068 fffff805'7ab2bfb0 CLFS! ClfsMgmtDeregisterManagedClient

وستكون الوسيطة بعد 0x48 بايت.

WINDBG>dps rcx+48

00000000'05000048 00000000'05000400

ClfsMgmtDeregisterManagedClient هي دالة ملائمة، لأنني أستطيع التحكم في الوسيطة، ولدي أيضًا قفزتان إلى دوال أتحكم فيها.

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ
تلقائيًا

المكالمة الأولى هي مرة أخرى إلى ClfsEarlierLsn التي أعادت في RDX=0xFFFFFFFF.

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
متوسطة

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
متوسطة

سيأخذ مصدر الكتابة من محتوى RDX=0xFFFFFFFF.

WINDBG>dps rdx

00000000'ffffffff ffff8005'3a4ee000

في العنوان 0xFFFFFFFF كنت قد خزنت system_EPROCESS & 0xfffffffffffff000.

صورة تحتوي على نص، خط، سطر، رقم، الوصف مُنشأ
تلقائيًا

الوجهة هي المؤشر الموجود عند 0x5000400 +0x48

*(UINT64*)0x5000448 = para_PipeAttributeobjInkernel + 0x18;

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ
تلقائيًا

مؤشر PipeAttribute في النواة الذي يشير إلى مخزن مؤقت مملوء بالحرف “A” سيتم استبداله بالجزء العلوي من مؤشر SYSTEM EPROCESS.

تم إنشاء هذا المؤشر عندما قمت سابقًا باستدعاء _NtFsControlFile مع مخزن مؤقت مليء بالحرف “A”.

لقطة شاشة لبرنامج كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
متوسطة

يمكن قراءة محتوى تلك السمة باستخدام NtFsControlFile.

لقطة شاشة لشاشة كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
متوسطة

الآن سمة الأنبوب لم تعد تشير إلى المخزن المؤقت الذي يحتوي على “A” بل إلى system_EPROCESS & 0xffffffffffffff000.

لقطة شاشة لبرنامج كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
منخفضة

سيتم تكرار هذا الكود حتى يتم استرداد رمز النظام.

لقطة شاشة لكود كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
متوسطة

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ
تلقائيًا

في ويندوز 11، يكون رمز النظام عند الإزاحة 0x4b8 من بنية EPROCESS التي تمت قراءتها مؤخرًا.

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ
تلقائيًا

أحتاج فقط إلى كتابة رمز النظام هذا في عمليتي عن طريق استدعاء CreateLogFile.

صورة تحتوي على نص، خط، سطر، لقطة شاشة، الوصف مُنشأ
تلقائيًا

للقيام بهذه المهمة، فقط كرر الخطوة المستخدمة لقراءة رمز النظام.

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
متوسطة

في المكالمة المزدوجة، يستدعي أولاً ClfsEarlierLsn لإرجاع 0xFFFFFFFF في RDX ثم يستدعي nt_SeSetAccessStateGenericMapping.

صورة تحتوي على نص، لقطة شاشة، خط، سطر، الوصف مُنشأ
تلقائيًا

أتحقق من أن القيمة المشار إليها بواسطة RDX هي رمز النظام.

صورة تحتوي على نص، لقطة شاشة، خط، الوصف مُنشأ
تلقائيًا

رمز عمليتي هو:

سوف يقوم بالكتابة هناك.

WINDBG>dps rax+8

ffff9b8b'fc446578 ffffc402'f601c06c

WINDBG>dps rax+8

ffff9b8b'fc446578 ffffc402'ef841919

الآن عمليتي هي System، يمكنني تشغيل Notepad للتحقق.

صورة تحتوي على نص، لقطة شاشة، خط، سطر، الوصف مُنشأ
تلقائيًا

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ
تلقائيًا

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ
تلقائيًا

7-التصحيح الحقيقي

يُظهر BINDIFF الكثير من الدوال المتغيرة.

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ
تلقائيًا

الدالة الهشّة موجودة هنا:

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
متوسطة

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ
تلقائيًا

الأساسي هو الإصدار المصحح، والثانوي هو الإصدار الهش.

يختبر التصحيح القيمة المرجعة من CflsEncodeBlock، وهي 0xC01A000A، ويخزنها في المتغير var_54، وبما أنها سالبة، يفحصها ويتجنب WriteSector.

التصحيح، بالإضافة إلى عدم كتابة الملف، تُرجع الدالة القيمة 0xc01a000a بشكل صحيح، وبهذا لا يُرجع CreateLogFile أي مقبض ولا يمكن أن يستمر الاستغلال.

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
متوسطة

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ
تلقائيًا

فقط إذا لم تكن ClfsDecodeBlock سالبة، فإنها تنتقل إلى WriteSector لكنها تُبقي على إرجاع القيمة السالبة 0xC01A000A.

لقطة شاشة لجهاز كمبيوتر، الوصف مُنشأ تلقائيًا بثقة
متوسطةهذا هو التصحيح الفعلي الذي يمنع الاستغلال حقًا باستخدام الـ PoC الذي أرفقته للتو.

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

نأمل أن تجده مفيدًا، إذا كان لديك أي استفسار يمكنك التواصل معنا:

[email protected] 
@ricnar456

 [email protected]
@solidclt

تنزيل الأداة