Talos Vulnerability Report

TALOS-2022-1515

Microsoft DirectComposition GetWeakReferenceBase null pointer dereference vulnerability

August 16, 2022
CVE Number

CVE-2022-40733

SUMMARY

An access violation vulnerability exists in the DirectComposition functionality win32kbase.sys driver version 10.0.22000.593 as part of Windows 11 version 22000.593 and version 10.0.20348.643 as part of Windows Server 2022 version 20348.643. A specially-crafted set of syscalls can lead to a reboot. An unprivileged user can run specially-crafted code to trigger Denial Of Service.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

Microsoft Windows Build 22000.593

PRODUCT URLS

win32kbase.sys - https://www.microsoft.com Windows - https://www.microsoft.com/en-us/windows/

CVSSv3 SCORE

5.0 - CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H

CWE

CWE-476 - NULL Pointer Dereference

DETAILS

Microsoft DirectComposition is a Windows component that enables high-performance bitmap composition with transforms, effects, and animations.

DirectComposition has support for creating weak references to its resource marshalers.

DirectComposition::CApplicationChannel::GetWeakReferenceBase(...) takes pointer to a resource marshaler as an argument and returns a weak reference to it. It either creates a new kernel object of type CWeakReferenceBase or returns an existing one if it was created previously. References to these weak reference objects are stored inside a table in the CApplicationChannel object.

When resource marshalers want to store references to other resource marshalers, CWeakRefereceBase pointer is stored rather than storing actual raw pointers.

After inserting a newly-created WeakReferenceBase object to the table, kernel does bitwise-OR 0x4 with status flag at offset +10h of the resource marshaler to indicate that there is a weak reference to this resource marshaler.

.text:00000001C00B5110                 call    cs:__imp_RtlInsertElementGenericTable
.text:00000001C00B5117                 nop     dword ptr [rax+rax+00h]
.text:00000001C00B511C                 test    rax, rax
.text:00000001C00B511F                 jz      loc_1C0146C87
.text:00000001C00B5125                 xor     edi, edi
.text:00000001C00B5127                 or      dword ptr [rsi+10h], 4  ; Updating the status flag

Later when releasing this resource marshaler, it checks the status flag to see if weak references to this exists and clears the weak reference like following.

.text:00000001C0059A33                 mov     eax, [rbx+10h]
.text:00000001C0059A36                 test    al, 4           ; Check if weak reference to this resource marshaler exists
.text:00000001C0059A38                 jnz     loc_1C0059BC2

.text:00000001C0059BC2 loc_1C0059BC2:                          ; CODE XREF: DirectComposition::CApplicationChannel::ReleaseResource(DirectComposition::CResourceMarshaler *)+54↑j
.text:00000001C0059BC2                 lea     rcx, [rdi+108h] ; Table
.text:00000001C0059BC9                 mov     rdx, rbx
.text:00000001C0059BCC                 call    ?RemoveObject@?$CGenericTable@PEAVCResourceMarshaler@DirectComposition@@VCWeakReferenceBase@2@$0HEHHEDEE@$00@DirectComposition@@QEAAPEAVCWeakReferenceBase@2@PEAVCResourceMarshaler@2@@Z ; DirectComposition::CGenericTable<DirectComposition::CResourceMarshaler *,DirectComposition::CWeakReferenceBase,1953973060,1>::RemoveObject(DirectComposition::CResourceMarshaler *)
.text:00000001C0059BD1                 and     dword ptr [rbx+10h], 0FFFFFFFBh  ; Update status of the releasing resource marshaler
.text:00000001C0059BD5                 and     qword ptr [rax+10h], 0           ; Clears pointer to the resource marshaler saved in the weak reference object
.text:00000001C0059BDA                 jmp     loc_1C0059A3E

First it removes a table element, which stores reference to CWeakReferenceBase of releasing resource marshaler, and updates its status flag. Then it clears raw pointer to the releasing resource marshaler, which is stored inside the weak reference object so that other resource marshalers, which have weak reference to this, don’t try to dereference after it is freed.

Although bit 2 in the status flag is used for weak references only, there is code which wrongly sets this bit while setting a float property to some types of resource marshalers. Resource marshalers are effected by this as follows:

DirectComposition::CProjectedShadowSceneMarshaler::SetFloatProperty
DirectComposition::CKeyframeAnimationMarshaler::SetFloatProperty
DirectComposition::CVisualMarshaler::SetFloatProperty
DirectComposition::CNineGridBrushMarshaler::SetFloatProperty
DirectComposition::CCompositionSpotLightMarshaler::SetFloatProperty
DirectComposition::CSaturationEffectMarshaler::SetFloatProperty
DirectComposition::CArithmeticCompositeEffectMarshaler::SetFloatProperty
DirectComposition::CShadowEffectMarshaler::SetFloatProperty
DirectComposition::CLinearTransferEffectMarshaler::SetFloatProperty
DirectComposition::CTableTransferEffectMarshaler::SetFloatProperty
DirectComposition::CAffineTransform2DEffectMarshaler::SetFloatProperty
DirectComposition::CColorGradientStopMarshaler::SetFloatProperty
DirectComposition::CSpriteShapeMarshaler::SetFloatProperty
DirectComposition::CGeometryMarshaler::SetFloatProperty
DirectComposition::CAnimationLoggingManagerMarshaler::SetFloatProperty
DirectComposition::CInteractionTrackerMarshaler::SetFloatProperty
DirectComposition::CDropShadowMarshaler::SetFloatProperty
DirectComposition::CCompositionAmbientLightMarshaler::SetFloatProperty
DirectComposition::CCompositionDistantLightMarshaler::SetFloatProperty
DirectComposition::CCompositionPointLightMarshaler::SetFloatProperty
DirectComposition::CVisualCaptureMarshaler::SetFloatProperty
DirectComposition::CNaturalAnimationMarshaler::SetFloatProperty

These resource marshalers internally call DirectComposition::CResourceMarshaler::SetFloatProperty, which is a general purpose method, setting a float property to a resource marshaler. Since any 32-bit value can be given as the float property type (or ID), this acts like a default case if previous cases are not met.

DirectComposition::CResourceMarshaler::SetFloatProperty internally calls DirectComposition::CResourceMarshaler::GetTargetProperty, which finds and returns a global structure storing information about the target property the kernel is trying to set on the target resource marshaler.

.text:00000001C00B2A51 loc_1C00B2A51:                          ; CODE XREF: DirectComposition::CResourceMarshaler::GetTargetProperty(uint)+3B↓j
.text:00000001C00B2A51                 cdqe
.text:00000001C00B2A53                 lea     rcx, ?resourcePropertyInformation@ResourceInformation@DirectComposition@@2PAUResPropInfo@2@A ; DirectComposition::ResPropInfo near * DirectComposition::ResourceInformation::resourcePropertyInformation
.text:00000001C00B2A5A                 shl     rax, 5
.text:00000001C00B2A5E                 add     rax, rcx
.text:00000001C00B2A61                 cmp     [rax], edx               ; Check if structure for target property is found
.text:00000001C00B2A63                 jz      short locret_1C00B2A6F   ; return
.text:00000001C00B2A65                 mov     eax, [rax+4]
.text:00000001C00B2A68
.text:00000001C00B2A68 loc_1C00B2A68:                          ; CODE XREF: DirectComposition::CResourceMarshaler::GetTargetProperty(uint)+1F↑j
.text:00000001C00B2A68                 cmp     eax, r8d
.text:00000001C00B2A6B                 jnz     short loc_1C00B2A51

Later, 4-byte value at offset +18h of the returned structure pointer is bitwise or’ed with the status flag (+10h) of the resource marshaler, which we are trying to set a float property.

.text:00000001C00B2A16                 mov     eax, [rax+18h]
.text:00000001C00B2A19                 or      [r10+10h], eax       ; Update the status flag of target resource marshaler
.text:00000001C00B2A1D                 mov     byte ptr [r9], 1
.text:00000001C00B2A21                 jmp     short loc_1C00B2A08

The problem here is that float property 0x1 and 0x36 retrieved by DirectComposition::CResourceMarshaler::GetTargetProperty has value 0x4 at offset +18h, which is the same value or’ed with the resource marshaler’s status flag when a new weak reference is created.

Therefore the kernel tries to get a weak reference to this resource marshaler, when float property 0x1 or 0x36 is set by DirectComposition::CResourceMarshaler::SetFloatProperty. Although there is no weak reference to this resource marshaler, its status flag shows that there is a weak reference to this resource.

Follwing is assembly code of DirectComposition::CApplicationChannel::GetWeakReferenceBase checking the status flag.

.text:00000001C00B5094                 mov     eax, [rdx+10h]  ; resource marshaler + 10h
.text:00000001C00B5097                 xor     edi, edi
.text:00000001C00B5099                 mov     r14, r8
.text:00000001C00B509C                 mov     rsi, rdx
.text:00000001C00B509F                 mov     rbp, rcx
.text:00000001C00B50A2                 test    al, 4           ; check if there are weak references to the given resource marshaler
.text:00000001C00B50A4                 jnz     loc_1C00B514F   ; do table lookup of target resource marshaler

Kernel will try to find an element in the table of references to the CWeakReferenceBase.

.text:00000001C00B514F                 xor     eax, eax
.text:00000001C00B5151                 mov     [rsp+0D8h+Buffer], rsi
.text:00000001C00B5156                 add     rcx, 108h       ; Table
.text:00000001C00B515D                 mov     [rsp+0D8h+var_B0], rax
.text:00000001C00B5162                 lea     rdx, [rsp+0D8h+Buffer] ; Buffer
.text:00000001C00B5167                 xor     ebx, ebx
.text:00000001C00B5169                 call    cs:__imp_RtlLookupElementGenericTable    ; Table lookup
.text:00000001C00B5170                 nop     dword ptr [rax+rax+00h]
.text:00000001C00B5175                 test    rax, rax
.text:00000001C00B5178                 jz      short loc_1C00B517E
.text:00000001C00B517A                 mov     rbx, [rax+8]    ; [1] This is not executed when weak reference for target resource marshaler is not found
.text:00000001C00B517E
.text:00000001C00B517E loc_1C00B517E:
.text:00000001C00B517E                 mov     ecx, [rbx+8]    ; [2] rbx stores pointer to CWeakReferenceBase only on lookup success => crash on fail
.text:00000001C00B5181                 lea     eax, [rcx+1]    ; increase weak reference count
.text:00000001C00B5184                 mov     [rbx+8], eax
.text:00000001C00B5187                 test    eax, eax
.text:00000001C00B5189                 jnz     short loc_1C00B512B
.text:00000001C00B518B                 jmp     loc_1C0146BB4

If weak reference is not found, which is the case since the only status flag is wrongly set, RtlLookupElementGenericTable returns NULL and it will not execute [1]. This stores a pointer in RBX register. Therefore RBX will remain as NULL, and dereferencing it at [2] will result in a system crash.

To trigger this bug, target resource marshaler must be able to call DirectComposition::CResourceMarshaler::SetFloatProperty to set the status flag and later have other resource marshalers create a weak reference to it by calling DirectComposition::CApplicationChannel::GetWeakReferenceBase(...).

Following are methods that call DirectComposition::CApplicationChannel::GetWeakReferenceBase(...). These methods should be called to create a weak reference to the target resource marshaler.

DirectComposition::CAnimationLoggingManagerMarshaler::SetBufferProperty
DirectComposition::CVisualReferenceControllerMarshaler::SetVisual
DirectComposition::CBaseExpressionMarshaler::SetReferenceProperty
DirectComposition::CExpressionMarshaler::SetReferenceArrayProperty
DirectComposition::CAnimationLoggingManagerMarshaler::SetBufferProperty

One example will be creating a CParticleEmmiterVisualMarshaler as the target and CVisualReferenceControllerMarshaler for creating a weak reference to the target. Setting a float property of type 0x36 to CParticleEmitterMarshaler will call DirectComposition::CVisualMarshaler::SetFloatProperty to set the status as having a weak reference to it.

Then, creating a reference property of type 0x0 to CParticleEmmiterVisualMarshaler on CVisualReferenceControllerMarshaler will eventually call DirectComposition::CVisualReferenceControllerMarshaler::SetVisual, creating a weak reference to CParticleEmmiterVisualMarshaler. This will crash the system.

kd> k
# Child-SP          RetAddr               Call Site
00 ffffd283`bfa6dc88 fffff800`67f82482     nt!DbgBreakPointWithStatus
01 ffffd283`bfa6dc90 fffff800`67f81cc1     nt!KiBugCheckDebugBreak+0x12
02 ffffd283`bfa6dcf0 fffff800`67e355c7     nt!KeBugCheck2+0xa71
03 ffffd283`bfa6e460 fffff800`67e480a9     nt!KeBugCheckEx+0x107
04 ffffd283`bfa6e4a0 fffff800`67e474bc     nt!KiBugCheckDispatch+0x69
05 ffffd283`bfa6e5e0 fffff800`67e3ec5f     nt!KiSystemServiceHandler+0x7c
06 ffffd283`bfa6e620 fffff800`67d498a7     nt!RtlpExecuteHandlerForException+0xf
07 ffffd283`bfa6e650 fffff800`67d4d7f1     nt!RtlDispatchException+0x2d7
08 ffffd283`bfa6edb0 fffff800`67e481ce     nt!KiDispatchException+0x1b1
09 ffffd283`bfa6f490 fffff800`67e441da     nt!KiExceptionDispatch+0x10e
0a ffffd283`bfa6f670 ffffae19`df0b517e     nt!KiPageFault+0x41a
0b ffffd283`bfa6f800 ffffae19`df0c74ae     win32kbase!DirectComposition::CApplicationChannel::GetWeakReferenceBase+0x106
0c ffffd283`bfa6f8e0 ffffae19`df0c7419     win32kbase!DirectComposition::CVisualReferenceControllerMarshaler::SetVisual+0x72
0d ffffd283`bfa6f920 ffffae19`df05846a     win32kbase!DirectComposition::CVisualReferenceControllerMarshaler::SetReferenceProperty+0x59
0e ffffd283`bfa6f950 ffffae19`df0580e8     win32kbase!DirectComposition::CApplicationChannel::ProcessCommandBufferIterator+0x2d6
0f ffffd283`bfa6fa10 ffffae19`df70fe6a     win32kbase!NtDCompositionProcessChannelBatchBuffer+0x168
10 ffffd283`bfa6faa0 fffff800`67e47a75     win32k!NtDCompositionProcessChannelBatchBuffer+0x16
11 ffffd283`bfa6fae0 00007ffa`f10139f4     nt!KiSystemServiceCopyEnd+0x25
TIMELINE

2022-04-26 - Vendor Disclosure
2022-08-16 - Public Release

Credit

Discovered by Jaewon Min of Cisco Talos.