CVE-2020-17057 Windows 权限提升漏洞解析

  • A+

CVE-2020-17057 Windows 权限提升漏洞解析


Microsoft has patched a vulnerability I found almost two years ago. The official introduction can be found inMSRC Acknowledgements. It’s a very excellent vulnerability. In this blog post, I will publish the detail of this vulnerability and how I use this vulnerability to get a palette which can read write memory.


DirectComposition component is a Windows kernel-mode graphics component with many syscalls and sub-functions. A main syscall is NtDCompositionProcessChannelBatchBuffer. This syscall can do a lot of operation which include creating resource, releasing resource, setting resource property and so on. To use that syscall, you need a channel which is created from NtDCompositionCreateChannel syscall.

NtDCompositionProcessChannelBatchBuffer support many sub-functions.

enum class DComProcessCommandId : unsigned int{ nCmdProcessCommandBufferIterator, nCmdCreateResource, nCmdOpenSharedResource, nCmdReleaseResource, nCmdGetAnimationTime, nCmdCapturePointer, nCmdOpenSharedResourceHandle, nCmdSetResourceCallbackId, nCmdSetResourceIntegerProperty, nCmdSetResourceFloatProperty, nCmdSetResourceHandleProperty, nCmdSetResourceHandleArrayProperty, nCmdSetResourceBufferProperty, nCmdSetResourceReferenceProperty, nCmdSetResourceReferenceArrayProperty, nCmdSetResourceAnimationProperty, nCmdSetResourceDeletedNotificationTag, nCmdAddVisualChild, nCmdRedirectMouseToHwnd, nCmdSetVisualInputSink, nCmdRemoveVisualChild};

Different sub-function have different structure. Some structures are showed here.

struct CREATE_RESOURCE{ DComProcessCommandId Command; ULONG hResource; ULONG ResourceType; ULONG bShare;};struct SET_BUFFER_PROPERTY{ DComProcessCommandId Command; ULONG hResource; ULONG flag; ULONG BufferSize;};

CVE-2020-17057 Analysis

Crash detail

FAULTING_IP:win32kbase!DirectComposition::CApplicationChannel::ReleaseResource+1cfffffa16`2c238900 ff4a14          dec     dword ptr [rdx+14h]CONTEXT:  ffff9e0af5bc3ea0 -- (.cxr 0xffff9e0af5bc3ea0)rax=0000000000000001 rbx=2929292929292929 rcx=fffffa40082f0ce0rdx=2929292929292929 rsi=0000000000000000 rdi=fffffa40082f0ce0rip=fffffa162c238900 rsp=ffff9e0af5bc48a0 rbp=fffffa40082f0ce0 r8=00000000000000cd  r9=ffff800000000000 r10=ffff9e0af5bc4b00r11=ffff9e0af5bc4780 r12=0000000000000002 r13=ffff9e0af5bc49b1r14=fffffa400c3f0010 r15=fffffa40082f0ce0iopl=0         nv up ei pl nz na pe nccs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00050202win32kbase!DirectComposition::CApplicationChannel::ReleaseResource+0x1c:fffffa16`2c238900 ff4a14          dec     dword ptr [rdx+14h] ds:002b:29292929`2929293d=???????? # Child-SP          RetAddr           Call Site00 ffff9e0a`f5bc2de8 fffff802`5812c9a2 nt!DbgBreakPointWithStatus01 ffff9e0a`f5bc2df0 fffff802`5812bf86 nt!KiBugCheckDebugBreak+0x1202 ffff9e0a`f5bc2e50 fffff802`5800f6a7 nt!KeBugCheck2+0x94603 ffff9e0a`f5bc3560 fffff802`58021569 nt!KeBugCheckEx+0x10704 ffff9e0a`f5bc35a0 fffff802`580209bc nt!KiBugCheckDispatch+0x6905 ffff9e0a`f5bc36e0 fffff802`5801845f nt!KiSystemServiceHandler+0x7c06 ffff9e0a`f5bc3720 fffff802`57e6dd97 nt!RtlpExecuteHandlerForException+0xf07 ffff9e0a`f5bc3750 fffff802`57e6c9a6 nt!RtlDispatchException+0x29708 ffff9e0a`f5bc3e70 fffff802`580216ac nt!KiDispatchException+0x18609 ffff9e0a`f5bc4530 fffff802`5801d3e0 nt!KiExceptionDispatch+0x12c0a ffff9e0a`f5bc4710 fffffa16`2c238900 nt!KiGeneralProtectionFault+0x3200b ffff9e0a`f5bc48a0 fffffa16`2c3f77e0 win32kbase!DirectComposition::CApplicationChannel::ReleaseResource+0x1c0c ffff9e0a`f5bc48d0 fffffa16`2c3f7bfc win32kbase!DirectComposition::CInteractionTrackerMarshaler::ReleaseManipulationReferences+0x5c0d ffff9e0a`f5bc4900 fffffa16`2c237148 win32kbase!DirectComposition::CInteractionTrackerMarshaler::SetBufferProperty+0x36c0e ffff9e0a`f5bc4960 fffffa16`2c236be1 win32kbase!DirectComposition::CApplicationChannel::ProcessCommandBufferIterator+0x4a80f ffff9e0a`f5bc4a20 fffffa16`2d17edfe win32kbase!NtDCompositionProcessChannelBatchBuffer+0x1a110 ffff9e0a`f5bc4ac0 fffff802`58020fb5 win32k!NtDCompositionProcessChannelBatchBuffer+0x1611 ffff9e0a`f5bc4b00 00007fff`d7753724 nt!KiSystemServiceCopyEnd+0x2512 00000026`ad8ffb98 00007ff6`e53c1365 win32u!NtDCompositionProcessChannelBatchBuffer+0x14

Vulnerability detail

The type of this vulnerability is uninitialized pool memory reference.


The allocation of the uninitialized memory is in function with the third argument is 0x15(This value is different in different OS version). The code below shows the allocation which is a resource object pointers table.

v10 = this;  Size = a5; // buffer size  v11 = a3 - 0x15; // the third argument  if ( !v11 )  {    if ( !a4 && *((_DWORD *)this + 0x5A) > 0u ) // a4 is buffer property    {      DirectComposition::CInteractionTrackerMarshaler::ReleaseManipulationReferences(this, a2);      *a6 = 1;      *((_DWORD *)v10 + 4) &= 0xFFFFF7FF;      return (unsigned int)v7;    }    DirectComposition::CInteractionTrackerMarshaler::ReleaseManipulationReferences(this, a2);    v24 = Size >> 3;    if ( (unsigned int)(Size >> 3) )    {      v25 = Win32AllocPoolWithQuota(16i64 * (unsigned int)v24, 0x72694344i64); // memory allocation, not zeroed      v26 = 0;      *((_QWORD *)v10 + 0x2C) = v25;      if ( !v25 )        v26 = 0xC0000017;

Then it process a pair of resource object in one loop. The memory of the second resource object in the pair will not be writen if it breaks out of the loop with the second resource object is not type of 0x57.

v8 = a4;      do // process two resources in one loop      {        if ( v27 >= (unsigned int)v24 )          break;        v28 = v8[2 * v27]; // get first resource handle        v29 = (unsigned int)(v28 - 1);        if ( v28 && v29 < *((_QWORD *)v9 + 0xA) )        {          _mm_lfence();          v30 = *(_QWORD *)(v29 * *((_QWORD *)v9 + 11) + *((_QWORD *)v9 + 7)); // get resource from resource table according to resource handle        }        else        {          v30 = 0i64;        }        if ( v30          && (*(unsigned __int8 (__fastcall **)(__int64, signed __int64))(*(_QWORD *)v30 + 0x60i64))(v30, 0x67i64) ) // check the resource is of type 0x67 or not        {          *(_QWORD *)(*((_QWORD *)v10 + 0x2C) + 16i64 * v27) = v30; // if true, write resource object pointer to the memory previously allocated.          DirectComposition::CResourceMarshaler::AddRef(*(DirectComposition::CResourceMarshaler **)(*((_QWORD *)v10 + 44)                                                                                                  + 16i64 * v27));          ++*((_DWORD *)v10 + 0x5A); // resource pair number        }        else        {          v7 = 0xC000000D;        }        if ( v7 >= 0 )        {          v31 = v8[2 * v27 + 1]; // get second resource handle          if ( v31 )          {            v32 = (unsigned int)(v31 - 1);            if ( v32 >= *((_QWORD *)v9 + 0xA) )            {              v33 = 0i64;            }            else            {              _mm_lfence();              v33 = *(_QWORD *)(v32 * *((_QWORD *)v9 + 0xB) + *((_QWORD *)v9 + 7)); // get resource from resource table according to resource handle            }            if ( v33              && (*(unsigned __int8 (__fastcall **)(__int64, signed __int64))(*(_QWORD *)v33 + 0x60i64))(v33, 0x57i64) ) // check the resource is of type 0x57 or not            {              *(_QWORD *)(*((_QWORD *)v10 + 0x2C) + 16i64 * v27 + 8) = v33;              DirectComposition::CResourceMarshaler::AddRef(*(DirectComposition::CResourceMarshaler **)(*((_QWORD *)v10 + 44) + 16i64 * v27 + 8));            }            else            {              v7 = 0xC000000D; // if false, it will not set the memory to zero            }          }          else          {            *(_QWORD *)(*((_QWORD *)v10 + 44) + 16i64 * v27 + 8) = 0i64;          }        }        ++v27;      }      while ( v7 >= 0 );      v6 = a6;      if ( v7 < 0 )        goto LABEL_55; // goto release function      ....................LABEL_55:  if ( *((_QWORD *)v10 + 44) )    DirectComposition::CInteractionTrackerMarshaler::ReleaseManipulationReferences(v10, v9); // go in here  return (unsigned int)v7;

Finally it will go in


function which will release a pair of resource object in one loop without any checks. The uninitialized object pointers memory will be referenced in 


function. Then the system will BSoD.

void __fastcall DirectComposition::CInteractionTrackerMarshaler::ReleaseManipulationReferences(DirectComposition::CInteractionTrackerMarshaler *this, struct DirectComposition::CApplicationChannel *a2){  DirectComposition::CInteractionTrackerMarshaler *v2; // rdi  DirectComposition::CApplicationChannel *v3; // rbp  __int64 v4; // rcx  unsigned int v5; // esi  v2 = this;  v3 = a2;  v4 = *((_QWORD *)this + 44);  if ( v4 )  {    v5 = 0;    if ( *((_DWORD *)v2 + 90) )    {      do      {        DirectComposition::CApplicationChannel::ReleaseResource(          v3,          *(struct DirectComposition::CResourceMarshaler **)(*((_QWORD *)v2 + 44) + 16i64 * v5));        DirectComposition::CApplicationChannel::ReleaseResource(          v3,          *(struct DirectComposition::CResourceMarshaler **)(*((_QWORD *)v2 + 44) + 16i64 * v5++ + 8)); // this memory is uninitialized, and crash in this function      }      while ( v5 < *((_DWORD *)v2 + 90) ); // *((_DWORD *)v2+90) is resource pair number      v4 = *((_QWORD *)v2 + 44);    }    Win32FreePool(v4);    *((_QWORD *)v2 + 44) = 0i64;    *((_DWORD *)v2 + 90) = 0;    *((_DWORD *)v2 + 91) = 0;  }}


How I convert arbitrary address minus one to arbitrary resource object release

The crash dump shows me that the vulnerability is a arbitrary address minus one vulnerability. But the function logic is to release resource object in a resource object pointer table. So what if we can make a resource object pointer table?

Let's dive into the structure of the CApplicationChannel object. CApplicationChannel object management the lifetime of its resource. Two CLinearObjectTableBase objects stored in CApplicationChannel. One is at this+0x38 and another is at this+0x70.

if ( a4 ) v8 = DirectComposition::CApplicationChannel::CreateInternalSharedResource(this, a3, &v12); else v8 = DirectComposition::CApplicationChannel::CreateInternalResource(this, a3, &v12); v9 = v8; if ( v8 >= 0 ) { v9 = DirectComposition::CLinearObjectTableBase::InsertObject( (DirectComposition::CApplicationChannel *)((char *)v6 + 56), (void *)v12, v5);

v27 = DirectComposition::CLinearObjectTableBase::InsertObject( (DirectComposition::CApplicationChannel *)((char *)v4 + 112), (void *)v11, (unsigned int *)(v11 + 24));

CLinearObjectTableBase hold all the resources which are created by the channel in a resource object pointer table. The InsertObject function will write resource pointer at certain index in the table. This is exactly what we want.

  1. We spray 0x400 resources which will lead two 0x2000 bytes object pointer tables in CLinearObjectTableBase object.
  2. If we create one more resource, two 0x2000 bytes object pointer tables will be freed and two new 0x4000 bytes object pointer tables will be allocated. Now we have two 0x2000 bytes memory which is full of resource object pointer.
  3. We trigger the vulnerability with buffer size is 0x1000. The 0x2000 bytes resource object pointer table just freed will be allocated. And a resource object at certain postion in resource object pointer table will be freed.

For now we achieve arbitrary resource object free.

How I get a palette with dangling data pointer

Look into CInteractionTrackerMarshaler::SetBufferProperty function with flag 0x41, there is a CDCompDynmicArrayBase object in CInteractionTrackerMarshaler also. We can use it to spray a large size memory and then free it.

if ( Size == 0xC ) { v17 = (_QWORD *)((char *)this + 368); v18 = *((_DWORD *)this + 98); v19 = *((_DWORD *)a4 + 2); Src = *(_QWORD *)a4; v36 = v19; v7 = DirectComposition::CDCompDynamicArrayBase::Grow( (DirectComposition::CInteractionTrackerMarshaler *)((char *)this + 368), 1i64, 0x72694344i64); if ( v7 >= 0 ) { memmove((void *)(*v17 + v17[4] * v18), &Src, v17[4]); *a6 = 1; }

  1. We use palette to do pool fengshui to make 0xc000 bytes hole in memory.
  2. We call CInteractionTrackerMarshaler::SetBufferProperty function with flag 0x41 to create a 0xc000 memory allocation which will locate in pool fengshui hole.
  3. We trigger bug to free this CInteractionTrackerMarshaler resource object.
  4. Spray some palette to allocate the memory we just freed.
  5. We call CInteractionTrackerMarshaler::SetBufferProperty function with flag 0x41 one more time to free the palette data. Then we have a palette with a dangling data pointer. We can manipulate that freed memory by calling SetPaletteEntries and GetPaletteEntries.


To exploit this vulnerability takes a lot of days and nights. Hope this post is useful to you.

CVE-2020-17057 Windows 权限提升漏洞解析

本文始发于微信公众号(360BugCloud):CVE-2020-17057 Windows 权限提升漏洞解析


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: