CVE-2025-21333 - Windows Hyper-V NT Kernel Integration VSP Elevation of Privilege Vulnerabilities

Page content

A vulnerability in the Windows Hyper-V NT Kernel Integration VSP driver exists due to a vulnerable function, VkiRootAdjustSecurityDescriptorForVmwp(), which can be invoked from user mode. This leads to a heap-based buffer overflow, ultimately resulting in privilege escalation.

CVE-2025-21333: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-21333
Vulnerability Type: Heap-based Buffer Overflow
Tested On: Windows 11 23H2
Driver Version: vkrnlintvsp.sys - 10.0.22621.2506

Requirements

To exploit this vulnerability, Windows Sandbox must be enabled in “Turn Windows features on or off”.

IMG

Vulnerability analysis

The vulnerability exists in the VkiRootAdjustSecurityDescriptorForVmwp() function of the vkrnlintvsp.sys driver, where the Dacl is user-controllable. The memmove operation uses Dacl->AclSize without verifying the user-supplied size, leading to a heap-based buffer overflow by copying unvalidated data into the Paged Pool.

__int64 __fastcall VkiRootAdjustSecurityDescriptorForVmwp(void *a1, char a2)
{
  struct _ACL *v4; // rdi
  NTSTATUS ObjectSecurity; // ebx
  __int16 v6; // bx
  __int16 v7; // ax
  WORD v8; // bx
  struct _ACL *Pool2; // rax
  int v10; // esi
  unsigned __int8 DaclDefaulted[8]; // [rsp+20h] [rbp-50h] BYREF
  PACL Dacl; // [rsp+28h] [rbp-48h] BYREF
  PSID Sid; // [rsp+30h] [rbp-40h] BYREF
  PSID P; // [rsp+38h] [rbp-38h] BYREF
  PSECURITY_DESCRIPTOR SecurityDescriptor; // [rsp+40h] [rbp-30h] BYREF
  _OWORD v17[2]; // [rsp+48h] [rbp-28h] BYREF
  __int64 v18; // [rsp+68h] [rbp-8h]
  unsigned __int8 DaclPresent; // [rsp+A0h] [rbp+30h] BYREF
  unsigned __int8 MemoryAllocated; // [rsp+A8h] [rbp+38h] BYREF

  Dacl = 0LL;
  DaclDefaulted[0] = 0;
  SecurityDescriptor = 0LL;
  Sid = 0LL;
  P = 0LL;
  memset(v17, 0, sizeof(v17));
  v18 = 0LL;
  DaclPresent = 0;
  v4 = 0LL;
  MemoryAllocated = 0;
  ObjectSecurity = ObGetObjectSecurity(a1, &SecurityDescriptor, &MemoryAllocated);
  if ( ObjectSecurity >= 0 )
  {
    if ( !SecurityDescriptor )
      goto LABEL_15;
    ObjectSecurity = RtlGetDaclSecurityDescriptor(SecurityDescriptor, &DaclPresent, &Dacl, DaclDefaulted);
    if ( ObjectSecurity < 0 )
      goto LABEL_16;
    if ( DaclPresent && Dacl )
    {
      ObjectSecurity = SeConvertStringSidToSid(L"S-1-5-83-0", &Sid);
      if ( ObjectSecurity >= 0 )
      {
        ObjectSecurity = SeConvertStringSidToSid(
                           L"S-1-15-3-1024-2268835264-3721307629-241982045-173645152-1490879176-104643441-2915960892-1612460704",
                           &P);
        if ( ObjectSecurity >= 0 )
        {
          v6 = RtlLengthSid(Sid);
          v7 = RtlLengthSid(P);
          v8 = Dacl->AclSize + v7 + v6 + 16;
          Pool2 = (struct _ACL *)ExAllocatePool2(256LL, v8, 1867671894LL);
          v4 = Pool2;
          if ( Pool2 )
          {
            memmove(Pool2, Dacl, Dacl->AclSize); // Vulnerable memmove/memcpy call
            v4->AclSize = v8;
            v10 = a2 != 0 ? 2 : 0;
            ObjectSecurity = RtlAddAccessAllowedAce(v4, 2u, v10 + 2031617, Sid);
            if ( ObjectSecurity >= 0 )
            {
              ObjectSecurity = RtlAddAccessAllowedAce(v4, 2u, v10 + 2031617, P);
              if ( ObjectSecurity >= 0 )
              {
                ObjectSecurity = RtlCreateSecurityDescriptor(v17, 1u);
                if ( ObjectSecurity >= 0 )
                {
                  ObjectSecurity = RtlSetDaclSecurityDescriptor(v17, 1u, v4, 0);
                  if ( ObjectSecurity >= 0 )
                    ObjectSecurity = ObSetSecurityObjectByPointer(a1, 4LL, v17);
                }
              }
            }
          }
          else
          {
            ObjectSecurity = -1073741801;
          }
        }
      }
    }
    else
    {
LABEL_15:
      ObjectSecurity = 0;
    }
  }
LABEL_16:
  if ( Sid )
    ExFreePoolWithTag(Sid, 0);
  if ( P )
    ExFreePoolWithTag(P, 0);
  if ( v4 )
    ExFreePoolWithTag(v4, 0x6F526956u);
  ObReleaseObjectSecurity(SecurityDescriptor, MemoryAllocated);
  return (unsigned int)ObjectSecurity;
}

The kernel has a mechanism to register and use specific callbacks for some predetermined drivers. When the specific driver gets loaded, the driver provides a table which contains list of callbacks that the kernel can use. During the initialization, the kernel calls ExRegisterHost() function with some specific inputs this is specific for each driver to register some predetermined unexported functions.

In this case, it was discovered that VkiRootCalloutCreateEvent() is one of the callback functions that can be invoked by NtCreateCrossVmEvent(). Therefore, to reach the vulnerable function call, we need to invoke NtCreateCrossVmEvent() from user space.

IMG

NtCreateCrossVmEvent() is an undocumented function that takes an OBJECT_ATTRIBUTES structure as an argument. This structure includes a SecurityDescriptor (SECURITY_DESCRIPTOR), which contains the security information associated with the object — including the Dacl. This is where the payload should be injected in order to trigger the vulnerability.

NtCreateCrossVmEvent(
    _Out_ PHANDLE CrossVmEvent,
    _In_ ACCESS_MASK DesiredAccess,
    _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
    _In_ ULONG CrossVmEventFlags,
    _In_ LPCGUID VMID,
    _In_ LPCGUID ServiceID
    );

Exploit

Tested on: Windows 11 23H2
Working POC: https://github.com/ghostbyt3/WinDriver-EXP/tree/main/CVE-2025-21333/POC

IMG1

Acknowledgements

References: