
भेद्यता का विश्लेषण
इस कमजोरी के बारे में: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-20698
ntoskrnl.exe में हमारे पास दो पैच किए गए फ़ंक्शन हैं: sub_1406AE224 और WbAddLookupEntryEx। कमजोर फ़ंक्शन WbAddLookupEntryEx है। इस फ़ंक्शन के पथ:

इस फ़ंक्शन को कॉल करने के लिए हमें पहले तर्क 0xb9 के साथ NtQuerySystemInformation को कॉल करना होगा। उपयोगकर्ता द्वारा दूसरे तर्क के रूप में प्रदान किए गए बफर का पहला बाइट इस स्विच का विकल्प है:
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 प्रक्रिया की संरचना को अन्य ऐसी संरचनाओं के बीच खोजने का प्रयास करता है। इन संरचनाओं के पॉइंटर्स प्रक्रिया ID के क्रम में मेमोरी में संग्रहीत होते हैं। यदि संरचना नहीं मिलती है, तो इसे 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 नए तत्व के लिए जगह बनाने के लिए तत्वों को स्थानांतरित करने का प्रयास करेगा। इस प्रकार, यह नए आवंटित चंक के बाद के चंक को दूषित कर देगा।
पूर्णांक ओवरफ्लो को ट्रिगर करने के लिए हमें 0xffffffff / size_of_item + 1 संरचनाएं बनानी होंगी। size_of_item हमेशा 8 होता है। इसलिए, हमें 0x20000001 संरचनाएं बनानी होंगी। प्रत्येक संरचना एक प्रक्रिया का प्रतिनिधित्व करती है। इसलिए हमें 0x20000001 (536870913) समवर्ती प्रक्रियाएं रखनी होंगी। मेरी वर्चुअल मशीन पर मैं केवल 12492 समवर्ती प्रक्रियाएं बना पाया और अपनी होस्ट मशीन पर 39652।