Talos Vulnerability Report

TALOS-2024-1968

Microsoft Windows CLIPSP.SYS License Update Field Type 0xC9 out-of-bounds read vulnerability

August 13, 2024
CVE Number

None,CVE-2024-38062

SUMMARY

An out-of-bounds read vulnerability exists in the License Update Field Type 0xC9 functionality of Microsoft Windows CLIPSP.SYS 10.0.22621 Build 22621, 10.0.26080.1 and 10.0.26085.1. A specially crafted license blob can lead to an out-of-bounds kernel read. An attacker can use the NtQuerySystemInformation function call to trigger this vulnerability.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

Microsoft CLIPSP.SYS 10.0.22621 Build 22621
Microsoft CLIPSP.SYS 10.0.26080.1
Microsoft CLIPSP.SYS 10.0.26085.1
Microsoft Windows 11 Pro 23H2 22631.3296
Microsoft Windows 11 Pro 24H2 Insider Preview 26085.1

PRODUCT URLS

CLIPSP.SYS - https://www.microsoft.com/en-us/windows/windows-11 Windows - https://www.microsoft.com/en-us/windows/

CVSSv3 SCORE

6.8 - CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:H

CWE

CWE-125 - Out-of-bounds Read

DETAILS

CLIPSP.SYS is a driver used to implement Client License System Policy on Windows 10 and 11. It provides the functions used when handling most of the requests involving licensing, notably the implementation of many use cases involved with the SystemPolicyInformation class used in conjunction with NtQuerySystemInformation.

Context

When calling NtQuerySystemInformation with the SystemPolicyInformation class, ntoskrnl will call ExHandleSPCall2 that will process the data provided. The format is mostly undocumented and encrypted using Microsoft’s Warbird. Upon decryption of the data provided, a call handler is invoked based on the command_id provided and dispatches the payload to the relevant function (e.g. SPCallServerHandleClepKdf, SPCallServerHandleUpdateLicense, etc.). A substential amount of these functions are wrapers around clipsp functions that are stored as function pointers in the nt!g_kernelCallbacks globlal array.

The SPCallServerHandleUpdateLicense (command_id:100) will accept a License blob whose format is also undocumented. Once installed, these license files are stored in the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\{7746D80F-97E0-4E26-9543-26B41FC22F79}\{A25AE4F2-1B96-4CED-8007-AA30E9B1A218} key, only accessible to the SYSTEM user. The format of this license file is TLV (Tag-length-value) following this format:

struct __unaligned __declspec(align(1)) LicenseParsing_entry
{
  __int16 type;
  __int16 reserved;
  int entry_size;
  char value[ANYSIZE_ARRAY]; //expected to be of size entry_size
};

The code snippets below are decompiled output and variables names were assumed from context or retrieved from public symbol servers, sdk, etc. The addresses provided are for the canary build 26085.1.amd64fre.ge_release.240315-1352

There exists multiple out-of-bound reads vulnerabilities when handling various license field types, in this case of type 0xC9. This vulnerability can be triggered by feeding to the SPCallServerHandleUpdateLicense function a license filed that has been tampered with.

When calling SPCallServerHandleUpdateLicense the data will eventually be past to an obfuscated function inside clipsp and stored in an array (see 0x0001C00E8BA3):

 License->field_0[v13].entry_size = v10->entry_size;
 License->field_0[v13].entry_field2 = v10->field_2;
 License->field_0[v13].entry_ptr = (__int64)&v10->first_byte;

Where v13 is the internal type associated with an entry type in the license file. In most of the case below, the root cause of the problem is reading data in entry_ptr without verifying first that entry_size accomodates for the extent of the data read (e.g. read 2 bytes of data at offset 8 without verifying that entry_size >= 8+2)

Offset 4

There are multiple out-of-bounds vulnerabilities for the type 0xC9 (internal type 15). A truncated blob of this type can be appended at the end of a valid license file and trigger an OOB read when installing the license and/or when it is being used. In this case the vulnerability is an OOB-read of 4 bytes at offset 4 in the license blob of type 0xC9. Such a blob would look like: {0xc9, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD }

The vulnerable function looks like this:

   //0000001C00E7714
   __int64 __fastcall __spoils<rax,rdx> LicenseStruct_get_DeviceLicenseIssueTime_deref_at_4_for_type15(LicenseStruct *a1)
{
  __int64 entry_ptr; // rdx
  __int64 result; // rax

  entry_ptr = a1->field_0[15].entry_ptr;
  result = 0i64;
  if ( entry_ptr )
    return *(unsigned int *)(entry_ptr + 4); //OOB Read here
  return result;
}

Offset 8

There are multiple out-of-bounds vulnerabilities for the type 0xC9 (internal type 15). A truncated blob of this type can be appended at the end of a valid license file and trigger an OOB read when installing the license and/or when it is being used. In this case the vulnerability is an OOB-read of 2 bytes at offset 8 in the license blob of type 0xC9. Such a blob would look like: {0xc9, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22, 0x33, 0x44 }

The vulnerable function looks like this:

//00000001C00E75A4
__int64 __fastcall __spoils<rdx,rax> License_get_dref8_for_type15(LicenseStruct *a1)
{
  __int64 entry_ptr; // rdx
  __int64 result; // rax

  entry_ptr = a1->field_0[15].entry_ptr;
  result = 0i64;
  if ( entry_ptr )
    return *(unsigned __int16 *)(entry_ptr + 8);  //OOB Read here
  return result;
}

CVE-2024-38062 - Offset 2

There are multiple out-of-bounds vulnerabilities for the type 0xC9 (internal type 15). A truncated blob of this type can be appended at the end of a valid license file and trigger an OOB read when installing the license and/or when it is being used. Such a blob would look like: {0xc9, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAA }

The vulnerable function is as such:

    //001C00E7848 
    __int64 __fastcall __spoils<rax,rdx> LicenseStruct_get_dref_at_2_for_type15_licensetype(LicenseStruct *a1)
{
  __int64 entry_ptr; // rdx
  __int64 result; // rax

  entry_ptr = a1->field_0[15].entry_ptr;
  result = 0i64;
  if ( entry_ptr )
    return *(unsigned __int16 *)(entry_ptr + 2); //OOB Read here
  return result;
}
TIMELINE

2024-04-11 - Vendor Disclosure
2024-07-09 - Vendor Patch Release
2024-08-13 - Public Release

Credit

Discovered by Philippe Laulheret of Cisco Talos.