diff --git a/_posts/2021-08-15-DirectComposition.md b/_posts/2021-08-15-DirectComposition.md new file mode 100644 index 000000000..9230983f6 --- /dev/null +++ b/_posts/2021-08-15-DirectComposition.md @@ -0,0 +1,235 @@ +--- +layout: post +toc: true +title: "Analysis of DirectComposition Binding and Tracker object vulnerability" +tags: [windows, kernel, DirectComposition] +author: + - iamelli0t +--- + +## DirectComposition introduction +Microsoft DirectComposition is a Windows component that enables high-performance bitmap composition with transforms, effects, and animations. Application developers can use the DirectComposition API to create visually engaging user interfaces that feature rich and fluid animated transitions from one visual to another.[1]
+ +DirectComposition API provides COM interface via dcomp.dll, calls win32kbase.sys through win32u.dll export function, and finally sends data to client program dwm.exe (Desktop Window Manager) through ALPC to complete the graphics rendering operation:
+![avatar](/images/DirectComposition/1.png)

+ +win32u.dll (Windows 10 1909) provides the following export functions to handle DirectComposition API:
+![avatar](/images/DirectComposition/2.png)

+ +The three functions related to trigger vulnerability are: NtDCompositionCreateChannel,NtDCompositionProcessChannelBatchBuffer and NtDCompositionCommitChannel:
+(1) **NtDCompositionCreateChannel** creates a channel to communicate with the kernel:
+```cpp +typedef NTSTATUS(*pNtDCompositionCreateChannel)( + OUT PHANDLE hChannel, + IN OUT PSIZE_T pSectionSize, + OUT PVOID* pMappedAddress + ); +``` + +(2) **NtDCompositionProcessChannelBatchBuffer** batches multiple commands:
+```cpp +typedef NTSTATUS(*pNtDCompositionProcessChannelBatchBuffer)( + IN HANDLE hChannel, + IN DWORD dwArgStart, + OUT PDWORD pOutArg1, + OUT PDWORD pOutArg2 + ); +``` + +The batched commands are stored in the pMappedAddress memory returned by NtDCompositionCreateChannel. The command list is as follows:
+```cpp +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 +}; +``` +The commands related to trigger vulnerability are: **CreateResource**, **SetResourceBufferProperty**, **ReleaseResource**. The data structure of different commands is different:
+![avatar](/images/DirectComposition/3.png)

+ +(3) **NtDCompositionCommitChannel** serializes batch commands and sends them to dwm.exe for rendering through ALPC:
+```cpp +typedef NTSTATUS(*pNtDCompositionCommitChannel)( + IN HANDLE hChannel, + OUT PDWORD out1, + OUT PDWORD out2, + IN DWORD flag, + IN HANDLE Object + ); +``` +
+ +## CInteractionTrackerBindingManagerMarshaler::SetBufferProperty process analysis +First use **CreateResource** command to create **CInteractionTrackerBindingManagerMarshaler** resource (ResourceType = 0x59, hereinafter referred to as "Binding") and **CInteractionTrackerMarshaler** resource (ResourceType = 0x58, hereinafter referred to as "Tracker").
+Then call the **SetResourceBufferProperty** command to set the Tracker object to the Binding object's BufferProperty.
This process is handled by the function **CInteractionTrackerBindingManagerMarshaler::SetBufferProperty**, which main process is as follows:
+![avatar](/images/DirectComposition/4.png)

+ +The key steps are as follows:
+(1) Check the input buffer subcmd == 0 && bufsize == 0xc
+(2) Get Tracker objects tracker1 and tracker2 from channel-> resource_list (+0x38) according to the resourceId in the input buffer
+(3) Check whether the types of tracker1 and tracker2 are CInteractionTrackerMarshaler (0x58)
+(4) If binding->entry_count (+0x50) > 0, find the matched TrackerEntry from binding->tracker_list (+0x38) according to the handleID of tracker1 and tracker2, then update TrackerEntry->entry_id to the new_entry_id from the input buffer
+(5) Otherwise, create a new TrackerEntry structure. If tracker1->binding == NULL || tracker2->binding == NULL, update their binding objects

+ +After SetBufferProperty, a reference relationship between the binding object and the tracker object is as follows:
+![avatar](/images/DirectComposition/5.png)

+ +When use **ReleaseResource** command to release the Tracker object, the **CInteractionTrackerMarshaler::ReleaseAllReferencescalled** function is called. ReleaseAllReferences checks whether the tracker object has a binding object internally:
+![avatar](/images/DirectComposition/6.png)

+ +If it has:
+(1) Call **RemoveTrackerBindings**. In RemoveTrackerBindings, TrackerEntry.entry_id is set to 0 if the resourceID in tracker_list is equal to the resourceID of the freed tracker. Then call **CleanUpListItemsPendingDeletion** to delete the TrackerEntry which entry_id=0 in tracker_list:
+![avatar](/images/DirectComposition/7.png)

+(2) Call **ReleaseResource** to set refcnt of the binding object minus 1.
+![avatar](/images/DirectComposition/8.png)

+(3) tracker->binding (+0x190) = 0

+ +According to the above process, the input command buffer to construct a normal SetBufferProperty process is as follows:
+![avatar](/images/DirectComposition/9.png)

+ +After SetBufferProperty, the memory layout of binding1 and tacker1, tracker2 objects is as follows:
+![avatar](/images/DirectComposition/10.png)

+ +After ReleaseResource tracker2, the memory layout of binding1, tacker1, and tracker2 objects is as follows:
+![avatar](/images/DirectComposition/11.png)

+ + +## CVE-2020-1381 +Retrospective the process of **CInteractionTrackerBindingManagerMarshaler::SetBufferProperty**, when new_entry_id != 0, a new TrackerEntry structure will be created:
+![avatar](/images/DirectComposition/12.png)

+ + +If tracker1 and tracker2 have been bound to binding1 already, after binding tracker1 and tracker2 to binding2, a new TrackerEntry structure will be created for binding2. Since tracker->binding != NULL at this time, tracker->binding will still save binding1 pointer and will not be updated to binding2 pointer. When the tracker is released, binding2->entry_list will retain the tracker's dangling pointer.

+Construct an input command buffer which can trigger the vulnerability as follows:
+![avatar](/images/DirectComposition/13.png)

+ +Memory layout after ReleaseResource tracker1:
+![avatar](/images/DirectComposition/14.png)

+ +It can be seen that after ReleaseResource tracker1, binding2->track_list[0] saves the dangling pointer of tracker1.

+ +## CVE-2021-26900 +According to analyze the branch of 'the new_entry_id != 0', the root cause of CVE-2020-1381 is when creating TrackerEntry, it didn't check the tracker object which has been bound to the binding object. The patch adds a check for tracker->binding when creating TrackerEntry:
+![avatar](/images/DirectComposition/15.png)

+ +CVE-2021-26900 is a bypass of the CVE-2020-1381 patch. The key point to bypass the patch is if the condition of tracker->binding==NULL can be constructed after the tracker is bound to the binding object.
+The way to bypass is in the 'update TrackerEntry' branch:
+![avatar](/images/DirectComposition/16.png)

+ +When TrackerEntry->entry_id == 0, **RemoveBindingManagerReferenceFromTrackerIfNecessary** function is called. It checks if entry_id==0 internally, then call **SetBindingManagerMarshaler** to set tracker->binding=NULL:
+![avatar](/images/DirectComposition/17.png)

+ + +Therefore, by setting entry_id=0 manually, the status of tracker->binding == NULL can be obtained, which can be used to bypass the CVE-2020-1381 patch.

+Construct an input command buffer which can trigger the vulnerability as follows:
+![avatar](/images/DirectComposition/18.png)

+ +After setting entry_id=0 manually, the memory layout of binding1 and tracker1:
+![avatar](/images/DirectComposition/19.png)

+ +At this time, binding1->TrackerEntry still saves the pointer of tracker1, but tracker1->binding = NULL. Memory layout after ReleaseResource tracker1: +![avatar](/images/DirectComposition/20.png)

+ +It can be seen that after ReleaseResource tracker1, binding1->track_list[0] saves the dangling pointer of tracker1.

+ +## CVE-2021-26868 +Retrospective the method in CVE-2021-26900 which sets entry_id=0 manually to get tracker->binding == NULL status to bypass the CVE-2020-1381 patch and the process of **CInteractionTrackerMarshaler::ReleaseAllReferences:ReleaseAllReferences** which checks that if the tracker object has a binding object, and then deletes the corresponding TrackerEntry.

+So when entry_id is set to 0 manually, tracker->binding will be set to NULL. When the tracker object is released via ReleaseResource command, the TrackerEntry saved by the binding object will not be deleted, then a dangling pointer of the tracker object will be obtained again.

+Construct an input command buffer which can trigger the vulnerability as follows:
+![avatar](/images/DirectComposition/21.png)

+ +Memory layout after ReleaseResource tracker1:
+![avatar](/images/DirectComposition/22.png)

+ +It can be seen that after ReleaseResource tracker1, binding1->track_list[0] saves the dangling pointer of tracker1.

+ +## CVE-2021-33739 +CVE-2021-33739 is different from the vulnerability in win32kbase.sys introduced in previous sections. It is a UAF vulnerability in dwmcore.dll of the dwm.exe process. The root cause is in ReleaseResource phase of CloseChannel. In **CInteractionTrackerBindingManager::RemoveTrackerBinding** function call, when the element Binding->hashmap(+0x40) is deleted, the hashmap is accessed directly without checking whether the Binding object is released, which causes the UAF vulnerability.

+ +Construct an input command buffer which can trigger the vulnerability as follows:
+![avatar](/images/DirectComposition/23.png)

+ +According to the previous analysis, in the normal scenario of **CInteractionTrackerBindingManagerMarshaler::SetBufferProperty** function call, the Binding object should be bound with two different Tracker objects. However, if it is bound with the same Tracker object:

+(1) **Binding phase**:
+**CInteractionTrackerBindingManager::ProcessSetTrackerBindingMode** function is called to process binding opreation, which calls **CInteractionTrackerBindingManager::AddOrUpdateTrackerBindings** function internally to update Tracker->Binding (+0x278). When Tracker has already be bound with the current Binding object, the binding operation will not be repeated:
+![avatar](/images/DirectComposition/24.png)

+ +Therefore, if the same Tracker object is bound already, the Binding object will not be bound again, then the refcnt of the Binding object will only be increased by 1 finally: +![avatar](/images/DirectComposition/25.png)

+ +(2) **Release phase**:
+After PreRender is finished, **CComposition::CloseChannel** will be called to close the Channel and release the Resource in the Resource HandleTable. The Binding object will be released firstly, at this time Binding->refcnt = 1:
+![avatar](/images/DirectComposition/26.png)

+ +Then the Tracker object will be released. **CInteractionTrackerBindingManager::RemoveTrackerBindings** will be called to release Tracker->Binding:
+![avatar](/images/DirectComposition/27.png)

+ +Three steps are included:
+(1) Get the Tracker object from the TrackerEntry
+(2) Erase the corresponding Tracker pointer from Binding->hashmap (+0x40)
+(3) Remove Tracker->Binding (Binding->refcnt --) from the Tracker object

+ +The key problem is: After completing the cleanup of the first Tracker object in TrackerEntry, the Binding object may be released already. When the second Tracker object is prepared to be cleared, because the Binding object has been released, the validity of the Binding object does not be checked before the Binding->hashmap is accessed again, which result in an access vialation exception:
+![avatar](/images/DirectComposition/28.png)

+ +## Exploitation: Another way to occupy freed memory +For the kernel object UAF exploitation, according to the publicly available exploit samples[5], the Palette object is used to occupy the freed memory:
+![avatar](/images/DirectComposition/29.png)

+ +Use **CInteractionTrackerBindingManagerMarshaler::EmitBoundTrackerMarshalerUpdateCommands** function to access the placeholder objects:
+![avatar](/images/DirectComposition/30.png)

+ +The red box here is the virtual function **CInteractionTrackerMarshaler::EmitUpdateCommands** of tracker1, tracker2 object (vtable + 0x50). Because the freed Tracker object has been reused by Palette, the program execution flow hijacking is achieved by forging a virtual table and writing other function pointers to fake Tracker vtable+0x50.
+The sample selects **nt!SeSetAccessStateGenericMapping**:
+![avatar](/images/DirectComposition/31.png)

+ +With the 16-byte write capability of nt!SeSetAccessStateGenericMapping, it modifies _KTHREAD->PreviousMode = 0 to inject shellcode into Winlogon process to complete the privilege escalation.

+ +**Another way to occupy freed memory**
+ +The exploitation of Palette object is relatively common, is there some object with user-mode controllable memory size in the DirectComposition component can be exploited?
+ +The Binding object and Tracker object we discussed before are belonged to the Resource of DirectComposition. DirectComposition contains many Resource objects, which are created by **DirectComposition::CApplicationChannel::CreateInternalResource**:
+![avatar](/images/DirectComposition/32.png)

+ +Each Resource has a BufferProperty, which is set by **SetResourceBufferProperty** command. So our object is to find one Resource which can be used to allocate a user-mode controllable memory size through the **SetResourceBufferProperty** command. Through searching, I found **CTableTransferEffectMarshaler::SetBufferProperty**. The command format is as follows:
+![avatar](/images/DirectComposition/33.png)

+ +When subcmd==0, the bufferProperty is stored at CTableTransferEffectMarshaler+0x58. The size of the bufferProperty is set by the user-mode input bufferSize, and the content is copied from the user-mode input buffer:
+![avatar](/images/DirectComposition/34.png)

+ +Modify the original sample and use the propertyBuffer of CTableTransferEffectMarshaler to occupy freed memory:
+![avatar](/images/DirectComposition/35.png)
+![avatar](/images/DirectComposition/36.png)

+ +By debugging, we can see that the propertyBuffer of CTableTransferEffectMarshaler occupies the freed memory successfully:
+![avatar](/images/DirectComposition/37.png)

+ +Finally, successful exploitation screenshot:
+![avatar](/images/DirectComposition/38.png)

+ +## References +[1] https://docs.microsoft.com/en-us/windows/win32/directcomp/directcomposition-portal
+[2] https://www.zerodayinitiative.com/blog/2021/5/3/cve-2021-26900-privilege-escalation-via-a-use-after-free-vulnerability-in-win32k
+[3] https://github.com/thezdi/PoC/blob/master/CVE-2021-26900/CVE-2021-26900.c
+[4] https://ti.dbappsecurity.com.cn/blog/articles/2021/06/09/0day-cve-2021-33739/
+[5] https://github.com/Lagal1990/CVE-2021-33739-POC
diff --git a/images/DirectComposition/1.png b/images/DirectComposition/1.png new file mode 100644 index 000000000..1db1172cb Binary files /dev/null and b/images/DirectComposition/1.png differ diff --git a/images/DirectComposition/10.png b/images/DirectComposition/10.png new file mode 100644 index 000000000..45ded432a Binary files /dev/null and b/images/DirectComposition/10.png differ diff --git a/images/DirectComposition/11.png b/images/DirectComposition/11.png new file mode 100644 index 000000000..283560a4c Binary files /dev/null and b/images/DirectComposition/11.png differ diff --git a/images/DirectComposition/12.png b/images/DirectComposition/12.png new file mode 100644 index 000000000..75f9bbf34 Binary files /dev/null and b/images/DirectComposition/12.png differ diff --git a/images/DirectComposition/13.png b/images/DirectComposition/13.png new file mode 100644 index 000000000..a0175c070 Binary files /dev/null and b/images/DirectComposition/13.png differ diff --git a/images/DirectComposition/14.png b/images/DirectComposition/14.png new file mode 100644 index 000000000..0d79ff7cb Binary files /dev/null and b/images/DirectComposition/14.png differ diff --git a/images/DirectComposition/15.png b/images/DirectComposition/15.png new file mode 100644 index 000000000..7f7faeb15 Binary files /dev/null and b/images/DirectComposition/15.png differ diff --git a/images/DirectComposition/16.png b/images/DirectComposition/16.png new file mode 100644 index 000000000..7d08eb0b1 Binary files /dev/null and b/images/DirectComposition/16.png differ diff --git a/images/DirectComposition/17.png b/images/DirectComposition/17.png new file mode 100644 index 000000000..167fa2bfa Binary files /dev/null and b/images/DirectComposition/17.png differ diff --git a/images/DirectComposition/18.png b/images/DirectComposition/18.png new file mode 100644 index 000000000..d2ec3503e Binary files /dev/null and b/images/DirectComposition/18.png differ diff --git a/images/DirectComposition/19.png b/images/DirectComposition/19.png new file mode 100644 index 000000000..d2b20a6ec Binary files /dev/null and b/images/DirectComposition/19.png differ diff --git a/images/DirectComposition/2.png b/images/DirectComposition/2.png new file mode 100644 index 000000000..e21551bf8 Binary files /dev/null and b/images/DirectComposition/2.png differ diff --git a/images/DirectComposition/20.png b/images/DirectComposition/20.png new file mode 100644 index 000000000..2d0a72623 Binary files /dev/null and b/images/DirectComposition/20.png differ diff --git a/images/DirectComposition/21.png b/images/DirectComposition/21.png new file mode 100644 index 000000000..8ddff4133 Binary files /dev/null and b/images/DirectComposition/21.png differ diff --git a/images/DirectComposition/22.png b/images/DirectComposition/22.png new file mode 100644 index 000000000..14c284746 Binary files /dev/null and b/images/DirectComposition/22.png differ diff --git a/images/DirectComposition/23.png b/images/DirectComposition/23.png new file mode 100644 index 000000000..9913c170d Binary files /dev/null and b/images/DirectComposition/23.png differ diff --git a/images/DirectComposition/24.png b/images/DirectComposition/24.png new file mode 100644 index 000000000..da9028c3a Binary files /dev/null and b/images/DirectComposition/24.png differ diff --git a/images/DirectComposition/25.png b/images/DirectComposition/25.png new file mode 100644 index 000000000..0d79e3174 Binary files /dev/null and b/images/DirectComposition/25.png differ diff --git a/images/DirectComposition/26.png b/images/DirectComposition/26.png new file mode 100644 index 000000000..2e130dd5a Binary files /dev/null and b/images/DirectComposition/26.png differ diff --git a/images/DirectComposition/27.png b/images/DirectComposition/27.png new file mode 100644 index 000000000..dd46e2e33 Binary files /dev/null and b/images/DirectComposition/27.png differ diff --git a/images/DirectComposition/28.png b/images/DirectComposition/28.png new file mode 100644 index 000000000..ba805eec3 Binary files /dev/null and b/images/DirectComposition/28.png differ diff --git a/images/DirectComposition/29.png b/images/DirectComposition/29.png new file mode 100644 index 000000000..336c8451e Binary files /dev/null and b/images/DirectComposition/29.png differ diff --git a/images/DirectComposition/3.png b/images/DirectComposition/3.png new file mode 100644 index 000000000..7d4fb2ed1 Binary files /dev/null and b/images/DirectComposition/3.png differ diff --git a/images/DirectComposition/30.png b/images/DirectComposition/30.png new file mode 100644 index 000000000..421ebcc19 Binary files /dev/null and b/images/DirectComposition/30.png differ diff --git a/images/DirectComposition/31.png b/images/DirectComposition/31.png new file mode 100644 index 000000000..6e54ce132 Binary files /dev/null and b/images/DirectComposition/31.png differ diff --git a/images/DirectComposition/32.png b/images/DirectComposition/32.png new file mode 100644 index 000000000..278e73dcf Binary files /dev/null and b/images/DirectComposition/32.png differ diff --git a/images/DirectComposition/33.png b/images/DirectComposition/33.png new file mode 100644 index 000000000..6b3970ce5 Binary files /dev/null and b/images/DirectComposition/33.png differ diff --git a/images/DirectComposition/34.png b/images/DirectComposition/34.png new file mode 100644 index 000000000..475081a94 Binary files /dev/null and b/images/DirectComposition/34.png differ diff --git a/images/DirectComposition/35.png b/images/DirectComposition/35.png new file mode 100644 index 000000000..877b1ec32 Binary files /dev/null and b/images/DirectComposition/35.png differ diff --git a/images/DirectComposition/36.png b/images/DirectComposition/36.png new file mode 100644 index 000000000..0ba37948f Binary files /dev/null and b/images/DirectComposition/36.png differ diff --git a/images/DirectComposition/37.png b/images/DirectComposition/37.png new file mode 100644 index 000000000..538105b5f Binary files /dev/null and b/images/DirectComposition/37.png differ diff --git a/images/DirectComposition/38.png b/images/DirectComposition/38.png new file mode 100644 index 000000000..a44125930 Binary files /dev/null and b/images/DirectComposition/38.png differ diff --git a/images/DirectComposition/4.png b/images/DirectComposition/4.png new file mode 100644 index 000000000..d64e3ac8f Binary files /dev/null and b/images/DirectComposition/4.png differ diff --git a/images/DirectComposition/5.png b/images/DirectComposition/5.png new file mode 100644 index 000000000..b17937263 Binary files /dev/null and b/images/DirectComposition/5.png differ diff --git a/images/DirectComposition/6.png b/images/DirectComposition/6.png new file mode 100644 index 000000000..3e9bcdfb5 Binary files /dev/null and b/images/DirectComposition/6.png differ diff --git a/images/DirectComposition/7.png b/images/DirectComposition/7.png new file mode 100644 index 000000000..0288497b3 Binary files /dev/null and b/images/DirectComposition/7.png differ diff --git a/images/DirectComposition/8.png b/images/DirectComposition/8.png new file mode 100644 index 000000000..c7f260737 Binary files /dev/null and b/images/DirectComposition/8.png differ diff --git a/images/DirectComposition/9.png b/images/DirectComposition/9.png new file mode 100644 index 000000000..f83739e6c Binary files /dev/null and b/images/DirectComposition/9.png differ