Insights and Research

Technical Analysis of Windows CLFS Zero-Day Vulnerability CVE-2022-37969 - Part 1: Root Cause Analysis

Windows Common Log File System Driver Privilege Escalation Zero-Day Vulnerability CVE-2022-37969

On September 2, 2022, Zscaler Threatlabz captured an in-the-wild 0-day exploit in the Windows Common Log File System Driver (CLFS.sys) and reported this discovery to Microsoft. In the September Tuesday patch, Microsoft fixed this vulnerability that was identified as CVE-2022-37969, which is a Windows Common Log File System Driver elevation of privilege vulnerability. An attacker who successfully exploits this vulnerability may gain SYSTEM privileges. The 0-day exploit can execute the privilege escalation successfully on Windows 10 and Windows 11 prior to the September patch. The cause of the vulnerability is due to the lack of a strict bounds check for the SignaturesOffset field in the Base Block for the base log file (BLF) in CLFS.sys. A specially crafted client context array and a fake Client Context in the base log file, can exploit CLFS to overwrite the SignaturesOffset field with an abnormal value. This leads to a validation bypass for the cbSymbolZone field when a Symbol is allocated. Thus, an invalid cbSymbolZone field can produce an out-of-bound write at an arbitrary offset. In this two-part blog series, we will demystify the vulnerability and the 0-day exploit discovered in-the-wild. The blogs consist of two parts: an analysis of the root cause, and an analysis of the exploit. In this blog, we first present a detailed analysis of the root cause for CVE-2022-37969.

 

Debugging Environment

All analysis and debugging in this two-part blog series were conducted in the following environment:

 

Windows 11 21H2 version 22000.918
CLFS.sys 10.0.22000.918

 

Introduction to CLFS Internals

The Common Log File System (CLFS) is a general-purpose logging subsystem that can be used by applications running in both kernel mode and user mode for building high-performance transaction logs, and is implemented in the driver CLFS.sys. The Common Log File System generates transaction logs in a base log file (BLF). The concepts and terminology introductions for CLFS are specified in the official documentation from Microsoft.

Prior to using CLFS, a log file is created using the CreateLogFile API. A log file is composed of a base log file that consists of metadata blocks, and several containers that store the actual data. The AddLogContainer API is used to add a container to the physical log that is associated with the log handle.

Figure 1 illustrates the BLF format based on the official CLFS documentation and Alex Ionescu’s unofficial documentation.

Figure 1. Base Log File (BLF) Format

A base log file is made up of six different metadata blocks, which are the control block, base block, and truncate block along with their corresponding block shadows. The three types of records (Control Record, Base Record, and Truncate Record) can reside in these blocks. This blog only focuses on the Base Record that is relevant to this vulnerability. The Base Record comprises the symbol tables that store information on the client contexts, container contexts, and security contexts associated with the base log file.

Every log block begins with a log block header, with the structure defined below:

 

typedef struct _CLFS_LOG_BLOCK_HEADER
{
    UCHAR MajorVersion;
    UCHAR MinorVersion;
    UCHAR Usn;
    CLFS_CLIENT_ID ClientId;
    USHORT TotalSectorCount;
    USHORT ValidSectorCount;
    ULONG Padding;
    ULONG Checksum;
    ULONG Flags;
    CLFS_LSN CurrentLsn;
    CLFS_LSN NextLsn;
    ULONG RecordOffsets[16];
    ULONG SignaturesOffset;
} CLFS_LOG_BLOCK_HEADER, *PCLFS_LOG_BLOCK_HEADER;

 

The memory layout of the CLFS_LOG_BLOCK_HEADER structure whose size is 0x70 bytes has been illustrated in Figure 1. The SignaturesOffset field is the offset of an in-memory array that is used to store all sector signatures. The array should be located on the last sector in each block. The sector signature is located at the end of every sector (size: 0x200) and consists of a Sector Block Type (1 byte) and Usn (1 byte). The following enumerates the types of a sector.

 

const UCHAR SECTOR_BLOCK_NONE   = 0x00;
const UCHAR SECTOR_BLOCK_DATA   = 0x04;
const UCHAR SECTOR_BLOCK_OWNER  = 0x08;
const UCHAR SECTOR_BLOCK_BASE   = 0x10;
const UCHAR SECTOR_BLOCK_END    = 0x20;
const UCHAR SECTOR_BLOCK_BEGIN  = 0x40;

 

In Figure 1, the Base Block starts at offset 0x800 and ends at offset 0x71FF in a .BLF file and starts with a Log Block Header (0x70 bytes), followed by the Base Record Header, followed by records. The Base Record Header can be represented by the CLFS_BASE_RECORD_HEADER structure described in Figure 2.

Figure 2. The definition of the CLFS_BASE_RECORD_HEADER structure

The structure layout of the CLFS_BASE_RECORD_HEADER structure is illustrated in Figure 3.

Figure 3. The structure layout of Base Record Header in a .BLF file

The Base Record begins with a header (CLFS_BASE_RECORD_HEADER) whose size is 0x1338 bytes, followed by related context data. In the CLFS_BASE_RECORD_HEADER structure, some important fields related to this vulnerability are described below:

  • rgClients represents the array of offsets that point to the Client Context object. 
  • rgContainers represents the array of offsets that point to the Container Context object. 
  • cbSymbolZone represents the next free available offset for a new symbol in the symbol zone.

In the Base Record, the Client Context, Container Context, and Shared Security Context are represented by symbols, which are preceded by the CLFSHASHSYM structure defined below:

 

typedef struct _CLFS_NODE_ID {
  ULONG cType;
  ULONG cbNode;
} CLFS_NODE_ID, *PCLFS_NODE_ID;

typedef struct _CLFSHASHSYM
{
    CLFS_NODE_ID cidNode;
    ULONG ulHash;
    ULONG cbHash;
    ULONGLONG ulBelow;
    ULONGLONG ulAbove;
    LONG cbSymName;
    LONG cbOffset;
    BOOLEAN fDeleted;
} CLFSHASHSYM, *PCLFSHASHSYM;

 

The memory layout of the CLFSHASHSYM structure is illustrated in Figure 4, followed by various context objects.

Figure 4. CLFSHASHSYM structure (symbol header)

In the Base Record, the client context is used to identify a client for a log file. At least one client context can be created in a base log file. The client context is represented by the CLFS_CLIENT_CONTEXT structure defined below:

 

typedef struct _CLFS_CLIENT_CONTEXT
{
    CLFS_NODE_ID cidNode;
    CLFS_CLIENT_ID cidClient;
    USHORT fAttributes;
    ULONG cbFlushThreshold;
    ULONG cShadowSectors;
    ULONGLONG cbUndoCommitment;
    LARGE_INTEGER llCreateTime;
    LARGE_INTEGER llAccessTime;
    LARGE_INTEGER llWriteTime;
    CLFS_LSN lsnOwnerPage;
    CLFS_LSN lsnArchiveTail;
    CLFS_LSN lsnBase;
    CLFS_LSN lsnLast;
    CLFS_LSN lsnRestart;
    CLFS_LSN lsnPhysicalBase;
    CLFS_LSN lsnUnused1;
    CLFS_LSN lsnUnused2;
    CLFS_LOG_STATE eState; //+0x78
    union
    {
        HANDLE hSecurityContext;
        ULONGLONG ullAlignment;
    };
} CLFS_CLIENT_CONTEXT, *PCLFS_CLIENT_CONTEXT;

 

The eState field is located at offset 0x78 in the CLFS_CLIENT_CONTEXT  structure, and can be one of the following values:

 

typedef UCHAR CLFS_LOG_STATE, *PCLFS_LOG_STATE;
const CLFS_LOG_STATE CLFS_LOG_UNINITIALIZED    = 0x01;
const CLFS_LOG_STATE CLFS_LOG_INITIALIZED      = 0x02;
const CLFS_LOG_STATE CLFS_LOG_ACTIVE           = 0x04;
const CLFS_LOG_STATE CLFS_LOG_PENDING_DELETE   = 0x08;
const CLFS_LOG_STATE CLFS_LOG_PENDING_ARCHIVE  = 0x10;
const CLFS_LOG_STATE CLFS_LOG_SHUTDOWN         = 0x20;
const CLFS_LOG_STATE CLFS_LOG_MULTIPLEXED      = 0x40;
const CLFS_LOG_STATE CLFS_LOG_SECURE           = 0x80;

 

In the Base Record, the container context is related to adding a container file for a base log file, which is represented by the CLFS_CONTAINER_CONTEXT structure defined below:

 

typedef struct _CLFS_CONTAINER_CONTEXT
{
    CLFS_NODE_ID cidNode; //8 bytes
    ULONGLONG cbContainer; //8 bytes
    CLFS_CONTAINER_ID cidContainer; // 4 bytes
    CLFS_CONTAINER_ID cidQueue; // 4 bytes
    union
    {
        CClfsContainer* pContainer; //8 bytes
        ULONGLONG ullAlignment;
    };
    CLFS_USN usnCurrent;
    CLFS_CONTAINER_STATE eState;
    ULONG cbPrevOffset; //4 bytes
    ULONG cbNextOffset; //4 bytes
} CLFS_CONTAINER_CONTEXT, *PCLFS_CONTAINER_CONTEXT;

 

The field pContainer is a kernel pointer to the CClfsContainer object representing the container at runtime, which is located at offset 0x18 in the CLFS_CONTAINER_CONTEXT structure. Figure 5 shows the memory layout of the CLFS_CONTAINER_CONTEXT structure.

Figure 5. The memory layout of the CLFS_CONTAINER_CONTEXT structure

 

Reproduce BSOD Crash

In order to determine the root cause of CVE-2022-37969, ThreatLabz developed a Proof-of-Concept (PoC) that triggers a “blue screen of death” (BSOD) crash stably.  Figure 6 shows detailed crash information after triggering the vulnerability.

Figure 6. CVE-2022-37969 crash information in WinDbg

As shown in Figure 6, the register rdi points to an invalid memory address. The register rdi stores a pointer to the CClfsContainer object. In the CLFS_CONTAINER_CONTEXT structure described before, the field pContainer is a pointer to the CClfsContainer object and located at offset 0x18 in the memory layout. Based on the location of the crash, the field pContainer in the CLFS_CONTAINER_CONTEXT structure has been corrupted. This leads to a BSOD crash when this pointer is dereferenced.

Figure 7 shows a comparison between a properly structured base log file (.BLF) and a specially crafted base log file that is used to trigger the CVE-2022-37969 vulnerability.

Figure 7. A specially crafted Base Log File (BLF) for CVE-2022-37969

After this base log file is created, specific bytes including the field SignatureOffset, client context offset array, cbSymbol, a fake client context, etc must be modified accordingly. As shown in Figure 7, all mutated bytes are located in the Base Log Record (offset: 0x800 ~ 0x81FF in the .blf file). The modifications to the .blf file are listed in Figure 8.

Figure 8. Modifications to the .BLF file to trigger CVE-2022-37969

Proof-of-Concept code to trigger CVE-2022-37969 is shown in Figure 9.

Figure 9. Proof-of-Concept code snippet for CVE-2022-37969

The Proof-of-Concept of CVE-2022-37969 involves the following steps.

  1. Create a base log file MyLog.blf in the folder C:\Users\Public\ via the CreateLogFile API
  2. Create dozens of base log files named MyLog_xxx.blf. It’s crucial to make the offset between the two subsequently created memory regions representing the Base Block constant (where the constant offset equals 0x11000 bytes). The original 0-day sample leveraged an advanced method to produce the offset constant. Part 2 of this blog series will cover this technique. The ThreatLabz PoC uses a constant count to create base log files, and therefore, might not make the offset between the two subsequently created memory regions constant. Hence, the constant value has to be tweaked for this situation.
  3. Modify a couple of bytes at specific offsets in MyLog.blf, recalculate the new CRC32 checksum for the Base Block, then write the new checksum value at offset 0x80C. Then, open the existing base log file MyLog.blf.
  4. Call the CreateLogFile API to create a base log file MyLxg_xxx.blf in the folder C:\Users\Public\.
  5. Call the AddLogContainer API to add a log container for the base log file MyLxg_xxx.blf created in Step 4.
  6. Call GetProcAddress(LoadLibraryA("ntdll.dll"), "NtSetInformationFile") to obtain the function address of the NtSetInformationFile API.
  7. Call the AddLogContainer API to add a log container for the base log file MyLog.blf opened in Step 3.
  8. Call NtSetInformationFile(v55, (PIO_STATUS_BLOCK)v33, v28, 1, (FILE_INFORMATION_CLASS)13), where the last parameter is the type of FileInformationClass. When the value is FileDispositionInformation (13), the function will delete the file when it is closed or will cancel a previously requested deletion.
  9. Call the CloseHandle API to close the handle of the base log file MyLxg_xxx.blf, to trigger this vulnerability.

 

Root Cause Analysis

Now that Proof-of-Code has been introduced, the root cause can be analyzed. In Figure 9, Step 3 calls the CreateLogFile API whose 5th parameter is 4 (OPEN_ALWAYS), which opens an existing file or creates the file if it does not exist. In this case, the existing base log file MyLog.blf is opened. In Step 4, the code calls the CreateLogFile API to create a new base log file named MyLxg_xxx.blf. In Steps 5 and 7, respectively, the code calls AddLogContainer to add a container to the physical log that is associated with the log handle. 

First, let’s take a closer look at how the CLFS driver handles the request of adding a log container when the AddLogContainer() function is called in user space. The following breakpoint can be set to trace the process of handling this request.

 

bu CLFS!CClfsRequest::AllocContainer

 

In CLFS.sys, the CClfsRequest class is responsible for handling the requests from user space. The CClfsRequest::AllocContainer function is used to handle the request of adding a container to the physical log. The CClfsRequest::AllocContainer function calls CClfsLogFcbPhysical::AllocContainer whose declaration is shown below:

 

CClfsLogFcbPhysical::AllocContainer(CClfsLogFcbPhysical *this, _FILE_OBJECT *,_UNICODE_STRING *,unsigned __int64 *)

 

Next, the breakpoint at CClfsLogFcbPhysical::AllocContainer is set as follows:

 

bu CLFS!CClfsLogFcbPhysical::AllocContainer

 

In Step 5, when the code calls the AddLogContainer function, the breakpoint at CClfsLogFcbPhysical::AllocContainer is triggered. When the breakpoint is hit, let’s inspect the this pointer of the CClfsLogFcbPhysical class. The this pointer points to the CClfsLogFcbPhysical object. As shown in Figure 10, the register rcx stores the this pointer of the CClfsLogFcbPhysical class.

Figure 10. Inspection of the this pointer for the CClfsLogFcbPhysical class at CClfsLogFcbPhysical::AllocContainer

The address of vftable in the CClfsLogFcbPhysical class is stored at offset 0x00. At offset this+0x30, a pointer to the log name is stored. At offset this+0x2B0, the this pointer to CClfsBaseFilePersisted class is stored. Once in memory, a CLFS Base Log File is represented by a CClfsBaseFile class, which can be further extended by a CClfsBaseFilePersisted class. In the this pointer of the CClfsBaseFilePersisted class, at offset 0x30 a pointer to a heap buffer is stored whose size is 0x90 bytes. Furthermore, in the heap buffer, a pointer to the Base Block is stored at offset 0x30. Additionally, in the this pointer of CClfsBaseFilePersisted, a pointer to the CClfsContainer object is stored at offset 0x1C0. After a container is added successfully, we can check the CLFS_CONTAINER_CONTEXT structure described in Figure 5 in memory as shown in Figure 11.

Figure 11. The CLFS_CONTAINER_CONTEXT structure after a container is added successfully

At offset 0x1C0 in the CClfsBaseFilePersisted object, a pointer to the CClfsContainer object is stored which comes from the field pContainer in the CLFS_CONTAINER_CONTEXT structure. At this point, a memory write breakpoint at CLFS_CONTAINER_CONTEXT+0x18 can be set to trace when the pointer to the CClfsContainer object in the CLFS_CONTAINER_CONTEXT structure is corrupted. Another memory write breakpoint at offset 0x1C0 in the CClfsBaseFilePersisted object can be set as follows:

 

1: kd> ba w8 ffffc80c`cc86a4f0 //CLFS_CONTAINER_CONTEXT: +0x18
1: kd> ba w8 ffffb702`3cf251c0 //CClfsBaseFilePersisted: +0x1C0

 

In Step 7, when the code calls the AddLogContainer function, the breakpoints at CLFS!CClfsRequest::AllocContainer and CLFS!CClfsLogFcbPhysical::AllocContainer are hit again. At this point, let’s inspect the this pointer (see Figure 12) of the CClfsLogFcbPhysical class. It's worth noting that the SignaturesOffset field, which was originally set to 0x00000050 in the crafted MyLog.blf file, has been set to 0xFFFF0050 in memory.

 

Figure 12. Inspection of the this pointer for CClfsLogFcbPhysical class

In WinDbg, let’s continue to run the code. The memory write breakpoint “ba w8 ffffc80c`cc86a4f0” will be hit, and the CLFS_CONTAINER_CONTEXT structure in the Base Record produces an out-of-bound write, which leads to a corrupted pointer in the CClfsContainer object shown in Figure 13.

Figure 13. Corrupting the pointer to the CClfsContainer object in CLFS_CONTAINER_CONTEXT structure

Based on the above back stack trace, the memset function was called to trigger the memory write breakpoint in the CClfsBaseFilePersisted::AllocSymbol function. Figure 14 shows the pseudocode of the CClfsBaseFilePersisted::AllocSymbol function.

Figure 14. The pseudocode of the CClfsBaseFilePersisted::AllocSymbol function

This function is described as follows:

  1. The variable v9 is the value of the field cbSymbolZone in the Base Record Header. The field cbSymbolZone was set to be an abnormal value described in Figure 8. This value was modified from 0x000000F8 to 0x0001114B. 
  2. Validate the cbSymbolZone field. In order to perform an out-of-bound write in Step 4,  this validation must be bypassed. The SignaturesOffset field, which was originally 0x00000050 in the crafted MyLog.blf file described in Figure 7, has been overwritten with a large number (0xFFFF0050) in memory. Therefore, even when the cbSymbolZone field is set to an abnormal value, the validation for the cbSymbolZone field can still be bypassed. Determining the reason that the SignaturesOffset field is set to 0xFFFF0050 from 0x00000050 is the root cause of CVE-2022-37969.
  3. The variable v10 is equal to v9 plus the BaseLogRecord plus 0x1338, and the variable v9 is equal to 0x0001114b, which leads to an invalid offset at v10
  4. The function calls memset() which causes an out-of-bound write at an invalid offset v10, which falls into the CLFS_CONTAINER_CONTEXT structure in the Base Record for MyLxg_xxx.blf, leading to a corrupted CClfsContainer pointer at offset 0x18 in the CLFS_CONTAINER_CONTEXT structure.

Figure 15 shows how the out-of-bound write occurs, leading to a corrupted pointer in the CClfsContainer object.

Figure 15. Explanation of the out-of-bound write caused by CVE-2022-37969

So far, we have discussed why an out-of-bound write occurred and how the pointer to CClfsContainer object in the CLFS_CONTAINER_CONEXT structure was corrupted. Next, let’s take a look at when the corrupted CClfsContainer pointer will be dereferenced. After that, we will go back to figure out why the SignaturesOffset field in memory is set to 0xFFFF0050 from 0x00000050.

When the CloseHandle function is called in user space, CClfsRequest::Close(PIRP Irp) is responsible for handling this request. In the kernel, another memory breakpoint (0x1c0+CClfsBaseFilePersisted) is hit in the ClfsBaseFilePersisted::WriteMetadataBlock function as shown in Figure 16.

Figure 16. Memory breakpoint(0x1c0+CClfsBaseFilePersisted) hit in ClfsBaseFilePersisted::WriteMetadataBlock

The corrupted pointer is copied to the CClfsContainer object in the CLFS_CONTAINER_CONTEXT structure in the Base Record to the offset 0x1c0 in the CClfsBaseFilePersisted object.

Figure 17 shows the pseudocode of the ClfsBaseFilePersisted::WriteMetadataBlock function after the corrupted pointer to CClfsContainer is stored at offset 0x1c0 in the CClfsBaseFilePersisted object. The code zeros out the field of the pointer to the CClfsContainer object in the CLFS_CONTAINER_CONTEXT structure. After decoding the block, the pointer to the CClfsContainer object is restored in the CLFS_CONTAINER_CONTEXT structure from the offset 0x1c0 in the CClfsBaseFilePersisted object. 

Figure 17. The pseudocode of the ClfsBaseFilePersisted::WriteMetadataBlock function

Finally, the breakpoint at CLFS!CClfsBaseFilePersisted::RemoveContainer can be set to trace when the corrupted pointer to the CClfsContainer object in the CLFS_CONTAINER_CONTEXT structure in the Base Record is dereferenced. 

Figure 18 shows the pseudocode of the CClfsBaseFilePersisted::RemoveContainer function.

Figure 18.The pseudocode of the CClfsBaseFilePersisted::RemoveContainer function

The following steps are executed in the CClfsBaseFilePersisted::RemoveContainer function.

  1. Obtains the container context offset at offset 0x398 in the Base Record.
  2. Calls CClfsBaseFile::GetSymbol to obtain the pointer to the CLFS_CONTAINER_CONTEXT structure and stores it in the 4th parameter.
  3. Assigns the value of the pointer to the CClfsContainter object in the CLFS_CONTAINER_CONTEXT structure obtained in Step 2 to the local variable v13.
  4. Zeros out the pointer to the CClfsContainter object in the CLFS_CONTAINER_CONTEXT structure.
  5. Calls CClfsContainer::Remove and CClfsContainer::Release, in turn, to remove the associated container log file and release the CClfsContainer object.

Dereferencing the corrupted pointer to the CClfsContainter object leads to a memory violation. Figure 19 shows the crash information in WinDbg, consequently producing the BSOD crash.

Figure 19. Dereferencing the corrupted pointer to the CClfsContainter object

As described in Figure 14, an out-of-bound write occurred in the CClfsBaseFilePersisted::AllocSymbol function. Before calling the memset function, the validation for the cbSymbolZone field has been bypassed due to the SignaturesOffset field in memory being overwritten with 0xFFFF0050. The SignaturesOffset field in memory in the base block can be overwritten with 0xFFFF0050 in the process of handling the request of calling the CreateLogFile API to open the specially crafted base log file MyLog.blf described in Step 3 in Figure 9.

When the CreateLogFile function is called in user space, CLFS!CClfsRequest::Create is responsible for handling this request. When the CreateLogFile function is used to open an existing base log file, the function CClfsLogFcbPhysical::Initialize is called in CLFS.sys. Figure 20 shows the pseudo-code snippet of the CClfsLogFcbPhysical::Initialize function.

Figure 20. The pseudo-code snippet of the CClfsLogFcbPhysical::Initialize

This function is described as follows:

1. Call the CClfsBaseFilePersisted::OpenImage function to create a bigpool (size: 0x7a00) for the base block in a base log file. The following function calls can be followed to enter the CClfsBaseFilePersisted::ReadMetadataBlock function.

 

CClfsBaseFilePersisted::OpenImage -> CClfsBaseFilePersisted::ReadImage -> CClfsBaseFile::AcquireMetadataBlock -> CClfsBaseFilePersisted::ReadMetadataBlock

 

Figure 21 shows the pseudo-code snippet of the CClfsBaseFilePersisted::ReadMetadataBlock function, where  the ExAllocatePoolWithTag(PagedPoolCacheAligned, 0x7a00, 0x73666C43u) is called to create a bigpool to store the base block, followed by initializations, and then the ClfsDecodeBlock function is called to decode the block. In the ClfsDecodeBlock function,  the ClfsDecodeBlockPrivate function is called to parse the sector signatures array that is located at offset 0x50 (the value of SignaturesOffset) in the base block. Two bytes are required to overwrite the sector signature of each sector. These two bytes 0x0050 at offset 0x68 in the base block can be overwritten to the offset 0x19FE (0xC*0x200+0x1FE) where the sector signature of the 13th section is stored. At this point, we can set two memory write breakpoints which are located at base_block+0x68 and base_block+0x200*0xE-0x8. The purpose of setting these two memory write breakpoints is to trace when the sector signature of the 14th sector in the base block is overwritten, and the SignaturesOffset field in the base block is overwritten to 0xFFFF0050.

 

1: kd> ba w8 ffffd08b`51c03000+0x68 //base_block+0x68
1: kd> ba w8 ffffd08b`51c03000+0x200*0xE-0x8 // base_block+0x200*0xe-0x8

 

Figure 21. The pseudo-code snippet of the CClfsBaseFilePersisted::ReadMetadataBlock function

 

2. Call the CClfsBaseFile::AcquireClientContext function to acquire the client context from the base block. As shown in Figure 7, the first offset in the Client Context offset array located at offset 0x9A8 in the base log file is specially crafted. A fake Client Context is located at offset 0x23A0 in the base log file. The eState field located at offset 0x78 in the fake Client Context structure is set to 0x20 (CLFS_LOG_SHUTDOWN).

3. Check if the eState field is not CLFS_LOG_SHUTDOWN or the base log is a multiplexed log.

4. Call the CClfsLogFcbPhysical::ResetLog function due to the condition being false in Step 3. Figure 22 shows the pseudo-code snippet of the CClfsLogFcbPhysical::ResetLog function. The 8 bytes located at base_block+0x1BF8 are set to 0xFFFFFFFF00000000. The sector signature is located at offset base_block+0x1BFC. Therefore, the sector signature is overwritten with 0xFFFF. Figure 23 demonstrates that the sector signature is overwritten in WinDbg.

Figure 22.  The pseudo-code snippet of the CClfsLogFcbPhysical::ResetLog function

Figure 23.  The sector signature is overwritten with 0xFFFF

5. Call the CClfsLogFcbPhysical::FlushMetaData function. The following function calls can be followed to enter the CLFS!ClfsEncodeBlockPrivate function.

 

CLFS!CClfsLogFcbPhysical::FlushMetadata -> CLFS!CClfsBaseFilePersisted::FlushImage -> CLFS!CClfsBaseFilePersisted::WriteMetadataBlock -> CLFS!ClfsEncodeBlock -> CLFS!ClfsEncodeBlockPrivate

 

Figure 24 shows the pseudo-code snippet of the CLFS!ClfsEncodeBlockPrivate function.

Figure 24. The pseudo-code snippet of the CLFS!ClfsEncodeBlockPrivate function

The code above acquires the sector signature from each sector in the base block and overwrites the sector signature array with the sector signature. The sector signature array is located at offset 0x50 and overlaps the SignaturesOffset field in the base block. The sector signature of the 14th sector has been set to 0xFFFF as shown in Figure 23. Therefore, two bytes (0xFFFF) are overwritten at offset 0x6C (0x50+0xE*2) in the base block. At this time, the SignaturesOffset field has the value 0xFFFF0050 as shown in Figure 25.

Figure 25. The SignaturesOffset field is overwritten with 0xFFFF0050

In the end, we summarize the process of overwriting the SignaturesOffset field in Figure 26.

Figure 26.  The process of overwriting the SignaturesOffset field

 

Conclusion

In this blog, ThreatLabz presented a detailed root cause analysis for CVE-2022-37969, which is due to improper bounds checking for the SignaturesOffset field in the Base Block for the base log file (BLF) in CLFS.sys. A specially crafted client context array and a fake Client Context in the base log file, can exploit CLFS to overwrite the SignaturesOffset field with an abnormal value. This leads to a validation bypass for the cbSymbolZone field when a Symbol is allocated. Thus, an invalid cbSymbolZone field can produce an out-of-bound write at an arbitrary offset. Therefore, the pointer to the CClfsContainer object can be corrupted. When dereferenced, the corrupted pointer to the CClfsContainer object causes a memory violation that triggers a BSOD crash. In Part 2, we will present the analysis of the 0-day exploit that leverages this vulnerability for privilege escalation. Stay tuned!

 

Mitigation

All users of Windows are encouraged to upgrade to the latest version. Zscaler’s Advanced Threat Protection and Advanced Cloud Sandbox can protect customers against the in-the-wild 0-day exploit of CVE-2022-37969.

Win32.GenExploit.LogFile
Win32.Exploit.CVE-2022-37969

 

Cloud Sandbox Detection

 

References

https://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-37969

https://github.com/ionescu007/clfs-docs/blob/main/README.md

https://i.blackhat.com/USA-22/Thursday/us-22-Jin-The-Journey-Of-Hunting-ITW-Windows-LPE-0day.pdf

https://www.slideshare.net/PeterHlavaty/deathnote-of-microsoft-windows-kernel

https://www.pixiepointsecurity.com/blog/nday-cve-2022-24521.html

https://blog.exodusintel.com/2022/03/10/exploiting-a-use-after-free-in-windows-common-logging-file-system-clfs/

https://learn.microsoft.com/en-us/previous-versions/windows/desktop/clfs/common-log-file-system-portal

https://learn.microsoft.com/en-us/previous-versions/windows/desktop/clfs/log-types

https://learn.microsoft.com/en-us/previous-versions/windows/desktop/clfs/creating-a-log-file

https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/introduction-to-the-common-log-file-system

https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/clfs-terminology

 

 

Stay up to date with the latest digital transformation tips and news.