Windows AppLocker Driver LPE Vulnerability – CVE-2024-21338
When I initially interviewed candidates for CF’s Windows Researchers position, one of the challenges I gave out was related to CVE-2024-21338. A Windows Kernel Elevation of Privileges, specifically an Untrusted Pointer Dereference vulnerability in the appid.sys
driver. The driver is responsible for the AppLocker technology.
Back then, this vulnerability became famous thanks to Avast’s beautiful work on the Lazarus FudModule Rootkit.
This is the detailed write-up from our newly hired researcher; welcome to the team! 🙂
TL;DR
Following the detailed posts from Avast, the main takepoints of the vulnerability can be summed up as follows:
- The bug resides in the
AppHashComputeImageHashInternal()
function, which could be invoked by sending an IOCTL with value0x22A018
to the device object named\\Device\Appid
. - The driver expects two pointers referenced from the IOCTL’s input buffer.
- This bug results in a powerful primitive, given that we have complete control of the instruction pointer and the data in the first argument via a callback.
- Based on the ACL present on the device object name, only the
LOCAL SERVICE
andAppIDSvc
users have enough permission to send the targetIoControlCode
. - The target driver,
appid.sys
, is not automatically loaded and requires sending an event to a specific AppLocker-related ETW provider.
Challenges
SMEP & kCFG
- Although we have full control over the instruction pointer, we cannot just supply a user-mode pointer to directly execute our shellcode due to the presence of the Supervisor Mode Execution Prevention (SMEP) mitigation.
- The callback function is an indirect call, so we must find a valid pointer in the kernel space to bypass the safeguard from Kernel Control Flow Guard (kCFG).
KASLR
Because this exploit would be run under the context of the LocalService
user, KASLR is not a big problem here. With the help of the evergreen NtQuerySystemInformation()
syscall, we can easily leak almost all the kernel addresses that are necessary for building the exploit.
Loading the target driver
Because the driver is not loaded by default, we can load it manually by either using the Service Manager or sending an event to an AppLocker-related ETW provider to kick off the AppID service.
For testing purposes, this driver was already loaded into the kernel space.
Exploitation
Root Cause Analysis
When an IOCTL with value 0x22A018
is received, the AipSmartHashImageFile()
function is called to handle this control code. In Windows 11, the first parameter is expected to be a pointer of the following structure:
typedef struct _HASH_IMAGE_FILE { PVOID ImageContext; // pointer to the context of hashing file image FILE_OBJECT *FileObject; // kernel object pointer of file PVOID CallbackTable; // pointer to callback functions ULONGLONG Action; // not so sure } HASH_IMAGE_FILE, *PHASH_IMAGE_FILE;
Then the AppHashComputeFileHashesInternal()
function is called to compute the hash of the target file. The first two parameters passed to this function are user-controllable:
Later, after some initialization, the function AppHashComputeFileHashesInternal()
calls AppHashComputeImageHashInternal()
to get the hash value(s), and the first two parameters are passed directly to the target function.
Inside the AppHashComputeImageHashInternal()
function, before computing the hash value of the target image, it performs a calling to 2 callback functions from the 2nd parameter pointer, and these pointers are entirely user controllable. Thus leading to a complete RIP takeover condition.
Overall, the graph call to vulnerable function looks like this:
SMEP & kCFG bypassing
Due to the presence of SMEP and kCFG, if we call a random ROP gadget or shellcode directly from the user mode, the kernel would bail out with a bug check. Instead, we must find some useful functions to help us perform a data-only attack.
After reading some articles (1, 2), I found that the function nt!SeSetAccessStateGenericMapping()
is widely used to bypass kCFG. However, it requires the first parameter to point to a struct of size, at least 0x50
:
Meanwhile, the target IOCTL requires the input pointer to be of size 0x20
(size of _HASH_IMAGE_FILE
structure):
So I decided to find a gadget by myself.
Spending some time looking for small functions performing interesting things, based on the given constraints, I found a candidate that worked: nt!DbgkpTriageDumpRestoreState
. This gadget allows to overwrite 8 bytes at offset 0x2078
of the first pointer (this structure is the ImageContext
pointer) with the value at offset 0x10
(field CallbackTable
in this case) of the input structure:
Building the exploit
There are two possible ways to exploit this bug using the gadget above:
- Set the
PreviousMode
field of a targetKTHREAD
, then abuse theNtReadProcessMemory/NtWriteProcessMemory
syscalls to archive arbitrary read/write over the kernel space. In Avast’s report, Lazarus used this technique. - Overwriting the
_SEP_TOKEN_PRIVILEGES
structure of the current process’s token to enable theSeDebugPrivilege
and abuse this privilege to inject a shellcode in a privileged process. This way requires triggering the bug twice in order to overwrite both thePresent
andEnabled
fields.
Although the write value is also the pointer to the CallbackTable
field of the _HASH_IMAGE_FILE
structure, it must be a valid pointer value. However, by utilizing the VirtualAlloc
API, it’s still possible to make a suitable value for writing since both of the two methods presented above don’t require a (too) specific value. In my exploit, I have demonstrated both ways to get a SYSTEM shell.
Exploit Code
You can find the expliot code on GitHub
Demo
POC 1 – Abusing PreviousMode
POC 2 – Abusing SeDebugPrivilege
References
- https://decoded.avast.io/janvojtesek/lazarus-and-the-fudmodule-rootkit-beyond-byovd-with-an-admin-to-kernel-zero-day/
- https://decoded.avast.io/luiginocamastra/from-byovd-to-a-0-day-unveiling-advanced-exploits-in-cyber-recruiting-scams/
- https://github.com/sam-b/windows_kernel_address_leaks
- https://research.nccgroup.com/2020/05/25/cve-2018-8611-exploiting-windows-ktm-part-5-5-vulnerability-detection-and-a-better-read-write-primitive/#previousmode-abuse:~:text=into%20PreviousMode%20further.-,PreviousMode%20%E2%80%93%20a%20%22god%20mode%22%20primitive%3F,-PreviousMode%20on%2064
- https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/previousmode
- https://nero22k.github.io/posts/windows-applocker-driver-elevation-of-privilege-cve-2024-21338/
- https://labs.bluefrostsecurity.de/blog/2020/01/07/cve-2019-1215-analysis-of-a-use-after-free-in-ws2ifsl/
- https://ti.qianxin.com/blog/articles/CVE-2023-28252-Analysis-of-In-the-Wild-Exploit-Sample-of-CLFS-Privilege-Escalation-Vulnerability/