Vendor | Microsoft, www.microsoft.com |
Affected Products | Windows Hyper-V |
Affected Versions | Windows 10 and Windows Server |
CVE ID | CVE-2021-28476 |
Severity | Critical |
Author | Daniel Fernandez Kuehr (@ergot86), Blue Frost Security GmbH |
I. Summary
Multiple bugs are present in WPP code when handling set OID requests, one of them allowing to dereference (read access) an attacker controlled pointer, and the rest causing out of bounds read accesses.
II. Description
The RndisDevHostHandleSetMessage function handles requests coming either from guest VMs or the host. WPP code has been added to log some specific OIDs. The code is executed regardless of the request origin.
struct entry if ( v5 ) { v32 = 8340; v30 = "onecore\\vm\\dv\\net\\nvsp\\driver\\vm\\host.c"; VmsIfrInfoParamsNdisOidRequestBuffer( (unsigned int *)&v34, v3[8], 0, v5, v3[9], (unsigned __int64)"RndisDevHostHandleSetMessage"); v17 = v34; }
The VmsIfrInfoParamsNdisOidRequestBuffer function logs the following OIDs:
VmsIfrInfoParams_OID_802_3_PERMANENT_ADDRESS((__int64)a1, v13, a4); VmsIfrInfoParams_OID_SWITCH_NIC_UPDATED(a1, v13, a4, a5); VmsIfrInfoParams_OID_SWITCH_NIC_REQUEST(a1, v12, a4, a5); VmsIfrInfoParams_OID_NIC_SWITCH_FREE_VF((__int64)a1, v11, a4); VmsIfrInfoParams_OID_NIC_SWITCH_ALLOCATE_VF((__int64)a1, v10, a4); VmsIfrInfoParams_OID_NIC_SWITCH_DELETE_VPORT((__int64)a1, v9, a4); VmsIfrInfoParams_NDIS_NIC_SWITCH_VPORT_PARAMETERS((__int64)a1, v8, a4); VmsIfrInfoParams_OID_RECEIVE_FILTER_MOVE_FILTER((__int64)a1, v7, a4);
Bug #1 - VmsIfrInfoParams_OID_SWITCH_NIC_REQUEST
OID_SWITCH_NIC_REQUEST messages are used by the switch to encapsulate requests to the physical NIC, VmsIfrInfoParams_OID_SWITCH_NIC_REQUEST is called to log these regardless the message origin. The parameters are the following:
typedef struct _NDIS_SWITCH_NIC_OID_REQUEST { NDIS_OBJECT_HEADER Header; ULONG Flags; NDIS_SWITCH_PORT_ID SourcePortId; NDIS_SWITCH_NIC_INDEX SourceNicIndex; NDIS_SWITCH_PORT_ID DestinationPortId; NDIS_SWITCH_NIC_INDEX DestinationNicIndex; PNDIS_OID_REQUEST OidRequest; } NDIS_SWITCH_NIC_OID_REQUEST, *PNDIS_SWITCH_NIC_OID_REQUEST;
If the OidRequest field is not NULL it will be dereferenced to access information in the encapsulated OID request. An attacker can craft a OID_SWITCH_NIC_REQUEST from an unprivileged guest and forge any pointer.
Bug #2 - VmsIfrInfoParams_OID_SWITCH_NIC_UPDATED
OID_SWITCH_NIC_UPDATED parameters are the following:
typedef struct _NDIS_SWITCH_NIC_PARAMETERS { NDIS_OBJECT_HEADER Header; ULONG Flags; NDIS_SWITCH_NIC_NAME NicName; NDIS_SWITCH_NIC_FRIENDLYNAME NicFriendlyName; NDIS_SWITCH_PORT_ID PortId; NDIS_SWITCH_NIC_INDEX NicIndex; NDIS_SWITCH_NIC_TYPE NicType; NDIS_SWITCH_NIC_STATE NicState; NDIS_VM_NAME VmName; NDIS_VM_FRIENDLYNAME VmFriendlyName; GUID NetCfgInstanceId; ULONG MTU; USHORT NumaNodeId; UCHAR PermanentMacAddress[NDIS_MAX_PHYS_ADDRESS_LENGTH]; UCHAR VMMacAddress[NDIS_MAX_PHYS_ADDRESS_LENGTH]; UCHAR CurrentMacAddress[NDIS_MAX_PHYS_ADDRESS_LENGTH]; BOOLEAN VFAssigned; ULONG64 NdisReserved[2]; } NDIS_SWITCH_NIC_PARAMETERS, *PNDIS_SWITCH_NIC_PARAMETERS;
Fields: NicName, NicFriendlyName, VmName, and VmFriendlyName are of type: IF_COUNTED_STRING_LH.
typedef struct _IF_COUNTED_STRING_LH { USHORT Length; WCHAR String[IF_MAX_STRING_SIZE + 1]; } IF_COUNTED_STRING_LH, *PIF_COUNTED_STRING_LH; typedef IF_COUNTED_STRING_LH IF_COUNTED_STRING; typedef IF_COUNTED_STRING *PIF_COUNTED_STRING;
Those are logged by VmsIfrInfoParams_OID_SWITCH_NIC_UPDATED and staging functions generated by the preprocessor display signatures like:
WPP_RECORDER_SF_sddsssDDSSddcLLSSd_MAC__MAC_d_guid_
We can see how strings are processed in defaultwpp.ini:
DEFINE_CPLX_TYPE(ASTR, WPP_LOGASTR, const char*, ItemString, "s", s, 0, win:AnsiString); DEFINE_CPLX_TYPE(ARSTR, WPP_LOGASTR, LPCSTR, ItemRString, "s", t, 0, win:AnsiString); DEFINE_CPLX_TYPE(ARWSTR, WPP_LOGWSTR, LPCWSTR, ItemRWString, "s", S, 0, win:UnicodeString); DEFINE_CPLX_TYPE(WSTR, WPP_LOGWSTR, LPCWSTR, ItemWString, "s", S, 0, win:UnicodeString); DEFINE_CPLX_TYPE(CSTR, WPP_LOGPCSTR, PCSTRING, ItemPString, "s", z, 0, 2); DEFINE_CPLX_TYPE(USTR, WPP_LOGPUSTR, PCUNICODE_STRING,ItemPWString,"s", Z, 0, 2); DEFINE_CPLX_TYPE(ANSTR, WPP_LOGPCSTR, PCANSI_STRING, ItemPString, "s", aZ, 0, 2);
Strings can be either counted (PCUNICODE_STRING) or zero-terminated (LPCWSTR) but the code has no knowledge about IF_MAX_STRING_SIZE. In this case the string is treated as a LPCWSTR, so the generated code iterates every WCHAR until finding the L'\0' termination.
fffff803`98ae7f67 488bc3 mov rax,rbx fffff803`98ae7f6a 48ffc0 inc rax fffff803`98ae7f6d 664139544500 cmp word ptr [r13+rax*2],dx fffff803`98ae7f73 75f5 jne vmswitch!WPP_RECORDER_SF_sddsssDDSSddcLLSSd_MAC__MAC_d_guid_+0x42a (fffff803`98ae7f6a)
If we fill the NDIS_SWITCH_NIC_PARAMETERS structure with a non-zero pattern then the loop will access addresses past the buffer size until two consecutive zero bytes are found. Proof of concept code is provided below demonstrating the issue for the NicName field but others fields like NicFriendlyName, VmName, and VmFriendlyName should be affected as well.
Bug #3 - VmsIfrInfoParams_OID_NIC_SWITCH_ALLOCATE_VF
typedef struct _NDIS_NIC_SWITCH_VF_PARAMETERS { NDIS_OBJECT_HEADER Header; ULONG Flags; NDIS_NIC_SWITCH_ID SwitchId; NDIS_VM_NAME VMName; NDIS_VM_FRIENDLYNAME VMFriendlyName; NDIS_SWITCH_NIC_NAME NicName; USHORT MacAddressLength; UCHAR PermanentMacAddress[NDIS_MAX_PHYS_ADDRESS_LENGTH]; UCHAR CurrentMacAddress[NDIS_MAX_PHYS_ADDRESS_LENGTH]; NDIS_SRIOV_FUNCTION_ID VFId; NDIS_VF_RID RequestorId; } NDIS_NIC_SWITCH_VF_PARAMETERS, *PNDIS_NIC_SWITCH_VF_PARAMETERS;
Same bug pattern as #2, PoC code is provided demonstrating the issue for VMName but VMFriendlyName and NicName should also be affected.
Bug #4 - VmsIfrInfoParams_NDIS_NIC_SWITCH_VPORT_PARAMETERS
typedef struct _NDIS_NIC_SWITCH_VPORT_PARAMETERS { NDIS_OBJECT_HEADER Header; ULONG Flags; NDIS_NIC_SWITCH_ID SwitchId; NDIS_NIC_SWITCH_VPORT_ID VPortId; NDIS_VPORT_NAME VPortName; NDIS_SRIOV_FUNCTION_ID AttachedFunctionId; ULONG NumQueuePairs; NDIS_NIC_SWITCH_VPORT_INTERRUPT_MODERATION InterruptModeration; NDIS_NIC_SWITCH_VPORT_STATE VPortState; GROUP_AFFINITY ProcessorAffinity; ULONG LookaheadSize; NDIS_NDK_PARAMETERS NdkParams; NDIS_QOS_SQ_ID QosSqId; } NDIS_NIC_SWITCH_VPORT_PARAMETERS, *PNDIS_NIC_SWITCH_VPORT_PARAMETERS;
Same bug pattern as #2, affecting VPortName.
III. Impact
Bug #1 can be used to crash the root-partition by supplying a pointer to unmapped kernel memory. Bugs #2,3,4 could crash the root-partition in the - highly unlikely - scenario when no sequence of two zero-bytes can be found starting at the object address to the end of the mapped area. However this condition can be demonstrated by enabling PageHeap.
IV. Proof of Concept
A kernel patch is provided here https://github.com/bluefrostsecurity/CVE-2021-28476 to trigger the described issues from a Linux guest by setting the nic MAC address to the following values:
bug #1: ip link set dev eth0 addr fe:01:00:00:00:00
bug #2: ip link set dev eth0 addr fe:02:00:00:00:00
bug #3: ip link set dev eth0 addr fe:03:00:00:00:00
bug #4: ip link set dev eth0 addr fe:04:00:00:00:00
To trigger #2,3,4 add vmswitch.sys to Verifier and enable PageHeap. All bugs should trigger in 10.0.19042.572 without further setup. Other versions might use TraceView to reach the vulnerable code, on these add GUID 1f387cbc-6818-4530-9db6-5f1058cd7e86 to TraceView. WPP code changes across different releases, for example in 10.0.18363.1139 only bug #1 is present (needs TraceView).
V. Disclosure Timeline
2020-11-09 |
Bug report sent to secure@microsoft.com |
2021-01-20 |
Microsoft confirms bug via email. |
2021-04-08 |
200.000 USD bounty awarded by Microsoft |
2021-05-11 |
Microsoft releases patch CVE-2021-28476 |
Unaltered electronic reproduction of this advisory is permitted. For all other reproduction or publication, in printing or otherwise, contact research@bluefrostsecurity.de for permission. Use of the advisory constitutes acceptance for use in an "as is" condition. All warranties are excluded. In no event shall Blue Frost Security be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if Blue Frost Security has been advised of the possibility of such damages.
Copyright 2021 Blue Frost Security GmbH. All rights reserved. Terms of use apply.