CVE-2021-26900 Windows Win32k提权漏洞分析

robots

 

前言

2021年3月,微软发布了一个补丁来修正Windows内核中的一个漏洞。该错误可能允许攻击者以升级的权限执行代码。该漏洞是由THEORI的安全研究员JeongOh Kyea向ZDI项目报告的。本文详细介绍了CVE-2021-26900的原理,以及它是如何绕过CVE-2020-1381在2020年7月的修复。

 

DirectComposition

DirectComposition是Windows中的一个组件,在Windows 8中被添加,它能够有效地支持图像转换和动画等图形效果。360Vulcan在CanSecWest 2017上发表了关于寻找DirectComposition漏洞的演讲—Win32k Dark Composition [PDF]。

DirectComposition可以通过以NtDComposition开头的win32k系统调用进行访问。在Windows 10 RS1之前,调用者为每个动作进行单独的系统调用,如创建或释放资源。在Windows 10 RS1之后,被合并到NtDCompositionProcessChannelBatchBuffer这一个系统调用中,它以批处理模式处理几个命令。360Vulcan在CanSecWest 2017上发表的工作对这个函数进行了Fuzz测试,以找到漏洞。此后,与DirectComposition有关的许多漏洞被发现,包括一个Pwn2Own的漏洞:CVE-2020-1382

触发DirectComposition漏洞时有三个必要的系统调用:

  • NtDCompositionCreateChannel
  • NtDCompositionProcessChannelBatchBuffer
  • NtDCompositionCommitChannel

要创建DirectComposition对象,调用者必须首先使用NtDCompositionCreateChannel系统调用创建一个通道。

// Create Channel 
HANDLE hChannel; 
PVOID pMappedAddress = NULL;  SIZE_T SectionSize = 0x4000; 
DWORD dwArg1, dwArg2; 
NtDCompositionCreateChannel(&hChannel, &SectionSize, &pMappedAddress); // Data is Transferred through pMappedAddress

创建通道之后,可以使用NtDCompositionProcessChannelBatchBuffer系统调用发送几个命令。每个命令都有自己的格式,且大小不同。

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来轻松地获得资源类型编号。

// 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用于设置目标对象的数据。该命令的大学和个数取决于资源类型。

*(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字节,格式如下。

*(DWORD*)(pMappedAddress) = ReleaseResource; 
*(HANDLE*)(pMappedAddress + 4) = 1; // Resource ID 
ntStatus = NtDCompositionProcessChannelBatchBuffer(hChannel, 8, &dwArg1, &dwArg2);

NtDCompositionCommitChannel系统调用将这些命令序列化后,通过本地过程调用(LPC)协议发送到桌面窗口管理器(dwm.exe)。在从内核接收到命令之后,桌面窗口管理器(dwm.exe)将把这些命令呈现到屏幕上。

 

关于漏洞

CVE-2021-26900涉及CInteractionTrackerBindingManagerMarshalerCInteractionTrackerMarshaler。此漏洞与CVE-2020-1381非常相似,因此在讨论CVE-2021-26900之前,我们将先详细解释CVE-2020-1381。

 

CVE-2020-1381

CVE-2020-1381修复补丁于2020年7月发布。该漏洞发生在DirectComposition::CInteractionTrackerBindingManagerMarshaler::SetBufferProperty函数中,该函数是CInteractionTrackerBindingManagerMarshaler对象中SetResourceBufferProperty命令的处理程序。

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命令的数据。数据由三个DWORDs组成:resource1_idresource2_idnew_entry_id

这个函数首先从用户指定的resource1_idresource2_id中检索资源[1]。然后检查每个资源的类型是否为0x58,这是CInteractionTrackerMarshaler的资源类型[2]。

接下来,这对CInteractionTrackerMarshaler资源被附加到CInteractionTrackerBindingManagerMarshaler对象的跟踪器列表中。如名称所示,这两种对象类型CInteractionTrackerMarshalerCInteractionTrackerBindingManagerMarshaler是相互关联的。CInteractionTrackerBindingManagerMarshaler对象保存了CInteractionTrackerMarshaler对象对的列表,并且每个CInteractionTrackerMarshaler对象都有一个返回到CInteractionTrackerBindingManagerMarshaler对象的指针。

DirectComposition:: cinteractiontrackbindingmanagermarshaler::SetBufferProperty函数第一次被调用时,跟踪器对被添加到列表中,因为列表是空的。

// 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]。然后,它使用DirectComposition::CInteractionTrackerMarshaler::SetBindingManagerMarshaler函数设置每个CInteractionTrackerMarshaler对象到CInteractionTrackerBindingManagerMarshaler对象的引用[4]。

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函数。

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); 
}

如果CInteractionTrackerBindingManagerMarshaler对象有一个与CInteractionTrackerBindingManagerMarshaler对象的绑定,则调用DirectComposition:: CInteractionTrackerBindingManagerMarshaler::RemoveTrackerBindings来删除相应的跟踪条目。

DirectComposition:: CInteractionTrackerBindingManagerMarshaler::RemoveTrackerBindings中,如果条目中的两个跟踪器对象中有一个具有与被删除对象匹配的资源id,那么该条目的entry_id将被设置为0。最后,它调用DirectComposition:: CInteractionTrackerBindingManagerMarshaler:: cleanuplisttitemspendingdeletion来清除那些entry_id等于0的条目。

然而,如果一个单独的CInteractionTrackerMarshaler被添加到多个cinteractiontrackbindingmanagermarshaler跟踪列表会发生什么?因为在添加新条目时没有检查,已经绑定到CInteractionTrackerBindingManagerMarshaler对象的CInteractionTrackerMarshaler对象可以绑定到第二个CInteractionTrackerBindingManagerMarshaler对象。

下图显示了这种情况:

在这种情况下,如果Tracker1被释放,只有TrackBindingB中的条目被删除,因为Tracker1被绑定到TrackBindingB。最终,TrackBindingB对象的条目具有已释放的对象指针。

这个悬空对象指针稍后会在DirectComposition:: CInteractionTrackerBindingManagerMarshaler:: EmitBoundTrackerMarshalerUpdateCommands函数中解引用,该函数可以通过ntdcompontioncommitchannel系统调用来触发。这个系统调用在批处理命令的序列化期间引用资源。

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; 
    } 
  } 
}

上面显示的函数为tracker_list中的对象调用EmitUpdateCommands方法。被释放的对象将在进程中被引用,这导致了一个“释放后再使用”的漏洞。

 

CVE-2021-26900

CVE-2021-26900将绕过CVE-2020-1381的补丁重新触发上述漏洞。CVE-2020-1381的补丁如下所示。

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 
      } 
    } 
  } 
  // ... 
}

标有[*]为新增的部分,是用来检查CInteractionTrackerMarshaler对象的binding_obj。它检查CInteractionTrackerMarshaler是否已经绑定到另一个CInteractionTrackerBindingManagerMarshaler

然而,这个补丁可以通过更新跟踪器条目绕过。让我们看看更新跟踪器条目的代码:

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为0时,CInteractionTrackerBindingManagerMarshaler对象将此条目视为不必要的。为了处理这个条目,它调用DirectComposition:: CInteractionTrackerBindingManagerMarshaler::RemoveBindingManagerReferenceFromTrackerIfNecessary函数。但是这个函数不会删除这个条目,它只删除绑定。

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); 
  } 
}

上面的函数尝试寻找资源id为tracker1_idtracker2_id的条目。如果没有其他资源id为tracker1_idtracker2_id的条目,这意味着这两个对象不必相互引用。因此,DirectComposition::CInteractionTrackerMarshaler::SetBindingManagerMarshaler函数被一个NULL绑定对象调用,以移除CInteractionTrackerMarshaler对象的绑定。

然而,尽管从CInteractionTrackerMarshalercinteractiontrackbindingmanagermarshaler的绑定被移除,tracker1tracker2的指针仍然留在跟踪器列表中。用零值new_entry_id更新条目会产生如下所示的状态:

现在,CInteractionTrackerMarshaler对象的binding_obj被设置为零,这可以绕过CVE-2020-1381的补丁。如果我们将tracker1绑定到另一个CInteractionTrackerBindingManagerMarshaler对象,状态会发生如下变化。

接下来,将TrackerBindingA中的entry_id更新为非零值将产生与CVE-2020-1381中相同的状态。

 

修复

该补丁应用于win32kbase.sys以修复CVE-2021-26900,如下:

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中时,该字段用于正确地释放对象。

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函数中被删除。

这里可以找到此漏洞的POC

(完)