Zero-day vulnerability in Desktop Window Manager (CVE-2021
Vulnerabilities mentionedAll →
| CVE | Vulnerability | CVSS | EPSS | Flags | Affected | Exposure | Published |
|---|---|---|---|---|---|---|---|
| CVE-2021-1732 | Out-of-Bounds Write Local Privilege Escalation in Microsoft Win32k (CVE-2021-1732) CVE-2021-1732 is a local elevation-of-privilege vulnerability (CWE-787, out-of-bounds write) in Microsoft's Win32k kernel driver, publicly characterized as an "offset confusion" in the Win32k ConsoleControl routine. It is triggered locally: a process with only low privileges can invoke the vulnerable Win32k functionality without any user interaction, causing a user-supplied offset/pointer to be mishandled in kernel mode and memory to be written out of bounds. An attacker who successfully exploits the flaw can execute code in the kernel and elevate to SYSTEM, gaining full control of the host — which makes it a valuable second-stage link in malware and ransomware chains. Any system running the affected Windows 10 releases (1803, 1809, 1909, 2004, 20H2) or Windows Server 2019/1909/2004/20H2 is exposed, though exploitation requires the attacker to already run code locally on the target. The flaw was fixed in Microsoft's February 2021 Patch Tuesday updates, was added to CISA's KEV catalog on 2021-11-03 with known ransomware use, and carries a very high EPSS score (78.4%, 100th percentile), indicating sustained exploitation pressure. Do: Apply Microsoft's February 2021 (or later) Windows cumulative security updates to all affected Windows 10 and Windows Server systems, per vendor instructions — CISA's KEV listing requires federal agencies to patch. Prioritize hosts exposed to untrusted local users or already compromised by malware (e.g., ransomware or Raspberry Robin activity, which has used chained Windows LPEs), and hunt on unpatched hosts for signs of post-exploitation privilege escalation to SYSTEM. | 7.8 | 78% | KEV ransomware PoC ×2 |
| mass≈1 billion+ Windows devices (the listed builds spanned the mainstream Windows 10/Server install base) | |
| CVE-2021-28310 | Out-of-Bounds Write in Microsoft Win32k Allows Local Privilege Escalation on Windows 10 CVE-2021-28310 is an elevation-of-privilege flaw caused by an out-of-bounds write (CWE-787) in the Windows Win32k kernel component. A local attacker who can already execute low-privileged code on a vulnerable system can trigger the memory corruption bug to run arbitrary code in kernel mode, with no user interaction required. Successful exploitation grants SYSTEM/kernel-level control of the host, typically as a follow-on step after an attacker has gained an initial foothold, rather than a remote-entry vector. Windows 10 versions 1803, 1809, 1909, 2004 and 20H2, plus the corresponding Windows Server versions 1909, 2004, 2019 and 20H2, are affected. The flaw was fixed in Microsoft's April 2021 security updates and has been actively exploited in the wild — CISA added it to the KEV catalog on 2021-11-03, and its EPSS of 8.3% (95th percentile) signals elevated exploitation risk. Do: Apply the April 2021 (or later) Windows cumulative security updates to all affected Windows 10 and Windows Server systems, prioritizing hosts where untrusted or low-privileged users can run code, such as terminal/RDS servers, VDI and shared workstations. Because this flaw is in the CISA KEV catalog, applying vendor updates is a required action for federal and regulated environments; verify remediation by confirming the installed OS build includes the April 2021 patch. For systems that cannot be patched promptly, limit local code execution by untrusted users and monitor for post-exploitation privilege-escalation behavior. | 7.8 | 8% | KEV |
| masshundreds of millions of Windows 10 and Windows Server endpoints worldwide |
Full article2,318 words · extracted from securelist.com · click to collapse
While analyzing the CVE-2021-1732 exploit originally discovered by the DBAPPSecurity Threat Intelligence Center and used by the BITTER APT group, we discovered another zero-day exploit we believe is linked to the same actor. We reported this new exploit to Microsoft in February and after confirmation that it is indeed a zero-day, it received the designation CVE-2021-28310. Microsoft released a patch to this vulnerability as a part of its April security updates.
We believe this exploit is used in the wild, potentially by several threat actors. It is an escalation of privilege (EoP) exploit that is likely used together with other browser exploits to escape sandboxes or get system privileges for further access. Unfortunately, we weren’t able to capture a full chain, so we don’t know if the exploit is used with another browser zero-day, or coupled with known, patched vulnerabilities.
The exploit was initially identified by our advanced exploit prevention technology and related detection records. In fact, over the past few years, we have built a multitude of exploit protection technologies into our products that have detected several zero-days, proving their effectiveness time and again. We will continue to improve defenses for our users by enhancing technologies and working with third-party vendors to patch vulnerabilities, making the internet more secure for everyone. In this blog we provide a technical analysis of the vulnerability and how the bad guys exploited it. More information about BITTER APT and IOCs are available to customers of the Kaspersky Intelligence Reporting service. Contact: [email protected].
Technical details
CVE-2021-28310 is an out-of-bounds (OOB) write vulnerability in dwmcore.dll, which is part of Desktop Window Manager (dwm.exe). Due to the lack of bounds checking, attackers are able to create a situation that allows them to write controlled data at a controlled offset using DirectComposition API. DirectComposition is a Windows component that was introduced in Windows 8 to enable bitmap composition with transforms, effects and animations, with support for bitmaps of different sources (GDI, DirectX, etc.). We’ve already published a blogpost about in-the-wild zero-days abusing DirectComposition API. DirectComposition API is implemented by the win32kbase.sys driver and the names of all related syscalls start with the string “NtDComposition”.
DirectComposition syscalls in the win32kbase.sys driver
For exploitation only three syscalls are required: NtDCompositionCreateChannel, NtDCompositionProcessChannelBatchBuffer and NtDCompositionCommitChannel. The NtDCompositionCreateChannel syscall initiates a channel that can be used together with the NtDCompositionProcessChannelBatchBuffer syscall to send multiple DirectComposition commands in one go for processing by the kernel in a batch mode. For this to work, commands need to be written sequentially in a special buffer mapped by NtDCompositionCreateChannel syscall. Each command has its own format with a variable length and list of parameters.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
enumDCOMPOSITION_COMMAND_ID { ProcessCommandBufferIterator, CreateResource, OpenSharedResource, ReleaseResource, GetAnimationTime, CapturePointer, OpenSharedResourceHandle, SetResourceCallbackId, SetResourceIntegerProperty, SetResourceFloatProperty, SetResourceHandleProperty, SetResourceHandleArrayProperty, SetResourceBufferProperty, SetResourceReferenceProperty, SetResourceReferenceArrayProperty, SetResourceAnimationProperty, SetResourceDeletedNotificationTag, AddVisualChild, RedirectMouseToHwnd, SetVisualInputSink, RemoveVisualChild }; |
List of command IDs supported by the function DirectComposition::CApplicationChannel::ProcessCommandBufferIterator
While these commands are processed by the kernel, they are also serialized into another format and passed by the Local Procedure Call (LPC) protocol to the Desktop Window Manager (dwm.exe) process for rendering to the screen. This procedure could be initiated by the third syscall – NtDCompositionCommitChannel.
To trigger the vulnerability the discovered exploit uses three types of commands: CreateResource, ReleaseResource and SetResourceBufferProperty.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
voidCreateResourceCmd(intresourceId) { DWORD *buf=(DWORD *)((PUCHAR)pMappedAddress+BatchLength); *buf=CreateResource; buf[1]=resourceId; buf[2]=PropertySet;// MIL_RESOURCE_TYPE buf[3]=FALSE; BatchLength+=16; } voidReleaseResourceCmd(intresourceId) { DWORD *buf=(DWORD *)((PUCHAR)pMappedAddress+BatchLength); *buf=ReleaseResource; buf[1]=resourceId; BatchLength+=8; } voidSetPropertyCmd(intresourceId,boolupdate,intpropertyId,intstorageOffset,inthidword,intlodword) { DWORD *buf=(DWORD *)((PUCHAR)pMappedAddress+BatchLength); *buf=SetResourceBufferProperty; buf[1]=resourceId; buf[2]=update; buf[3]=20; buf[4]=propertyId; buf[5]=storageOffset; buf[6]=_D2DVector2;// DCOMPOSITION_EXPRESSION_TYPE buf[7]=hidword; buf[8]=lodword; BatchLength+=36; } |
Format of commands used in exploitation
Let’s take a look at the function CPropertySet::ProcessSetPropertyValue in dwmcore.dll. This function is responsible for processing the SetResourceBufferProperty command. We are most interested in the code responsible for handling DCOMPOSITION_EXPRESSION_TYPE = D2DVector2.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
intCPropertySet::ProcessSetPropertyValue(CPropertySet *this,...) { ... if(expression_type==_D2DVector2) { if(!update) { CPropertySet::AddProperty<D2DVector2>(this,propertyId,storageOffset,_D2DVector2,value); } else { if(storageOffset!=this->properties[propertyId]->offset&0x1FFFFFFF) { gotofail; } CPropertySet::UpdateProperty<D2DVector2>(this,propertyId,_D2DVector2,value); } } ... } intCPropertySet::AddProperty<D2DVector2>(CResource *this,unsignedintpropertyId,intstorageOffset,inttype,_QWORD *value) { intpropertyIdAdded; intresult=PropertySetStorage<DynArrayNoZero,PropertySetUserModeAllocator>::AddProperty<D2DVector2>( this->propertiesData, type, value, &propertyIdAdded); if(result<0) { returnresult; } if(propertyId!=propertyIdAdded||storageOffset!=this->properties[propertyId]->offset&0x1FFFFFFF) { return0x88980403; } result=CPropertySet::PropertyUpdated<D2DMatrix>(this,propertyId); if(result<0) { returnresult; } return0; } intCPropertySet::UpdateProperty<D2DVector2>(CResource *this,unsignedintpropertyId,inttype,_QWORD *value) { if(this->properties[propertyId]->type==type) { *(_QWORD *)(this->propertiesData+(this->properties[propertyId]->offset&0x1FFFFFFF))=*value; intresult=CPropertySet::PropertyUpdated<D2DMatrix>(this,propertyId); if(result<0) { returnresult; } return0; } else { return0x80070057; } } |
Processing of the SetResourceBufferProperty (D2DVector2) command in dwmcore.dll
For the SetResourceBufferProperty command with the expression type set to D2DVector2, the function CPropertySet::ProcessSetPropertyValue(…) would either call CPropertySet::AddProperty<D2DVector2>(…) or CPropertySet::UpdateProperty<D2DVector2>(…) depending on whether the update flag is set in the command. The first thing that catches the eye is the way the new property is added in the CPropertySet::AddProperty<D2DVector2>(…) function. You can see that it adds a new property to the resource, but it only checks if the propertyId and storageOffset of a new property are equal to the provided values after the new property is added, and returns an error if that’s not the case. Checking something after a job is done is bad coding practice and can result in vulnerabilities. However, a real issue can be found in the CPropertySet::UpdateProperty<D2DVector2>(…) function. No check takes place that will ensure if the provided propertyId is less than the count of properties added to the resource. As a result, an attacker can use this function to perform an OOB write past the propertiesData buffer if it manages to bypass two additional checks for data inside the properties array.
|
1 2 |
(1)storageOffset==this->properties[propertyId]->offset&0x1FFFFFFF (2)this->properties[propertyId]->type==type |
Conditions which need to be met for exploitation in dwmcore.dll
These checks could be bypassed if an attacker is able to allocate and release objects in the dwm.exe process to groom heap into the desired state and spray memory at specific locations with fake properties. The discovered exploit manages to do this using the CreateResource, ReleaseResource and SetResourceBufferProperty commands.
At the time of writing, we still hadn’t analyzed the updated binaries that are fixing this vulnerability, but to exclude the possibility of other variants for this vulnerability Microsoft would need to check the count of properties for other expression types as well.
Even with the above issues in dwmcore.dll, if the desired memory state is achieved to bypass the previously mentioned checks and a batch of commands are issued to trigger the vulnerability, it still won’t be triggered because there is one more thing preventing it from happening.
As mentioned above, commands are first processed by the kernel and only after that are they sent to Desktop Window Manager (dwm.exe). This means that if you try to send a command with an invalid propertyId, NtDCompositionProcessChannelBatchBuffer syscall will return an error and the command will not be passed to the dwm.exe process. SetResourceBufferProperty commands with expression type set to D2DVector2 are processed in the win32kbase.sys driver with the functions DirectComposition::CPropertySetMarshaler::AddProperty<D2DVector2>(…) and DirectComposition::CPropertySetMarshaler::UpdateProperty<D2DVector2>(…), which are very similar to those present in dwmcore.dll (it’s quite likely they were copy-pasted). However, the kernel version of the UpdateProperty<D2DVector2> function has one notable difference – it actually checks the count of properties added to the resource.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
intDirectComposition::CPropertySetMarshaler::UpdateProperty<D2DVector2>(DirectComposition::CPropertySetMarshaler *this,unsignedint*commandParams,_QWORD *value) { unsignedintpropertyId=commandParams[0]; unsignedintstorageOffset=commandParams[1]; unsignedinttype=commandParams[2]; if(propertyId>=this->propertiesCount ||storageOffset!=this->properties[propertyId]->offset&0x1FFFFFFF) ||type!=this->properties[propertyId]->type) { return0xC000000D; } else { *(_QWORD *)(this->propertiesData+(this->properties[propertyId]->offset&0x1FFFFFFF))=*value; ... } return0; } |
DirectComposition::CPropertySetMarshaler::UpdateProperty<D2DVector2>(…) in win32kbase.sys
The check for propertiesCount in the kernel mode version of the UpdateProperty<D2DVector2> function prevents further processing of a malicious command by its user mode twin and mitigates the vulnerability, but this is where DirectComposition::CPropertySetMarshaler::AddProperty<D2DVector2>(…) comes in to play. The kernel version of the AddProperty<D2DVector2> function works exactly like its user mode variant and it also applies the same behavior of checking property after it has already been added and returns an error if propertyId and storageOffset of the created property do not match the provided values. Because of this, it’s possible to use the AddProperty<D2DVector2> function to add a new property and force the function to return an error and cause inconsistency between the number of properties assigned to the same resource in kernel mode/user mode. The propertiesCount check in the kernel could be bypassed this way and malicious commands would be passed to Desktop Window Manager (dwm.exe).
Inconsistency between the number of properties assigned to the same resource in kernel mode/user mode could be a source of other vulnerabilities, so we recommend Microsoft to change the behavior of the AddProperty function and check properties before they are added.
The whole exploitation process for the discovered exploit is as follows:
- Create a large number of resources with properties of specific size to get heap into predictable state.
- Create additional resources with properties of specific size and content to spray memory at specific locations with fake properties.
- Release resources created at stage 2.
- Create additional resources with properties. These resources will be used to perform OOB writes.
- Make holes among resources created at stage 1.
- Create additional properties for resources created at stage 4. Their buffers are expected to be allocated at specific locations.
- Create “special” properties to cause inconsistency between the number of properties assigned to the same resource in kernel mode/user mode for resources created at stage 4.
- Use OOB write vulnerability to write shellcode, create an object and get code execution.
- Inject additional shellcode into another system process.
Kaspersky products detect this exploit with the verdicts:
- HEUR:Exploit.Win32.Generic
- HEUR:Trojan.Win32.Generic
- PDM:Exploit.Win32.Generic
Latest Webinars
Reports
Kaspersky researchers have discovered new Mirage Kitten attacks using previously undocumented malware families: NodeRabbit in Node.js and PollCat in JavaScript.
Our experts discovered a new CoolClient backdoor variant with a kernel-mode rootkit driver that hides malicious processes, files, and network connections from security tools and threat analysts.
Kaspersky experts break down a new Armored Likho campaign that poses as a fundraising efforts and delivers a new Still Toolkit aimed at stealing Telegram data and eavesdropping on victims.
Kaspersky researchers reveal previously undocumented malware attributed to Mirage Kitten (UNC1549, Smoke Sandstorm, Nimbus Manticore): NightLedger backdoor, ArcBridge, and BridgeHead tunneling tools.
Text extracted automatically; images, tables and formatting may be missing. Original: https://securelist.com/zero-day-vulnerability-in-desktop-window-manager-cve-2021-28310-used-in-the-wild/101898/