This is part five of our study about the Common Log File System (CLFS) and five vulnerabilities in this Windows OS component that have been used in ransomware attacks throughout the year. Please read the previous parts first if you haven’t already.
You can skip to the other parts using this table of contents or using the link at the end of this part.
Exploit #4 – CVE-2023-23376
The October changes complicated the exploitation of the GENERAL block, and the author of the previously discussed exploits switched to exploiting the CONTROL block. CVE-2023-23376 was discovered as a zero-day in the wild by the Microsoft Threat Intelligence Center (MSTIC) and the Microsoft Security Response Center (MSRC). It was fixed in February 2023.
To discuss this vulnerability, we need to take a closer look at the CLFS_CONTROL_RECORD structure. As mentioned in part one of our study that discussed CLFS internals, it is used to hold an array of CLFS_METADATA_BLOCK structures with information about all the blocks present in the file, and also contains additional fields used to change the size of blocks. We are interested in the fields associated with the block extension operation: eExtendState – state of operation (None (0) / Extending (1) / Flushing (2)); iExtendBlock – index of the block being extended; iFlushBlock – index of the block being written; cNewBlockSectors – size of the new block (in sectors); cExtendStartSectors – original block size; cExtendSectors – number of sectors added.
When the driver opens an existing BLF file, the CClfsBaseFilePersisted::OpenImage function checks whether the interrupted block expansion operation should continue.
CClfsBaseFilePersisted::OpenImage function
This function checks the iExtendBlock and iFlushBlock indexes – they should be less than six. Otherwise, the block pointer will be read outside the block map buffer m_rgBlocks in the ExtendMetadataBlock function.
It’s important to note that the ExtendMetadataBlock function does not check the iExtendBlock and iFlushBlock indexes. The indexes are only checked by the OpenImage function that calls ExtendMetadataBlock. However, the ExtendMetadataBlock function is called in other functions that do not check indexes. It is assumed that “bad” indexes can only be passed from a file on disk, and the checks in OpenImage should protect against this. Could anything go wrong? Yes. If code uses malicious CLFS_CONTROL_RECORD or “bad” indexes after the initial check in OpenImage, this can be exploited to pass an arbitrary address as a pointer to a block and use it to escalate privileges.
It’s also worth mentioning that exploitation of CLFS_CONTROL_RECORD is not entirely new. The same attack method was previously described in the Exodus Intelligence blog post dedicated to the exploitation of CVE-2021-36955/CVE-2021-36963/CVE-2021-38633. The author of the exploit most likely read that blog post and realized that the ExtendMetadataBlock function could be exploited again using the same tricks from the GENERAL block exploit.
The exploit patches many bytes in a freshly created BLF file:
- The exploit moves the legitimate CLFS_CONTROL_RECORD structure to offset 0x1FF so that the offset of the DumpCount field matches the offset of the sector signature.
- The values of eExtendState, iExtendBlock, iFlushBlock and other fields in the CLFS_CONTROL_RECORD structure are changed to cause the code to execute the ExtendMetadataBlock function from the OpenImage
- The exploit builds a malicious CLFS_CONTROL_RECORD structure at offset 0x2FF.
- The exploit increases the cbSymbolZone value in the record of the GENERAL block so that the ExtendMetadataBlock function will be executed again when the code tries to add a new symbol.
- The exploit patches CLFS_LOG_BLOCK_HEADER->SignaturesOffset just like in exploit #2, but now for the CONTROL block. The array with the original bytes is moved from the last sector, where it should be, to the first sector where the block header is located (offset before patch: 0x3F8, offset after patch: 0x28).
Overlap of new signatures data array with existing block header
As a result of the changes made in steps 1 and 5, the record offset now overlaps the location of the original bytes for sector #0, and the sector #0 signature now partially overlaps CLFS_CONTROL_RECORD->DumpCount.
Overlap of the CLFS_CONTROL_RECORD->DumpCount field with the signature for sector #0
As you may have already guessed, the root cause of this vulnerability is almost identical to the root cause of the vulnerability from exploit #2. The main difference is that the exploit now targets CLFS_CONTROL_RECORD instead of CLFS_BASE_RECORD_HEADER. The RecordOffsets[0] and DumpCount fields are now connected by the signature of sector #0, resulting in the following:
- When opening a BLF file, the ClfsDecodeBlock function will copy the original bytes from CLFS_LOG_BLOCK_HEADER->RecordOffsets[0] to the position of the sector #0 signature (which is also CLFS_CONTROL_RECORD->DumpCount). The word at offset 0x1FE will be equal to 0x1FF.
- The code will proceed to the ExtendMetadataBlock function, and it will make changes to CLFS_CONTROL_RECORD. The value of the DumpCount field will be incremented. The word at offset 0x1FE becomes equal to 0x2FF.
- The FlushMetadata function will call the WriteMetadataBlock/ClfsEncodeBlock functions to encode the blocks and then write the changes to disk.
- ClfsEncodeBlock will copy the updated original bytes from the position of the sector #0 signature to where it should be stored, overwriting CLFS_LOG_BLOCK_HEADER->RecordOffsets[0] (0x1FF -> 0x2FF).
- The exploit adds a new container and because of the patched cbSymbolZone, the ExtendMetadataBlock function is executed again, but now the malicious CLFS_CONTROL_RECORD will be used instead of the legitimate record.
As a result, the exploit manages to pass “bad” iExtendBlock and iFlushBlock indexes, forcing the code to use a pointer sprayed into memory as the pointer of the block. The WriteMetadataBlock function, called from the ExtendMetadataBlock function, uses the pointer of the block to increment the DumpCount field, allowing an arbitrary value to be incremented in memory. On operating systems that support the PreviousMode technique, it is exploited by corrupting another BLF file and then following the same exploitation process as all the other exploits described previously. On newer builds of Windows 11 that do not support the PreviousMode technique, it is exploited by corrupting pipe attribute fields and building an arbitrary read/write primitive via the NtFsControlFile API function.
Use the following link to read the next part: