Microsoft Hyper-V: Multiple Vulnerabilities in vmswitch.sys

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

 

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.