Skip to content

Commit 8846893

Browse files
committed
1 parent fc736c7 commit 8846893

File tree

2 files changed

+632
-0
lines changed

2 files changed

+632
-0
lines changed
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
---
2+
title: "CVE-2024-30084 - Windows Kernel Streaming Driver Elevation of Privilege Vulnerability"
3+
pubDate: 2025-05-11
4+
author: "Ghostbyt3"
5+
tags: ["1day", "ks.sys", "windows", "kernel", "heap"]
6+
description: "CVE-2024-30084 is a privilege escalation vulnerability in the Microsoft Kernel Streaming driver (ks.sys) caused by a race condition in how user-supplied data is handled."
7+
---
8+
9+
A vulnerability in the Microsoft Kernel Streaming driver (`ks.sys`) caused by a race condition in how user-supplied data is handled. The driver performs a second, unchecked read of a buffer after it has already been validated, introducing a time-of-check to time-of-use (TOCTOU) flaw. This allows modification of the input between the two accesses, leading the kernel to dereference attacker-controlled pointers and potentially execute arbitrary code in kernel mode, resulting in elevation of privileges to SYSTEM.
10+
11+
12+
**CVE-2025-30084:** https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-30084
13+
**Vulnerability Type:** Time-of-check Time-of-use (TOCTOU) Race Condition
14+
**Tested On:** Windows 11 23H2
15+
**Driver Version:** ks.sys - 10.0.22621.2506
16+
17+
## Vulnerability analysis
18+
19+
The vulnerability exists in the `KspPropertyHandler()` function, which handles the IOCTL call made to the `ks.sys` driver. The IOCTL uses the `METHOD_NEITHER` transfer type, which is considered unsafe. In this case, the user input (`Type3InputBuffer`) is directly copied to a newly allocated buffer (`SystemBuffer`).
20+
21+
```c++
22+
NTSTATUS __fastcall KspPropertyHandler(
23+
PIRP Irp,
24+
unsigned int a2,
25+
struct _LIST_ENTRY *a3,
26+
__int64 (__fastcall *a4)(_QWORD, _QWORD, _QWORD),
27+
int a5,
28+
__int64 a6,
29+
unsigned int a7)
30+
{
31+
struct _LIST_ENTRY *v7; // r11
32+
struct _IO_STACK_LOCATION *CurrentStackLocation; // rdi
33+
34+
[::]
35+
36+
37+
memset(Irp->AssociatedIrp.SystemBuffer, 0, v12);
38+
memmove(
39+
(char *)Irp->AssociatedIrp.SystemBuffer + v12,
40+
CurrentStackLocation->Parameters.DeviceIoControl.Type3InputBuffer,
41+
InputBufferLength);
42+
*(_DWORD *)((char *)Irp->AssociatedIrp.SystemBuffer + v12 + 20) = v16; // First copy of user input
43+
44+
[::]
45+
46+
47+
if ( v24 <= 0x2000 )
48+
{
49+
if ( v24 != 0x2000 )
50+
{
51+
if ( v24 == 256 )
52+
return 0;
53+
if ( v24 != 512 )
54+
{
55+
if ( v24 == 2048 )
56+
return SerializePropertySet(Irp, v22, (__int64)v7, v28);
57+
if ( v24 == 4096 )
58+
return UnserializePropertySet(Irp, v22, (__int64)v7); // Vulnerable function call
59+
goto LABEL_99;
60+
}
61+
62+
[::]
63+
```
64+
65+
Inside the `UnserializePropertySet()` function, the user input (`Type3InputBuffer`) is copied again to a newly allocated region (InBuffer). This creates a double fetch scenario. If an attacker modifies the input in user space between the first and second fetch, it can result in inconsistent data being processed.
66+
67+
```c++
68+
__int64 __fastcall UnserializePropertySet(IRP *irp, __int64 a2, __int64 a3)
69+
{
70+
_QWORD *SystemBuffer; // rdi
71+
struct _IO_STACK_LOCATION *CurrentStackLocation; // r13
72+
unsigned int OutputBufferLength; // ebx
73+
_DWORD *InBuffer; // rsi
74+
int v9; // r14d
75+
char *v10; // rdi
76+
unsigned int v11; // ebx
77+
char *v12; // r15
78+
unsigned int v13; // ebx
79+
char *OutBuffer; // rdi
80+
ULONG OutSize; // eax
81+
NTSTATUS v16; // r12d
82+
__int64 v17; // rax
83+
ULONG InSize; // [rsp+88h] [rbp+10h]
84+
ULONG BytesReturned; // [rsp+98h] [rbp+20h] BYREF
85+
86+
if ( *(_DWORD *)(a2 + 16) )
87+
return 3221225485LL;
88+
SystemBuffer = irp->AssociatedIrp.SystemBuffer;
89+
CurrentStackLocation = irp->Tail.Overlay.CurrentStackLocation;
90+
InSize = CurrentStackLocation->Parameters.DeviceIoControl.InputBufferLength;
91+
OutputBufferLength = CurrentStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
92+
InBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, InSize, 0x7070534Bu);
93+
if ( !InBuffer )
94+
return 3221225626LL;
95+
if ( OutputBufferLength < 0x14 )
96+
{
97+
LABEL_23:
98+
ExFreePoolWithTag(InBuffer, 0);
99+
return 3221225990LL;
100+
}
101+
else
102+
{
103+
if ( **(_QWORD **)a3 != *SystemBuffer || *(_QWORD *)(*(_QWORD *)a3 + 8LL) != SystemBuffer[1] )
104+
{
105+
LABEL_22:
106+
ExFreePoolWithTag(InBuffer, 0);
107+
return 3221225485LL;
108+
}
109+
memmove(InBuffer, CurrentStackLocation->Parameters.DeviceIoControl.Type3InputBuffer, InSize); // Double Fetch
110+
InBuffer[5] = 2;
111+
v9 = *((_DWORD *)SystemBuffer + 4);
112+
v10 = (char *)SystemBuffer + 20;
113+
v11 = OutputBufferLength - 20;
114+
while ( v11 && v9 )
115+
{
116+
if ( v11 < 0x20 )
117+
goto LABEL_23;
118+
v12 = v10;
119+
if ( *((_DWORD *)v10 + 5) )
120+
goto LABEL_22;
121+
InBuffer[4] = *((_DWORD *)v10 + 6);
122+
v13 = v11 - 32;
123+
OutBuffer = v10 + 32;
124+
OutSize = *((_DWORD *)v12 + 7);
125+
if ( OutSize > v13 )
126+
goto LABEL_23;
127+
v16 = KsSynchronousIoControlDevice(
128+
CurrentStackLocation->FileObject,
129+
0,
130+
CurrentStackLocation->Parameters.DeviceIoControl.IoControlCode,
131+
InBuffer,
132+
InSize,
133+
OutBuffer,
134+
OutSize,
135+
&BytesReturned);
136+
if ( v16 < 0 )
137+
{
138+
ExFreePoolWithTag(InBuffer, 0);
139+
return (unsigned int)v16;
140+
}
141+
v17 = *((unsigned int *)v12 + 7);
142+
if ( (unsigned int)v17 < v13 )
143+
{
144+
v17 = ((_DWORD)v17 + 3) & 0xFFFFFFFC;
145+
*((_DWORD *)v12 + 7) = v17;
146+
if ( (unsigned int)v17 >= v13 )
147+
goto LABEL_23;
148+
}
149+
v10 = &OutBuffer[v17];
150+
v11 = v13 - v17;
151+
--v9;
152+
}
153+
ExFreePoolWithTag(InBuffer, 0);
154+
if ( v11 )
155+
return 3221225476LL;
156+
else
157+
return v9 != 0 ? 0xC0000004 : 0;
158+
}
159+
}
160+
```
161+
162+
The exploitation methodology is similar to [CVE-2024-35250](https://labs.pwnfuzz.com/breakdowns/cve-2024-35250/), with a slight modification. In the previous exploit, `KSPROPERTY→Set` was set to `KSPROPSETID_DrmAudioStream` to invoke the vulnerable function call. To demonstrate this double-fetch vulnerability, a different Set value is used during the first copy, and just before the second copy occurs, it is changed back to `KSPROPSETID_DrmAudioStream`. This allows `ksthunk.sys` to invoke the target function (`RtlClearAllBits`) with the intended arguments.
163+
164+
Using any of the following property set GUIDs as the initial copy and then changing it back to `KSPROPSETID_DrmAudioStream` will lead to the same vulnerability. These GUIDs were identified through reverse engineering the `KspPropertyHandler()` function, where it checks the GUID that triggers a call to `UnserializePropertySet()`, as well as from the [`ks.h`](https://github.com/tpn/winsdk-10/blob/master/Include/10.0.10240.0/shared/ks.h) header file, as follows:
165+
166+
- 8C134960-51AD-11CF-878A-94F801C10000 - KSPROPSETID_Pin
167+
- 720D4AC0-7533-11D0-A5D6-28DB04C10000 - KSPROPSETID_Topology
168+
- 1464EDA5-6A8F-11D1-9AA7-00A0C9223196 - KSPROPSETID_General
169+
170+
171+
## Exploit
172+
173+
Tested on: Windows 11 23H2
174+
Working POC: https://github.com/ghostbyt3/WinDriver-EXP/tree/main/CVE-2024-30084
175+
176+
```
177+
PS C:\Users\h4x\Desktop> whoami
178+
desktop-3rgmcon\h4x
179+
PS C:\Users\h4x\Desktop> .\CVE-2024-30084.exe
180+
[+] Successfully opened internal topology of an audio device!
181+
[+] NT base address fffff8010fe00000
182+
[+] Found EPROCESS of the current process FFFFD70BF11E70C0
183+
[+] Found KTHREAD of the current thread FFFFD70BED9A4080
184+
[+] Found EPROCESS of the system.exe FFFFD70BEA104040
185+
[+] pFakeBitmapAddr = 0x0000000010000000
186+
[+] Beginning race condition...
187+
[+] Calling Kernel Streaming....
188+
[+] Stealing system's Token..
189+
[+] Replacing KTHREAD.PreviousMode as UserMode..
190+
[+] Spawning shell as SYSTEM...
191+
Microsoft Windows [Version 10.0.22631.3593]
192+
(c) Microsoft Corporation. All rights reserved.
193+
194+
C:\Users\h4x\Desktop>whoami
195+
nt authority\system
196+
197+
```
198+
199+
## Patch Analysis
200+
201+
The patched version adds checks to ensure the `KSPROPERTY→Set` is not set to `KSPROPSETID_DrmAudioStream`. The first `memmove`/`memcpy` operation in `KspPropertyHandler()` has also been removed.
202+
203+
```c++
204+
STATUS __fastcall KspPropertyHandler(
205+
PIRP Irp,
206+
unsigned int a2,
207+
__int64 a3,
208+
__int64 (__fastcall *a4)(_QWORD, _QWORD, _QWORD),
209+
unsigned int a5,
210+
__int64 a6,
211+
unsigned int a7)
212+
{
213+
__int64 v7; // rbx
214+
struct _IO_STACK_LOCATION *CurrentStackLocation; // r15
215+
unsigned int Options; // r10d
216+
217+
[::]
218+
219+
if ( v23 == 4096 )
220+
{
221+
LOBYTE(v20) = (unsigned int)Feature_2849679676__private_IsEnabledDeviceUsage() != 0;
222+
if ( !v20
223+
|| *(_QWORD *)v21 != *(_QWORD *)&KSPROPSETID_DrmAudioStream.Data1
224+
|| *(_QWORD *)(v21 + 8) != *(_QWORD *)KSPROPSETID_DrmAudioStream.Data4 ) // Fix
225+
{
226+
return UnserializePropertySet((__int64)Irp, v21, v7);
227+
}
228+
229+
[::]
230+
```
231+
232+
## Acknowledgements
233+
234+
- The original research on Kernel Streaming was conducted by [Angelboy](https://x.com/scwuaptx), and it can be found [here](https://devco.re/blog/2024/08/23/streaming-vulnerabilities-from-windows-kernel-proxying-to-kernel-part1-en/).
235+
- The [PoC](https://github.com/varwara/CVE-2024-35250/) was developed by Varwara, and the above PoC is based on it.

0 commit comments

Comments
 (0)