This is part six 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 go to other parts using this table of contents:
- Part 1 – Windows CLFS and five exploits of ransomware operators
- Part 2 – Windows CLFS and five exploits of ransomware operators (Exploit #1 – CVE-2022-24521)
- Part 3 – Windows CLFS and five exploits of ransomware operators (Exploit #2 – September 2022)
- Part 4 – Windows CLFS and five exploits of ransomware operators (Exploit #3 – October 2022)
- Part 5 – Windows CLFS and five exploits of ransomware operators (Exploit #4 – CVE-2023-23376)
- Part 6 – Windows CLFS and five exploits of ransomware operators (Exploit #5 – CVE-2023-28252)
Exploit #5 – CVE-2023-28252
April 2023 brought a patch for yet another CLFS zero-day – CVE-2023-28252. It’s the last one we are going to discuss in this study. It was captured in the wild by yours truly Boris Larin (oct0xor) with Kaspersky, Genwei Jiang with Mandiant and Quan Jin with DBAPPSecurity WeBin Lab. This patch was released exactly one year after the release of the patch for CVE-2022-24521 used in exploit #1.
On Patch Tuesday in April 2023, we published a brief report about Nokoyawa ransomware attacks using this zero-day, as well as details about the exploit itself. Eight days later we updated the post with details about the vulnerability, but we decided not to go into too much detail to avoid helping other attackers develop an exploit while everyone was updating. Now that a few months have passed since the patch was released, let’s take a closer look at the root cause of the vulnerability.
Exploit #5 (CVE-2023-28252) is very similar to exploit #4 (CVE-2023-23376). The new exploit also targets CLFS_CONTROL_RECORD, and it also aims to bypass iExtendBlock and iFlushBlock index verification. In fact, both exploits are almost identical, the only difference being the patches made to the BLF file. But although the exploits are almost identical, the root causes of both vulnerabilities are completely different. CVE-2023-28252 can also be considered a logical vulnerability, like all those previously discussed, but it is not similar to any of them because it does not involve overlapping any structures.
The exploit makes these patches to the BLF file:
- The values of eExtendState, iExtendBlock, iFlushBlock and other fields in the CLFS_CONTROL_RECORD structure of the CONTROL block are changed to make the code execute the ExtendMetadataBlock function from the OpenImage
- The exploit builds a malicious CLFS_CONTROL_RECORD structure in the CONTROL_SHADOW block.
- The value of the DumpCount field for the CONTROL_SHADOW block is set to be less than the DumpCount in the CONTROL block.
- 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 decreases the value of the ValidSectorCount field in the CONTROL block from 2 to 1.
To understand the vulnerability, let’s take a closer look at how blocks are read from disk. This is done by the ReadMetadataBlock function, which is quite large. It reads the requested block and its SHADOW version and decides which one to use. A simplified code for this decision process is shown in the image below.
Simplified code that decides whether to use a block or its SHADOW version
If both blocks are OK, the newest one is used (the one with the higher DumpCount). If one block is OK and the other is not, the valid one is used. If both blocks are not OK, the code returns an error.
The decision whether a block is OK or not is made by the ClfsDecodeBlock function. It’s shown in the image below.
ClfsDecodeBlock function
It checks whether the block has a valid checksum or is in an older format, but additional checks are performed in the ClfsDecodeBlockPrivate function.
ClfsDecodeBlockPrivate function
It has additional version checks, checks the TotalSectorCount, checks the flags and, if all is well, replaces the sector signatures with the original bytes.
Now let’s take a look at the ClfsEncodeBlock function, which is used when writing blocks to disk. The code for this function is shown in the image below.
ClfsEncodeBlock function
It doesn’t check anything, clears the checksum and proceeds to call ClfsEncodeBlockPrivate. This function performs some checks.
ClfsEncodeBlockPrivate function
What immediately catches the eye is the inconsistency of the checks in ClfsDecodeBlock(Private)/ClfsEncodeBlock(Private). The ClfsEncodeBlockPrivate function also checks the TotalSectorCount and the flags, but unlike ClfsDecodeBlockPrivate it also checks the ValidSectorCount. So if a block has an invalid ValidSectorCount, it will be decoded without problems, but the code will not be able to encode it later. The last piece of the puzzle becomes clear when we see how the ClfsEncodeBlock function is used in the WriteMetadataBlock function.
WriteMetadataBlock function
The WriteMetadataBlock function does not check the return value of the ClfsEncodeBlock function, so if it corrupts the block by clearing its checksum and returning an error due to an invalid ValidSectorCount value, the code will continue its normal execution. This leads to:
- When opening a BLF file, the code will proceed to the ExtendMetadataBlock function and it will call the ReadMetadataBlock
- ReadMetadataBlock will check the CONTROL and CONTROL_SHADOW blocks, and since both blocks are valid, it will use the CONTROL block because of the larger DumpCount.
- ExtendMetadataBlock will make changes and will execute the WriteMetadataBlock
- WriteMetadataBlock will call ClfsEncodeBlock and corrupt the CONTROL block by setting the checksum to 0 and returning an error because ValidSectorCount < TotalSectorCount.
- The corrupted CONTROL block is written to disk because the returned ClfsEncodeBlock value is not checked.
- The exploit adds a new container and because of the patched cbSymbolZone, the ExtendMetadataBlock function is executed again.
- ReadMetadataBlock will check the CONTROL and CONTROL_SHADOW blocks and this time will use the malicious CONTROL_SHADOW block since the CONTROL block checksum is 0. As a result, ExtendMetadataBlock will use unverified iExtendBlock and iFlushBlock
The rest of the exploitation process is exactly the same as exploit #4.
Conclusion
As expected, some of the vulnerabilities discussed in this study were actually variants of previously known issues and may not have existed if they had been properly patched in the first place.
As for the Common Log File System (CLFS), this is a good example of how not to design a file format. I’m actually surprised it still exists in its current form. After so many vulnerabilities and captured zero-days, is it now completely secure? Since the release of the patch for CVE-2023-28252 in April 2023, several other CLFS vulnerabilities have been patched.
At the same time, we are very grateful to Microsoft for their work in blocking the PreviousMode and NtQuerySystemInformation techniques. Over the last couple of years, we have seen a spate of zero-day exploits used by cybercriminals to escalate privileges from Medium IL to System IL, and blocking these techniques will make new versions of Windows 11 more resistant to such exploits.
In the meantime, we continue to closely monitor cybercriminal exploitation of vulnerabilities and hunt for actively exploited zero-days.
Windows CLFS and five exploits used by ransomware operators (Exploit #5 – CVE-2023-28252)