[Vuln Series #1] CLFS Vulnerability Analysis

GhouLSec
6 min readNov 10, 2022

--

The sample most probably related to CVE-2022-24521 which is related to CLFS parsing bug.

The vulnerability is due to the parsing issue of the CLFS on specially crafted log file (.BLF / Base Log File) which allow user to alter the kthread.previous_mode and enable write permission on kernel memory address, in this case is token replacement.

Please correct me if there is some misinformation in the post!

General exploitation flow:

  • Create 2 buffer that contains the address to setup the gadget ClfsSetEndofLog and previous mode address
  • Create a new log file with a new container
  • Modify the log file with specific value which in ulAbove _CLFSHASHSYM of the container context
Picture taken from Zscaler blog
  • Zero out CLFS_NODE_ID of container context (0xC1FDF008, which is next to _CLFSHASHSYM)
  • Changing Reserved LSN 1 / CClfsContainer in container context with hard coded address (0x40000000)
  • Changing the first 4 bytes of Shared Security Descriptor Symbol Hash Table
From Keen Security Lab
  • Re-calculating the crc32 hash of the log content
  • Value of previous mode will be decrease from 1 to 0 via ObfDereferenceObjectwhich is located inside CClfsLogFcbPhysical::CloseContainers->CClfsContainer::Close
  • Using NtWriteVirtualMemory to replace the user token to system (PID 4) token
  • Set Previous Mode 0 to 1 usingNtWriteVirtualMemory
  • Spawn new process with the escalated privilege (The token is inherited from parent process)

Setting up for vulnerability

It first creates 2 virtual memory regions with fixed address which does the following:
- A modded memory buffer that will access by the CClfsContainer::Close later. e.g. 0x40000000
- Contains address to gadget ClfsSetEndofLog which is inside the modded memory buffer. e.g. 0x50000000 inside 0x40000000

Crafted Virtual Memory

Here you can see that there is a weird +0x30 in previous mode address which you will see its purpose in ObfDereferenceObject.

Get previous mode offset

Before triggering the vulnerability, it creates a new container file using AddLogContainer and then reopen the log file and modify them with some value which including the hard-coded virtual memory address. The crc32 hash will be calculated afterwards.

Triggering the vulnerability

The vulnerability triggers when the user open the modified log file and close it again after all required memory content has been modified. The vulnerability is trigger inside CClfsLogFcbPhysical::Initialize (When open the log file) to load the modded container context buffer into kernel memory and CClfsLogFcbPhysical::CloseContainers (When close the log file) where it parse and read the specially crafted memory region inside the modified log file.

First in CClfsLogFcbPhysical::Initialize, CClfsBaseFile::AcquireClientContext is crucial steps to setting up the vulnerability by modify the flow of the clfs code in order to load the modded buffer of the container context into kernel memory.

Inside CClfsBaseFile::GetSymbol (one of the child function of CClfsBaseFile::AcquireClientContext), it will read the first 4 bytes of ulAbove and compare with first 4 bytes of Shared Security Descriptor Symbol Hash Table. If it is same, it will continue to fetch the container context (contains hard-coded address of 0x40000000) inside the kernel memory for later use.

CClfsBaseFile::GetSymbol: Fetching the malformed container_context which contains 0x40000000

* Where is the CClfsBaseFile::GetSymbol ?

CClfsLogFcbPhysical::Initialize -> CClfsBaseFile::AcquireClientContext -> CClfsBaseFile::GetSymbol

After finished running CClfsBaseFile::AcquireClientContext, the container_context variable (0xC1FDF008, _CLFS_NODE_ID in exact) will be check whether it is empty. Since, the it has been modified in earlier stage, it will just pass and proceed to load more data into the temporary storage memory.

Bypassing the function by zeroed out CLFS_NODE_ID of container context

The data will load into a CClfsLogFcbPhysical struct including the hard-coded 0x40000000 which will be read by CClfsLogFcbPhysical::CloseContainers later.

Load these data into CClfsLogFcbPhysical struct

After finishing the call of CClfsLogFcbPhysical::Initialize, CClfsLogFcbPhysical::CloseContainers will comes into the play to decrease the previous_mode into zero.

Buffer that points to the hard-coded virtual memory address (e.g. 40000000)

The CClfsContainer::Close (inside CClfsLogFcbPhysical::CloseContainers) is the part where previous mode decrease into 0 via ObfDereferenceObject which is the main vulnerability triggered.

Culprit to decrease the previous mode

What is ObfDereferenceObject actually?

This routine decrements the reference count of the specified object and does whatever cleanup there is if the count goes to zero.

In this case the 1 in the previous mode will decrease to 0.

Figure below shows some of screenshot related to the deference of previous mode

Number that pointed is the previous_mode+0x30

Inside the ObfDereferenceObject, it can see that the rsi is actually the previous_mode+0x30 , you can verify with it also (shown in the left hand side of the screenshot below)

The [rsi-0x30] will move the address back to the previous_mode

You can perform other check such as kthread structure via dt nt!_kthread <Addr of the kthread e.g. (Addr in 40000030/rsi) — 30 — 232>

After successfully to deference the previous mode, it continue with CLFSSetEndOfLog.

I guess the CLFSSetEndOfLog just to prevent the execution of the CClfsLogFcbPhysical::Release which will destroy the struct data of CClfsLogFcbPhysical struct.

The destructor function call of CClfsContainer

I will think like that because i didn’t see anything happens in CLFSSetEndOfLog (Probably I’m just too lazy until this part of analysis 😪) as the code in IF statement will not be execute at last.

Enter CLFSSetEndOfLog via CLFS!__guard_dispatch_icall_fptr
It will skip the If statement and return to caller function

Then, it flows back to the exploit program and continue the overwriting of the low privilege token with high privilege token (PID: 4)

Overwrite Previous mode from 1 to 0

Finally, It will set the previous mode back to 1 by calling NtWriteVirtualMemory before spawn a new cmd.exe process with privilege access.

whoami /all
whoami /all

Thoughts

It is quite interesting to perform some vulnerability analysis on the CLFS, even though it is quite difficult at first 😌 for a beginner like me. During the time of writing this blog, the publication of blog from zscaler really helps me a lot on understanding some things that I encountered in my analysis. Kudos to them!!!

Also, It is glad that I’m managed to analyze a windows exploit sample instead of just some day-to-day malware analysis. 🤪

I did skipped some details about how to get the correct token offset for different windows version, getting the previous mode inside kthread and getting _EPROCESS for both system and user process using NtQuerySystemInformation (SystemExtendedHandleInformation).

References:

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

https://www.zscaler.com/blogs/security-research/technical-analysis-windows-clfs-zero-day-vulnerability-cve-2022-37969-part

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

https://i.blackhat.com/Asia-22/Friday-Materials/AS-22-Xu-The-Next-Generation-of-Windows-Exploitation-Attacking-the-Common-Log-File-System.pdf

https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/windows-debugging-and-exploiting-part-4-ntquerysysteminformation/

http://blog.rewolf.pl/blog/?p=1683

https://www.nirsoft.net/kernel_struct/vista/KTHREAD.html

https://www.codewarrior.cn/ntdoc/wrk/ob/ObfDereferenceObject.htm

https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/how-kernel-exploits-abuse-tokens-for-privilege-escalation

https://research.nccgroup.com/2021/08/17/cve-2021-31956-exploiting-the-windows-kernel-ntfs-with-wnf-part-2/

https://www.zscaler.com/blogs/security-research/technical-analysis-windows-clfs-zero-day-vulnerability-cve-2022-37969-part2-exploit-analysis

--

--

GhouLSec
GhouLSec

No responses yet