CVE-2022-40733
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.
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
win32kbase.sys - https://www.microsoft.com Windows - https://www.microsoft.com/en-us/windows/
5.0 - CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H
CWE-476 - NULL Pointer Dereference
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
2022-04-26 - Vendor Disclosure
2022-08-16 - Public Release
Discovered by Jaewon Min of Cisco Talos.