(CVE-2025-55336) Windows Cloud Files Mini Filter Driver Information Disclosure

CVE: CVE-2025-55336

Affected Versions: Windows 10 (21H2, 22H2, 1809), Windows 11 (22H2, 23H2, 24H2, 25H2), Windows Server 2019, 2022, 2022 23H2, 2025

CVSS3.1: 5.5 (Medium) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N

Summary

Product Windows Cloud Files Mini Filter Driver (cldflt.sys)
Vendor Microsoft
Severity Medium — an unprivileged local attacker may exploit this to leak kernel addresses
Affected Versions Windows 10 (21H2, 22H2, 1809), Windows 11 (22H2, 23H2, 24H2, 25H2), Windows Server 2019 / 2022 / 2022 23H2 / 2025
Tested Versions Windows 11 24H2 (Build 26100.2161)
CVE Identifier CVE-2025-55336
CVE Description A side channel in the Windows Cloud Files Mini Filter Driver allows an unprivileged local attacker to leak the EPROCESS kernel address of the current process
CWE Classification(s) CWE-200: Exposure of Sensitive Information to an Unauthorized Actor

CVSS3.1 Scoring System

Base Score: 5.5 (Medium) Vector String: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N

Metric Value
Attack Vector (AV) Local
Attack Complexity (AC) Low
Privileges Required (PR) Low
User Interaction (UI) None
Scope (S) Unchanged
Confidentiality (C) High
Integrity (I) None
Availability (A) None

Product Background

cldflt.sys is a filesystem minifilter driver that acts as a proxy between user applications and cloud sync engines. The sync engine handles on-demand downloading and uploading of data, while cldflt.sys works with the Windows Shell to present cloud-backed files as if they were locally available. It underpins technologies such as OneDrive.

Description of the vulnerability

An insecure comparison when processing a request to abort hydration (command 0x4002) allows a side channel attack to leak the EPROCESS address of the current process.

cldflt.sys creates a filter communication port \\CLDMSGPORT for usermode clients via CldPortInitialize():

RtlInitUnicodeString(&DestinationString, L"\\CLDMSGPORT");
status = SeSddlSecurityDescriptorFromSDDL((unsigned __int16 *)v7, v1, &P);
HsmDbgBreakOnStatus(status);
if ( status >= 0 )
{
  ObjectAttributes.RootDirectory = 0LL;
  ObjectAttributes.SecurityQualityOfService = 0LL;
  ObjectAttributes.ObjectName = &DestinationString;
  ObjectAttributes.SecurityDescriptor = P;
  ObjectAttributes.Length = 48;
  ObjectAttributes.Attributes = 576;
  status = FltCreateCommunicationPort(
            Globals,
            &ServerPort,
            &ObjectAttributes,
            0LL,
            (PFLT_CONNECT_NOTIFY)CldiPortNotifyConnect,
            (PFLT_DISCONNECT_NOTIFY)CldiPortNotifyDisconnect,
            (PFLT_MESSAGE_NOTIFY)CldiPortNotifyMessage,
            0x7FFFFFFF);

When a client connects, CldiPortNotifyConnect() allocates a ClientInfo structure for each open handle:

clientInfo = (CLDFLT_PORT_CLIENT_INFO *)ExAllocatePool2(256LL, 0x118LL, 0x63706C43LL);

This structure contains various fields including the EPROCESS address of the client process.

Command 0x4002 is dispatched to CldiPortProcessAbortHydration(), which iterates over all registered clients and compares their stored EPROCESS pointer against Element13 — a value supplied directly from usermode:

FltAcquirePushLockSharedEx(&g_ClientInfoListLock, 0LL);
curClientInfo = g_ClientInfoList.Flink;
if ( g_ClientInfoList.Flink != &g_ClientInfoList )
{
    elem13Val_ = elem13Val;
    elem7Val_  = elem7Val;
    do
    {
        if ( !elem13Val_ || curClientInfo->ClientProcess_ == elem13Val_ ) // Insecure comparison
        {
            FltAcquirePushLockSharedEx(&curClientInfo->Lock, 0LL);
            if ( elem4Val )
            {
                PortrootData = CldiPortLookupSyncRootNoLock(curClientInfo, elem4Val);
                if ( PortrootData )
                    CldSyncAbortOperation(elem14Val, PortrootData, elem7Val_, elem12Val, elem5Val);
            }
            else
            {
                elem7Val = 0LL;
                while ( 1 )
                {
                    portrootElem = RtlEnumerateGenericTableWithoutSplayingAvl(&curClientInfo->AvlTable, &elem7Val);
                    if ( !portrootElem )
                        break;
                    CldSyncAbortOperation(elem14Val, portrootElem[1], elem7Val_, elem12Val, elem5Val);
                }
            }
            FltReleasePushLockEx(&curClientInfo->Lock, 0LL);
            elem13Val_ = elem13Val;
        }
        curClientInfo = curClientInfo->Link.Flink;
    }
    while ( curClientInfo != &g_ClientInfoList );

When a match is found, CldSyncAbortOperation() waits for any in-progress operations on the syncroot to complete before aborting. This creates a timing oracle: by issuing a dir command on a connected syncroot directory and observing whether the abort call hangs, an attacker can determine whether their candidate address matched the real EPROCESS pointer.

Exploitation

EPROCESS structures are always allocated on a 0x40-byte alignment on the latest Windows 11 builds. The segment servicing EPROCESS allocations is empirically observed to fall within one of two ranges:

  • 0xffffXX80000000000xffffXX9000000000
  • 0xffffXX00000000000xffffXX1000000000

The XX byte ranges from 0x80 to 0xf0 (112 variations). With two threads per variation and 112 concurrent thread pairs, the full search space of ~1,073,741,824 candidates can be exhausted in under 30 minutes on an 8-core VM, yielding the EPROCESS address of one of the exploit threads — a powerful primitive for subsequent exploitation.

Proof of Concept

The PoC demonstrates the oracle for one of the 112 address variations. On success it pauses and prints the EPROCESS address of the running process.

Credit

Chen Le Qi (@cplearns2h4ck) of STAR Labs SG Pte. Ltd.

Timeline