
تحليل الثغرة الأمنية
حول هذه الثغرة: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-20698
في ملف ntoskrnl.exe لدينا دالتان تم تصحيحهما: sub_1406AE224 و WbAddLookupEntryEx. الدالة المعرضة للخطر هي WbAddLookupEntryEx. المسارات المؤدية إلى هذه الدالة:

لاستدعاء هذه الدالة نحتاج إلى استدعاء NtQuerySystemInformation مع الوسيط الأول 0xb9. البايت الأول في المخزن المؤقت الذي يوفره المستخدم كوسيط ثاني هو خيار لهذا التبديل:
switch ( first_byte_in_usermod_buf )
{
case 1:
status_1 = WbDecryptEncryptionSegment(item, copy_of_usermod_buf, NumberOfBytes_1);
goto set_status_and_return;
case 2:
status_1 = WbReEncryptEncryptionSegment(item, copy_of_usermod_buf, NumberOfBytes_1);
goto set_status_and_return;
case 3:
status_1 = WbHeapExecuteCall(item, copy_of_usermod_buf, usermod_buffer, NumberOfBytes_1);
goto set_status_and_return;
case 4:
if ( !copy_of_usermod_buf )
{
status_1 = patched_case4_no_user_buffer(item, another_item);
set_status_and_return:
status = status_1;
goto return;
}
break;
case 5:
case 6:
status = usermod_buffer != 0i64 ? STATUS_NOT_IMPLEMENTED : STATUS_INVALID_PARAMETER;
goto return;
case 7:
status_1 = WbRemoveWarbirdProcess(item->proc_handle);
goto set_status_and_return;
case 8:
status_1 = WbProcessStartup(item, copy_of_usermod_buf, NumberOfBytes_1);
goto set_status_and_return;
case 9:
status_1 = WbProcessModuleUnload(item, copy_of_usermod_buf, NumberOfBytes_1);
goto set_status_and_return;
}
لكل عملية تستخدم NtQuerySystemInformation مع الوسيط 0xb9، سيتم إنشاء هيكل سيتم تخزينه في الذاكرة حتى تنتهي العملية. أسميت هذا الهيكل item_instance. أولاً، تحاول الدالة sub_1406AF294 العثور على هيكل العملية بين الهياكل الأخرى المشابهة. يتم تخزين المؤشرات إلى هذه الهياكل في الذاكرة بترتيب معرف العملية. إذا لم يتم العثور على الهيكل، فسيتم إنشاؤه بواسطة WbCreateWarbirdProcess. بعد ذلك، سيتم إضافة مؤشر إلى هذا الهيكل إلى المصفوفة باستخدام WbAddLookupEntryEx المعرضة للخطر.
كود WbAddLookupEntryEx قبل التصحيح:
NTSTATUS WbAddLookupEntryEx(
items_info *items_info,
__int64 new_item_pointer,
__int64 not_used,
unsigned int new_item_index)
{
unsigned int last_item_index;
NTSTATUS status;
unsigned int old_items_count;
unsigned int size_of_item;
last_item_index = items_info->last_item_index;
status = 0;
old_items_count = items_info->old_items_count;
if ( last_item_index + 1 >= old_items_count )
{
status = WbReAlloc(
items_info->vuln_buffer,
old_items_count * items_info->size_of_item,
items_info->size_of_item * (old_items_count + items_info->count_of_new_items),
&items_info->vuln_buffer);
if ( status < 0 )
return status;
items_info->old_items_count += items_info->count_of_new_items;
old_items_count = items_info->old_items_count;
last_item_index = items_info->last_item_index;
}
if ( new_item_index > last_item_index || !old_items_count )
return STATUS_INVALID_PARAMETER;
memmove(
(char *)items_info->vuln_buffer + (new_item_index + 1) * items_info->size_of_item,
(char *)items_info->vuln_buffer + new_item_index * items_info->size_of_item,
items_info->size_of_item * (last_item_index - new_item_index));
size_of_item = items_info->size_of_item;
++items_info->last_item_index;
*(_QWORD *)((char *)items_info->vuln_buffer + new_item_index * size_of_item) = new_item_pointer;
return status;
}
يقوم بإعادة تخصيص مصفوفة المؤشرات، وإدراج مؤشر جديد.
يضيف التصحيح عمليات التحقق الأمنية هذه:
status = RtlULongMult(old_items_count, items_info->size_of_item, &size_of_old_items);
if ( status < 0 )
return status;
status = RtlULongAdd(old_items_count, items_info->count_of_new_items, &sum_result);
if ( status < 0 )
return status;
status = RtlULongMult(sum_result, mb_item_size, result_size_8byte);
if ( status < 0 )
return status;
هذا يجعل عمليات الجمع والضرب آمنة من الفائض. لذا، في الإصدار غير المصحح، يمكننا الحصول على فائض صحيح في هذه الحالات:
بعد فائض الصحيح، سيتم إعادة تخصيص مصفوفة المؤشرات، ولكن بحجم أصغر. ثم، سيحاول memmove إزاحة العناصر لإفساح المجال للعنصر الجديد. وبالتالي، سوف يفسد الأجزاء (chunks) التي تقع بعد الكتلة المخصصة الجديدة.
لتفعيل فائض الصحيح، نحتاج إلى إنشاء 0xffffffff / size_of_item + 1 من الهياكل. size_of_item دائمًا 8. لذا، نحتاج إلى إنشاء 0x20000001 هيكل. كل هيكل يمثل عملية. لذا نحتاج إلى وجود 0x20000001 (536870913) عملية متزامنة. على جهازي الافتراضي، تمكنت فقط من إنشاء 12492 عملية متزامنة و 39652 على جهازي المضيف.