标题 | 简介 | 类型 | 公开时间 | ||||||||||
|
|||||||||||||
|
|||||||||||||
详情 | |||||||||||||
[SAFE-ID: JIWO-2024-3214] 作者: 大猪 发表于: [2022-11-06]
本文共 [603] 位读者顶过
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 EnvironmentAll analysis and debugging in this two-part blog series were conducted in the following environment:
Introduction to CLFS InternalsThe 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:
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.
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:
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:
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:
The eState field is located at offset 0x78 in the CLFS_CLIENT_CONTEXT structure, and can be one of the following values:
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:
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 CrashIn 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.
Root Cause AnalysisNow 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.
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:
Next, the breakpoint at CClfsLogFcbPhysical::AllocContainer is set as follows:
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:
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:
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.
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.
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.
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.
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
ConclusionIn 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!
MitigationAll 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
Cloud Sandbox Detection
Referenceshttps://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://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/clfs-terminology |