Aggregator
CVE-2024-45191 | Matrix libolm up to 3.2.16 AES timing discrepancy
CVE-2024-40884 | Mattermost up to 9.5.7/9.10.0/9.11.0 Add Team Members access control
CVE-2024-36439 | Swissphone DiCal-RED 4009 Administrative Web Interface privileges management
CVE-2024-36440 | Swissphone DiCal-RED 4009 /etc/deviceconfig a one-way hash with a predictable salt
CVE-2024-42497 | Mattermost up to 9.5.7/9.8.2/9.9.1/9.10.0/9.11.0 Systems Manager Role access control
Oil Services Giant Halliburton Disrupted by Hack Attack
Oil services giant Halliburton is reportedly dealing with a disruption tied to a Wednesday hack attack, after which the Houston-based firm advised employees to not connect to any internal IT resources - a sign that the incident could be a ransomware attack.
Streaming vulnerabilities from Windows Kernel - Proxying to Kernel - Part I
Over the past few decades, vulnerabilities in the Windows Kernel have emerged frequently. The popular attack surface has gradually shifted from Win32k to CLFS (Common Log File System). Microsoft has continuously patched these vulnerabilities, making these targets increasingly secure. However, which component might become the next attack target? Last year, MSKSSRV (Microsoft Kernel Streaming Service) became a popular target for hackers. However, this driver is tiny and can be analyzed in just a few days. Does this mean there might not be new vulnerabilities?
This research will discuss an overlooked attack surface that allowed us to find more than ten vulnerabilities within two months. Additionally, we will delve into a proxy-based logical vulnerability type that allows us to bypass most validations, enabling us to successfully exploit Windows 11 in Pwn2Own Vancouver 2024.
(This research will be divided into several parts, each discussing different bug classes and vulnerabilities. This research was also presented at HITCON CMT 2024.)
Start from MSKSSRVFor vulnerability research, looking at historical vulnerabilities is indispensable.
Initially, we aimed to challenge Windows 11 in Pwn2Own Vancouver 2024. Therefore, we began by reviewing past Pwn2Own events and recent in-the-wild Windows vulnerabilities, searching for potential attack surfaces. Historical trends show that Win32K, primarily responsible for handling GDI-related operations, has always been a popular target, with numerous vulnerabilities still emerging. Since 2018, CLFS (Common Log File System) has also gradually become a popular target. Both components are extremely complex, suggesting that there are likely still many vulnerabilities. However, becoming familiar with these components requires significant time, and many researchers are already examining them. Therefore, we did not choose to analyze them first.
Last year, after Synacktiv successfully exploited a vulnerability in MSKSSRV to compromise Windows 11 during Pwn2Own 2023, many researchers began to focus on this component. Shortly thereafter, a second vulnerability ,CVE-2023-36802, was discovered. At this time, chompie also published an excellent blog post detailing this vulnerability and its exploitation techniques. Given that this component is very small, with a file size of approximately 72 KB, it might only take a few days of careful examination to fully understand it. Therefore, we chose MSKSSRV for historical vulnerability analysis, with the hopeful prospect of identifying other vulnerabilities.
We will briefly discuss these two vulnerabilities but not go into much detail.
CVE-2023-29360 - logical vulnerabilityThe first one is the vulnerability used by Synacktiv in Pwn2Own Vancouver 2023.
This is a logical vulnerability in the MSKSSRV driver. When MSKSSRV uses MmProbeAndLockPages to lock user-specified memory address as framebuffer, the AccessMode was not set correctly. This leads to a failure to check whether the user-specified address belongs to the user space. If the user provides a kernel address, it will map the specified kernel address to user space for the user to use. Ultimately, this allows the user to write data to any address in the kernel. The exploitation is simple and very stable, making it become one of the most popular vulnerabilities.
For more details, please refer to Synacktiv’s presentation at HITB 2023 HKT and Nicolas Zilio(@Big5_sec) ‘s blog post.
CVE-2023-36802 - type confusionThis vulnerability was discovered shortly after CVE-2023-29360 was released. It was already being exploited when Microsoft released their patches. This is a very easily discovered vulnerability. It uses the objects (FSContextReg, FSStreamReg) stored in FILE_OBJECT->FsContext2 for subsequent processing. However, there is no check on the type of FsContext2, leading to type confusion. For detailed information, you can refer to the IBM X-Force blog, which provides a very thorough explanation.
Since then, there have been very few vulnerabilities related to MSKSSRV. Due to its relatively small size, MSKSSRV is quickly reviewed, and gradually, fewer and fewer people pay attention to it.
But is that the end of it ?However, does this mean there are no more vulnerabilities?
In fact, the entire Kernel Streaming looks like the diagram below:
MSKSSRV is just the tip of the iceberg. In fact, there are many other potential components, and those listed in the diagram above are all part of Kernel Streaming. After delving into this attack surface, numerous vulnerabilities were eventually discovered, flowing like a stream.
By the way, while I was writing this blog, chompie also published about the vulnerability she used in this year’s Pwn2Own Vancouver 2024, CVE-2024-30089, which is also a vulnerability in MSKSSRV. The vulnerability lies in handling the reference count, requiring a lot of attention and thought to discover. It is also quite interesting, but I won’t discuss it here. I highly recommend reading this one.
Brief overview of Kernel StreamingSo, what is Kernel Streaming? In fact, we use it very frequently.
On Windows systems, when we open the webcam, enable sound, and activate audio devices such as microphones, the system needs to write or read related data such as your voice and captured images from your devices into RAM. It is essential to read data into your computer more efficiently during this process. Microsoft provides a framework called Kernel Streaming to handle these data, which primarily operates in kernel mode. It features low latency, excellent scalability, and a unified interface, making handling streaming data more convenient and efficient.
In Microsoft’s Kernel Streaming, three multimedia class driver models are provided: port class, AVStream, and stream class. We will briefly introduce port class and AVStream, as stream class is less common and more outdated and will not be discussed here.
Port classThis type of driver is mostly used for PCI and DMA-based audio device hardware drivers. Currently, most audio-related processing, such as volume control or microphone-related processing, falls into this category. The main component library used would be portcls.sys.
AVStreamAVStream is a multimedia driver provided by Microsoft that primarily supports video-only and integrated audio/video streaming. Currently, most video-related processing, such as your webcam, capture card, etc., is associated with this category.
In fact, the use of Kernel Streaming is also very complex. We will only provide a brief description. For more detailed information, please refer to Microsoft Learn.
Interact with deviceWhen we want to interact with audio devices or webcams, we need to open the device just like with any other device. Essentially, it interacts with the device driver in the same way. So, what would the names of these types of devices be? These names are not typically like \Device\NamedPipe, but rather something like the following:
\\?\hdaudio#subfunc_01&ven_8086&dev_2812&nid_0001&subsys_00000000&rev_1000#6&2f1f346a&0&0002&0000001d#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\ehdmiouttopo Enumerate deviceHere you can use APIs such as SetupDiGetClassDevs to enumerate devices. Generally, KS series devices are registered under KSCATEGORY*, such as audio devices which are registered under KSCATEGORY_AUDIO.
Additionally, you can use the APIs provided by KS, such as KsOpenDefaultDevice, to obtain the handle of the first matching PnP device in that category. Actually, it is just a wrapper around SetupDiGetClassDevs and CreateFile.
hr = KsOpenDefaultDevice(KSCATEGORY_VIDEO_CAMERA,GENERIC_READ|GENERIC_WRITE, &g_hDevice) Kernel Streaming objectAfter we open these devices, Kernel Streaming will create some Kernel Streaming related instances, the most important of which are KS Filters and KS Pins.
These will be used during the Kernel Streaming process. We will only provide a brief introduction here. We will use audio filters as an example, as most others are quite similar.
KS filtersEach KS Filter typically represents a device or a specific function of a device. When we open an audio device, it usually corresponds to a KS filter object. When we read data from the audio device, this data is first processed through this KS Filter.
Conceptually, as shown in the diagram below, the large box in the middle represents a KS filter for the audio device. When we want to read data from the audio device, it is read into the filter from the left, processed through several nodes, and then output from the right.
(From: https://learn.microsoft.com/en-us/windows-hardware/drivers/audio/audio-filters)
KS pinsIn the figure above, the points for reading and outputting data are called pins. The kernel also has corresponding KS pin Objects to describe the Pins, recording whether the Pin is a sink or a source, the data format for input and output, and so on. When we use it, we must open a pin on the filters to create an instance to read from or write to the device.
KS propertyEach of these KS objects will have its own property, and each property corresponds to a specific feature. For instance, the data format mentioned earlier in the Pin, the volume level, and the status of the device are all properties. These properties typically correspond to a set of GUIDs. We can set these properties through IOCTL_KS_PROPERTY.
This greatly simplifies the development of multimedia drivers and ensures consistency and scalability across different devices.
Read streams from webcamHere is a simple example illustrating how an application can read data from a webcam.
The most basic flow is roughly shown in this diagram:
- Open the device to obtain the device handle.
- Use this device handle to create an instance of the Pin on this filter and obtain the Pin handle.
- Use IOCTL_KS_PROPERTY to set the device state of the Pin to RUN.
- Finally, you can use IOCTL_KS_READ_STREAM to read data from this Pin.
For vulnerability research, we must first understand its architecture and consider the potential attack surfaces.
After gaining a preliminary understanding of the functionalities and operations of Kernel Streaming, we need to understand the architecture to find vulnerabilities. It’s crucial to know how Windows implements these functions and what components are involved. This way, we can identify which sys files to analyze and where to start.
After our analysis, the overall architecture looks approximately like this diagram:
In the Kernel Streaming components, the most important ones are ksthunk.sys and ks.sys. Almost all functionalities are related to them.
ksthunk (Kernel Streaming WOW Thunk Service Driver)ksthunk.sys is the entry point in Kernel Streaming. Its function is quite simple: it converts 32-bit requests from the WoW64 process into 64-bit requests, allowing the underlying driver to handle the requests without additional processing for 32-bit structures.
ks (Kernel Connection and Streaming Architecture Library)ks.sys is one of the core components of Kernel Streaming. It is the library of Kernel Streaming responsible for forwarding requests such as IOCTL_KS_PROPERTY to the corresponding device driver, and it also handles functions related to AVStream.
The work flow of IOCTL_KS_*IOCTL_KS_PROPERTY will be used as an example here. When calling DeviceIoControl, as shown in the figure below, the user’s request will be sequentially passed to the corresponding driver for processing.
At step 6, ks.sys will determine which driver and handler to hand over your request based on the KSPROPERTY you requested.
Finally, forward it to the corresponding driver, as shown in the figure above, where it is ultimately forwarded to the handler in portcls to operate the audio device.
You should now have a preliminary understanding of the architecture and process of Kernel Streaming. Next, it’s time to look for vulnerabilities.
Based on the existing elements, which attack surfaces are worth examining?
From attacker’s viewBefore digging for vulnerabilities, if you can carefully consider under what circumstances they are likely to occur, you can achieve twice the result with half the effort.
From a vulnerability researcher’s perspective, there are a few key points to consider:
1. Property handler in each deviceKS object for each device has its own properties, and each property has its own handler. Some properties are prone to issues during handling.
2. ks and ksthunkks and ksthunk have not had vulnerabilities for a long time, but they are the most accessible entry points and might be good targets. The last vulnerabilities(CVE-2020-16889 and CVE-2020-17045) were found in 2020 by @nghiadt1098.
3. Each driver handles a part of the contentIn some functionalities of Kernel Streaming, certain drivers handle parts of the input individually, which may lead to inconsistencies.
After reviewing Kernel Streaming from the above perspectives, we quickly identified several relatively easy-to-discover vulnerabilities.
- portcls.sys
- CVE-2024-38055 (out-of-bounds read when set dataformat for Pin)
- CVE-2024-38056
- ksthunk
- CVE-2024-38054 (out-of-bounds write)
- CVE-2024-38057
However, we will not be explaining these vulnerabilities one by one. Most of these are obvious issues such as unchecked length or index leading to out-of-bounds access. @Fr0st1706 also wrote an exploit for CVE-2024-38054 recently. We might slowly explain these in subsequent parts in the future. We will leave this for the readers to study for now.
During the review process, we discovered some interesting things. Do you think the following code snippet is really fine?
__int64 __fastcall CKSThunkDevice::CheckIrpForStackAdjustmentNative(__int64 a1, struct _IRP *irp, __int64 a3, int *a4) { if ( irp->RequestorMode ) { v14 = 0xC0000010; } else { UserBuffer = (unsigned int *)irp->UserBuffer; ... v14 = (*(__int64 (__fastcall **)(_QWORD, _QWORD, __int64 *)) (Type3InputBuffer + 0x38))(// call Type3InputBuffer+0x38 *UserBuffer, 0LL, v19); } }Seeing this code reminds me of CVE-2024-21338. Initially, the vulnerability had no checks, but after patching, ExGetPreviousMode was added. However, the check here uses the RequestorMode in IRP for validation. Generally, the RequestorMode from a user-called IOCTL will be UserMode(1), so there shouldn’t be any issues.
At this point, I also recall James Forshaw’s article Windows Kernel Logic Bug Class: Access Mode Mismatch in IO Manager.
The overlooked bug classFirst, we need to mention a few terms and concepts. If you are already familiar with Previous Mode and RequestorMode, you can skip to A logical bug class section.
PreviousModeThe first one is PreviousMode. In an Application, if a user operates on a device or file through Nt* System Service Call, upon entering the kernel, it will be marked as UserMode(1) in _ETHREAD’s PreviousMode, indicating that this System Service Call is from the user. Conversely, if it is called from kernel mode, such as a device driver invoking the Zw* System Service Call, it will be marked as KernelMode(2).
RequestorModeAnother similar field is the RequestorMode in the IRP. This field records whether your original request came from UserMode or KernelMode. In kernel driver code, this is a very commonly used field, typically derived from PreviousMode.
It is often used to decide whether to perform additional checks on user requests, such as Memory Access Check or Security Access Check. In the example below, if the request comes from UserMode, it will check the user-provided address. If it comes from the Kernel, no additional checks are performed to increase efficiency.
But in reality, this has also led to some issues.
A logical bug classJames Forshaw’s Windows Kernel Logic Bug Class: Access Mode Mismatch in IO Manager mentions a type of Bug Class.
What would happen if a user calls a System Service Call like NtDeviceIoControlFile, and then the driver handling it uses user-controllable data as parameters for ZwOpenFile?
After the driver calls ZwOpenFile, PreviousMode will switch to KernelMode, and when it uses NtOpenFile processing, most checks will be skipped due to PreviousMode being KernelMode. Subsequently, Irp->RequestorMode will become KernelMode, bypassing the Security Access Check and Memory Access Check. However, this largely depends on how the subsequent driver implements these checks. There might be issues if it relies solely on RequestorMode to decide whether to perform checks The actual situation is slightly more complex and related to the flags of CreateFile. For details, refer to the following articles:
- Windows Kernel Logic Bug Class: Access Mode Mismatch in IO Manager
- Hunting for Bugs in Windows Mini-Filter Drivers
- Local privilege escalation via the Windows I/O Manager: a variant finding collaboration
These studies mainly focused on the Zw* series of System Service Call. Are there other similar situations that could also cause this kind of logical vulnerability?
The new bug patternActually, it is possible. When the device driver uses IoBuildDeviceIoControlRequest to create a DeviceIoControl IRP, it is easy to encounter such issues if not careful. This API is primarily used by kernel drivers to call IOCTL, and it helps you build the IRP. Subsequently, calling IofCallDriver allows you to call IOCTL within the kernel driver. On Microsoft Learn, there is a particular passage worth noting.
By default, if you do not explicitly set the RequestorMode, it will directly call IOCTL with KernelMode.
Following this approach, we revisited Kernel Streaming and discovered an intriguing aspect.
The function where IoBuildDeviceIoControlRequest is used in Kernel Streaming is in ks!KsSynchronousIoControlDevice and it obviously involves calling IOCTL in the kernel using the aforementioned method. However, it appears that Irp->RequestorMode is properly set here, and different values are assigned based on the parameters of KsSynchronousIoControlDevice. This will be a convenient library for kernel streaming driver developers.
However…
ks!CKsPin::GetState
ks!SerializePropertySet
ks!UnserializePropertySet
We found that in Kernel Streaming, all functions using KsSynchronousIoControlDevice consistently use KernelMode(0). At this point, we can carefully inspect whether the places it is used have any security issues. Therefore, we convert the bug pattern in Kernel Streaming into the following points:
- Utilized KsSynchronousIoControlDevice
- Controllable InputBuffer & OutputBuffer
- The second processing of IOCTL relies on RequestorMode for security checks
Following this pattern, we quickly found the first vulnerability.
The vulnerability & exploitation CVE-2024-35250This vulnerability is also the one we used in Pwn2Own Vancouver 2024. In the IOCTL_KS_PROPERTY of Kernel Streaming, to increase efficiency, KSPROPERTY_TYPE_SERIALIZESET and KSPROPERTY_TYPE_UNSERIALIZESET requests are provided to allow users to operate on multiple properties through a single call. These types of requests will be broken down into multiple calls by the KsPropertyHandler. For more details, refer to this one.
It is implemented in ks.sys
When handling property in ks.sys, if the KSPROPERTY_TYPE_UNSERIALIZESET flag is provided, ks!UnserializePropertySet will handle your request.
Let’s take a look at UnserializePropertySet.
unsigned __int64 __fastcall UnserializePropertySet( PIRP irp, KSIDENTIFIER* UserProvideProperty, KSPROPERTY_SET* propertyset_) { ... New_KsProperty_req = ExAllocatePoolWithTag(NonPagedPoolNx, InSize, 0x7070534Bu); ... memmove(New_KsProperty_req, CurrentStackLocation->Parameters.DeviceIoControl.Type3InputBuffer, InSize); //------[1] ... status = KsSynchronousIoControlDevice( CurrentStackLocation->FileObject, 0, CurrentStackLocation->Parameters.DeviceIoControl.IoControlCode, New_KsProperty_req, InSize, OutBuffer, OutSize, &BytesReturned); //-----------[2] ... }You can see that during the processing, the original request is first copied into a newly allocated buffer at [1]. Subsequently, this buffer is used to call the new IOCTL using KsSynchronousIoControlDevice at [2]. Both New_KsProperty_req and OutBuffer are contents provided by the user.
The flow when calling UnserializePropertySet is roughly as illustrated below:
When calling IOCTL, as shown in step 2 of the diagram, the I/O Manager will set Irp->RequestorMode to UserMode(1). Until step 6, it will check if the requested property by the user exists in the KS object. If the property exists in the KS object and is set with KSPROPERTY_TYPE_UNSERIALIZESET, UnserializePropertySet will be used to handle the specified property.
Next, in step 7, KsSynchronousIoControlDevice will be used to perform the IOCTL again. At this point, the new Irp->RequestorMode will become KernelMode(0), and the subsequent processing will be the same as a typical IOCTL_KS_PROPERTY. This part will not be elaborated further.
As a result, we now have a primitive that allows us to perform arbitrary IOCTL_KS_PROPERTY operations. Next, we need to look for places where it might be possible to achieve EoP (Elevation of Privilege).
The EoPThe first thing you will likely notice is the entry point ksthunk.sys.
Let’s take a look at ksthunk!CKSThunkDevice::DispatchIoctl.
__int64 __fastcall CKSThunkDevice::DispatchIoctl(CKernelFilterDevice *a1, IRP *irp, unsigned int a3, NTSTATUS *a4) { ... if ( IoIs32bitProcess(irp) && irp->RequestorMode ) //------[3] { //Convert 32-bit requests to 64-bit requests } else if ( CurrentStackLocation->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_PROPERTY ) { return CKSThunkDevice::CheckIrpForStackAdjustmentNative((__int64)a1, irp, v11, a4) //-----[4]; } }You can see that ksthunk will first determine whether the request is from a WoW64 Process. If it is, it will convert the original 32-bit Requests into 64-bit at [3]. If the original request is already 64-bit, it will call CKSThunkDevice::CheckIrpForStackAdjustmentNative at [4] to pass it down.
__int64 __fastcall CKSThunkDevice::CheckIrpForStackAdjustmentNative(__int64 a1, struct _IRP *irp, __int64 a3, int *a4) { ... if ( *(_OWORD *)&Type3InputBuffer->Set == *(_OWORD *)&KSPROPSETID_DrmAudioStream && !type3inputbuf.Id && (type3inputbuf.Flags & 2) != 0 ) //-----[5] { if ( irp->RequestorMode ) //-------[6] { v14 = 0xC0000010; } else { UserBuffer = (unsigned int *)irp->UserBuffer; ... v14 = (*(__int64 (__fastcall **)(_QWORD, _QWORD, __int64 *))(Type3InputBuffer + 0x38))(// call Type3InputBuffer+0x38 *UserBuffer, 0LL, v19); //------------[7] } } }We can notice that if we give the property set as KSPROPSETID_DrmAudioStream, there is additional processing at [5]. At [6], we can see that it first checks whether Irp->RequestorMode is KernelMode(0). If it is called from user, it will directly return an error.
It should be pretty obvious here that if we call IOCTL with KSPROPERTY_TYPE_UNSERIALIZESET and specify the KSPROPSETID_DrmAudioStream property, it will be KernelMode(0) here. Additionally, it will directly use the input provided by the user as a function call at [7]. Even the first parameter is controllable.
After writing the PoC, we confirmed our results.
Some people might wonder, under what device or situation would have KSPROPSETID_DrmAudioStream? Actually, most audio devices will have it, primarily used for setting DRM-related content.
ExploitationOnce arbitrary calls are achieved, accomplishing EoP is not too difficult. Although protections such as kCFG, kASLR, and SMEP will be encountered, the only protection that needs to be dealt with under Medium IL is kCFG.
- kCFG
- kASLR
- NtQuerySystemInformation
- SMEP
- Reuse Kernel Code
- …
Our goal here is straightforward: to create an arbitrary write primitive from a legitimate function, which can then be used to achieve EoP through typical methods like replacing the current process token with system token or abusing the token privilege.
It is intuitive to look directly for legitimate functions with names containing set, as they are more likely to do arbitrary writing. We directly take the export functions from ntoskrnl.exe to see if there are any good gadgets, as these functions are generally legitimate.
We quickly found RtlSetAllBits.
It is a very useful gadget and a legitimate function in kCFG. Additionally, it only requires controlling the first parameter _RTL_BITMAP.
struct _RTL_BITMAP { ULONG SizeOfBitMap; ULONG* Buffer; };We can assign the buffer to any address and specify the size, allowing us to set up a range of bits entirely. At this point, we are almost done. As long as Token->Privilege is fully set up, we can use the Abuse Privilege method to achieve EoP.
However, before the Pwn2Own event, we created a new Windows 11 23H2 VM on Hyper-V and ran the exploit. The result was a failure. It failed at the open device stage.
After investigation, we discovered that Hyper-V does not have an audio device by default, causing the exploit to fail.
In Hyper-V, only MSKSSRV is present by default. However, MSKSSRV does not have the KSPROPSETID_DrmAudioStream property, which prevents us from successfully exploiting this EoP vulnerability. Therefore, we must find other ways to trigger it or discover new vulnerabilities. We decided to review the entire process again to see if there were any other exploitable vulnerabilities.
CVE-2024-30084After re-examining, it was found that IOCTL_KS_PROPERTY uses Neither I/O to transmit data, which means it uses the input buffer for data processing directly. Generally speaking, this method is not recommended as it often leads to double fetch issues.
From the above figure of KspPropertyHandler, we can see that after the user calls IOCTL, the Type3InputBuffer is directly copied into a newly allocated buffer, containing the KSPROPERTY structure. This GUID in the structure is then used to check if the property set exists in the device. If it does, it will proceed to call UnserializePropertySet.
Let’s take another look at UnserializePropertySet.
It once again copies the user-provided data from Type3InputBuffer as the input for the new IOCTL. Clearly, there exists a double fetch vulnerability here, so we have modified the entire process, as shown in the following diagram.
When we initially send IOCTL_KS_PROPERTY, we use the existing property KSPROPSETID_Service of MSKSSRV for subsequent operations. As shown in step 6 of the diagram, a copy of KSPROPERTY is first made into the SystemBuffer, and then this property is used to check whether it is in the support list of the KS object. Since MSKSSRV supports it, it will then call UnserializePropertySet.
After calling UnserializePropertySet, due to the double fetch vulnerability, we can change KSPROPSETID_Service with KSPROPSETID_DrmAudioStream between the check and use phases. Subsequently, KSPROPSETID_DrmAudioStream will be used as the request to send IOCTL, thereby triggering the CVE-2024-35250 mentioned above logic flaw. This makes the vulnerability exploitable in any environment.
Finally, we successfully compromised Microsoft Windows 11 during Pwn2Own Vancouver 2024.
After the Pwn2Own event, our investigation revealed that this vulnerability has existed since Windows 7 for nearly 20 years. Moreover, it is highly reliable and has a 100% success rate in exploitation. We strongly recommend that everyone update to the latest version as soon as possible.
To be continuedThis article focuses on how we identified the vulnerabilities used in this year’s Pwn2Own and the attack surface analysis of Kernel Streaming. After discovering this vulnerability, we continued our research on this attack surface and found another exploitable vulnerability along with other interesting findings.
Stay tuned for Part II, expected to be published in October of this year.
Reference- Critically Close to Zero-Day: Exploiting Microsoft Kernel Streaming Service
- Windows Kernel Security - A Deep Dive into Two Exploits Demonstrated at Pwn2Own
- CVE-2023-29360 Analysis
- Racing Round and Round: The Little Bug That Could
- Windows Kernel Logic Bug Class: Access Mode Mismatch in IO Manager
- Hunting for Bugs in Windows Mini-Filter Drivers
- Local Privilege Escalation via the Windows I/O Manager: A Variant Finding & Collaboration
Streaming vulnerabilities from Windows Kernel - Proxying to Kernel - Part I
在過去的幾十年中 Windows Kernel 的漏洞層出不窮,熱門的攻擊面逐漸從 Win32k 慢慢轉移到 CLFS (Common Log File System) 上。微軟也持續且積極地修補這些漏洞,使得這些元件越來越安全。而下一個熱門的目標會是哪個元件呢?去年開始,MSKSSRV (Microsoft Kernel Streaming Service) 成為駭客喜愛的目標之一。這個驅動程式小到可以在幾天內完成分析。這是否意味著可能不太會有新的漏洞了?
在這篇研究將講述一個長期被忽視的攻擊面,讓我們在兩個月內就找出了超過 10 個漏洞。此外,也將深入探討了一種 Proxy-Based 的邏輯漏洞類型,使我們可以忽略掉大多數的檢查,最終成功在 Pwn2Own Vancouver 2024 中,攻下 Windows 11 的項目。
這份研究將分成數個部分來撰寫,分別講述不同的漏洞類型及漏洞型態,亦發表於 HITCON CMT 2024 中。
Start from MSKSSRV對於一項漏洞研究來說,從歷史的漏洞看起,是不可或缺的。
起初,我們為了挑戰 Pwn2Own Vancouver 2024 中 Windows 11 的項目,開始從過去的 Pwn2Own 以及近期 in-the-wild 的漏洞中開始審視,尋找可能的攻擊面。沿著歷史軌跡可以得知,過去主要負責 GDI 相關操作的 Win32K 一直是個很熱門的目標,從 2018 年以來,CLFS (Common Log File System) 也漸漸成為了熱門目標之一。這兩個元件都非常複雜,並且直到現在仍然有不少新漏洞出現,但要熟悉這兩個元件需要花不少時間,同時也有許多研究員在看這兩個元件,所以最終我們沒有先選擇分析他們。
去年 Synacktiv 在 Pwn2Own 2023 中,使用 MSKSSRV 的漏洞成功攻下 Windows 11 後,便有不少人往這個元件開始看起,短時間內就又出現了第二個漏洞 CVE-2023-36802,這時 chompie 也發表了一篇非常詳細的文章,講述這個漏洞成因及其利用細節。由於這個元件非常的小,只看檔案大小約略只有 72 KB,可能認真看個幾天就可以全部看完,因此我們便挑了 MSKSSRV 來做歷史漏洞分析,看看是否有機會抓出其他漏洞。
接下來我們會提一下這兩個漏洞,但不會著墨過多。
CVE-2023-29360 - logical vulnerability第一個是 Synacktiv 在 Pwn2Own 2023 中所使用的漏洞 :
這是一個邏輯上的漏洞。當 MSKSSRV 使用 MmProbeAndLockPages 鎖定使用者給的記憶體位置作為 FrameBuffer 時,並沒有設置正確的 AccessMode,導致沒有檢查使用者指定的位置是否屬於 User space。如果使用者給的是 Kernel space 中的位置,它就會把指定的 Kernel 位置映射到 User space 給使用者用,最終導致使用者可以對 Kernel 中的任意位置寫入,利用上簡單且非常穩定,成為了受歡迎的漏洞之一。
更多細節可以參考 Synacktiv 在 HITB 2023 HKT 的演講及 Nicolas Zilio(@Big5_sec) 的部落格文章
CVE-2023-36802 - type confusion這個漏洞則是在 CVE-2023-29360 出來後沒多就被許多人發現,並且在微軟發佈更新時,就已經偵測到利用,是個非常容易被發現的漏洞。MSKSSRV 會先將內部使用的物件(FSContextReg、FSStreamReg)存放在 FILE_OBJECT 的 FsContext2 中,然而後續使用時並沒有對 FsContext2 的型態做檢查,導致 type confusion,詳細內容可參考 IBM X-Force 的部落格。
至此之後,就很少有關於 MSKSSRV 的相關漏洞了。
But is that the end of it ?然而是否這樣就沒洞了呢?
而我要更準確地回答,No!
實際上整個 Kernel Streaming 就像下面這張圖這樣 :
MSKSSRV 只是冰山一角而已,實際上還有不少潛在的元件,上圖中所寫的都是屬於 Kernel Streaming 的一部分。實際往這方向挖掘之後,最終也在這個攻擊面上取得不少漏洞,就如同流水般的流出漏洞來。
順帶一提,我在寫這篇部落格時,chompie 也發表了有關於他在今年 Pwn2Own Vancouver 2024 中所使用的漏洞 CVE-2024-30089。這個漏洞也在 MSKSSRV 中,該漏洞發生在 Reference Count 的處理,其成因也很有趣,不過這邊就不多談,詳細內容可參考她發表的文章。
Brief overview of Kernel Streaming那麼,什麼是 Kernel Streaming 呢? 事實上,我們正常使用電腦情況下就會用到 :
在 Windows 系統上,當我們打開鏡頭、開啟音效以及麥克風等音訊設備時,系統需要從這些設備讀取你的聲音、影像等相關資料到 RAM 中。為了更高效地完成這些資料的傳輸,微軟提供了一個名為 Kernel Streaming 的框架,用來處理這些資料。這個框架主要在 Kernel mode 下運行,具有低延遲、良好的擴充性和統一介面等特性,使你能更方便、更高效地處理串流(Stream)資料。
Kernel Streaming 中,提供了三種多媒體驅動模型:port class、AVStream 和 stream class。這裡將主要介紹 port class 和 AVStream,而 stream class 因為較為罕見且過時,不會多加討論。
Port Class大多數用於 PCI 和 DMA 型音效裝置的硬體驅動程式,它處理與音訊相關的數據傳輸,例如音量控制、麥克風輸入等等,主要會使用到的元件函式庫會是 portcls.sys。
AVStreamAVStream 則是由微軟提供的多媒體類驅動程式,主要支援僅限影片的串流和整合音訊/影片串流,目前跟影像有關的處理多數都跟這類別有關,例如你的視訊鏡頭、擷取卡等等。
實際上 Kernel Streaming 的使用很複雜,因此這裡只會簡單的敘述一下,更多詳細內容可以參考微軟官方文件。
Interact with device在我們想要與音訊設備或是視訊鏡頭等設備互動時該怎麼做呢?其實就跟一般設備互動一樣,可以透過 CreateFile 函數來開啟一個設備。那麼這類型的設備,名稱又會是甚麼呢?其實這邊不太會像是 \Devcie\NamedPipe 這類型的名稱,而是會像下面這樣的路徑 :
\\?\hdaudio#subfunc_01&ven_8086&dev_2812&nid_0001&subsys_00000000&rev_1000#6&2f1f346a&0&0002&0000001d#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\ehdmiouttopo Enumerate device每台電腦都可能不一樣,必須使用 SetupDiGetClassDevs 等 API 去列舉設備,一般來說 KS 系列的設備都會註冊在 KSCATEGORY* 底下,像是音訊設備就會註冊在 KSCATEGORY_AUDIO 中。
你也可以使用 KS 所提供的 KsOpenDefaultDevice 獲得該類別中第一個符合的 PnP 裝置的 Handle,實際上來說也只是 SetupDiGetClassDevs 和 CreateFile 的封裝而已。
hr = KsOpenDefaultDevice(KSCATEGORY_VIDEO_CAMERA,GENERIC_READ|GENERIC_WRITE, &g_hDevice) Kernel Streaming object我們在開啟這些設備之後,Kernel Streaming 會在 Kernel 中建立一些相關的 Instance,其中最為重要的就是 KS Filters 及 KS Pins。在 Kernel Streaming 的使用過程中,這些 Instance 會被頻繁使用,它們主要用來封裝設備的硬體功能,方便開發者透過統一的介面進行串流的處理。
這邊先以 Audio Filters 作為例子,其他多數大同小異,我們也只會簡單介紹,其他細節請自行參考微軟官方文件。
KS filters每個 KS Filter 通常代表一個設備或設備的特定功能,在我們打開一個音訊設備後,大部分情況下會對應到一個 Kernel Filter,當我們從音訊設備讀取資料時,這些資料就會先通過這個 KS Filter 進行處理。
概念上如下圖所示,中間的大框表示一個代表音訊設備的 KS filter。而我們想要從音訊設備中讀取資料時,會從左邊讀入 Filter,經過幾個節點進行處理後,從右邊輸出。
(From: https://learn.microsoft.com/en-us/windows-hardware/drivers/audio/audio-filters)
KS pins上圖中,讀取及輸出資料的點稱為 Pin,Kernel 也有相對應的 KS Pin Object,用於描述這些 Pin 的行為,例如 Pin 是輸入端還是輸出端、支援的格式有哪些等。我們使用時必須在 Filters 上,開啟一個 Pin 來建立 Instance,才能從設備讀取或輸出資料。
KS Property這些 KS Object 都會有自己的 Property,每個 Property 都會有相對應的功能,前面所提到的 Pin 中的資料格式、音量大小及設備的狀態等等,這些都是一個 Property,通常會對應到一組 GUID,我們可以透過 IOCTL_KS_PROPERTY 來讀取或設定這些 Property。
這大大簡化了多媒體驅動程式的開發,並且確保了不同設備之間的一致性和可擴展性。
Read streams from webcam這邊就用個簡單的範例來介紹一下 Application 如何從視訊鏡頭讀取資料
其最簡單的流程大概如這張圖所示 :
- 開啟設備後獲得設備 Handle
- 使用這個 Handle 在這個 Filter 上建立 Pin 的 Instance 並獲得 Pin handle
- 使用 IOCTL_KS_PROPERTY 設置 Pin 的狀態到 RUN
- 最後就可以使用 IOCTL_KS_READ_STREAM 從這個 Pin 中讀資料進來
對漏洞研究而言,我們必須先了解其架構,思考有哪些可能的攻擊面
在初步了解 Kernel Streaming 有哪些功能和操作後,為了找尋漏洞必須先了解一下架構,了解 Windows 是怎麼實作這些功能、分別有哪些元件等等,才知道應該要分析哪些 sys,從哪邊下手會比較好。
經過我們分析後,整個架構約略會像這張圖所示 :
在 Kernel Stearming 元件中,最為核心的就是 ksthunk.sys 及 ks.sys,幾乎所有功能都會與它們有關。
ksthunk (Kernel Streaming WOW Thunk Service Driver)Application 呼叫 DeviceIoControl 後,在 Kernel Streaming 中的入口點,但它功能很簡單,負責將 WoW64 process 中 32-bit 的 requests 轉換成 64-bit 的 requests,使得下層的 driver 就可以不必為 32 位元的結構另外處理。
ks (Kernel Connection and Streaming Architecture Library)Kernel Streaming 的核心元件之一,它是 Kernel Streaming 的函示庫,負責及轉發 IOCTL_KS_PROPERTY 等 requests 到對應設備的 driver 中,同時也會負責處理 AVStream 的相關功能。
The work flow of IOCTL_KS_*而在呼叫 DeviceIoControl 時,就會像下圖一樣,將使用者的 requests 依序給相對應的 driver 來處理
而到第 6 步時 ks.sys 就會根據你 requests 的 Property 來決定要交給哪個 driver 及 handler 來處理你的 request。
最終再轉發給相對應的 Driver,如上圖中最後轉發給 portcls 中的 handler 來操作音訊設備。
到這邊應該對 Kernel Streaming 的架構及流程有初步概念了,接下來就是找洞的時刻。依照現有的元素來看,哪些是值得一看的攻擊面呢?
From attacker’s view在挖掘漏洞前,如果能仔細思考怎樣的情況下容易有洞,可以達到事半功倍的效果
從一個漏洞研究員的角度來說,大概會有下列這幾個點
-
每個設備中的 Property handler 每個設備中的 KS Object 都有各自的 Property,而且每個 Property 都有各自的實作,有些 Property 處理起來容易出問題。
-
ks 及 ksthunk ks 及 ksthunk 已經有很長一段時間沒有漏洞,但卻是個最容易接觸到的入口點,也許是一個好目標,上一次出現的漏洞是在 2020 年 @nghiadt1098 所找到的兩個漏洞 CVE-2020-16889 及 CVE-2020-17045
-
每個 driver 都各自處理一部分的內容 在 Kernel Streaming 的部分功能中,有些 driver 會各自先處理部分的內容,可能會造成一些不一致性的問題。
我們針對上面幾個角度去對整個 Kernel Streaming 做 Code Review 後,很快的就發現了幾個比較容易發現的漏洞
- portcls.sys
- CVE-2024-38055 (out-of-bounds read when set dataformat for Pin)
- CVE-2024-38056
- ksthunk
- CVE-2024-38054 (out-of-bounds write)
- CVE-2024-38057
不過我們這一篇不會一一講解這些漏洞,這幾個多數都是沒有檢查長度或是 index 之類的越界存取等等明顯的洞,也許會在後續的部分慢慢來講解,@Fr0st1706 也在前陣子寫出了 CVE-2024-38054 的利用,這邊就暫時留給讀者研究了。
這篇要提的是,我們在 Review 過程中發現了一些有趣的事情。
你覺得下面這段程式碼是否安全呢?
__int64 __fastcall CKSThunkDevice::CheckIrpForStackAdjustmentNative(__int64 a1, struct _IRP *irp, __int64 a3, int *a4) { if ( irp->RequestorMode ) { v14 = 0xC0000010; } else { UserBuffer = (unsigned int *)irp->UserBuffer; ... v14 = (*(__int64 (__fastcall **)(_QWORD, _QWORD, __int64 *)) (Type3InputBuffer + 0x38))(// call Type3InputBuffer+0x38 *UserBuffer, 0LL, v19); } }看到這段程式碼讓我想起了 CVE-2024-21338,該漏洞原先並沒有任何檢查,而在修補後則是新增了 ExGetPreviousMode,但這邊檢查則是使用了 IRP 中的 RequestorMode 來做檢查,不過一般情況下從使用者呼叫的 IOCTL 的 RequestorMode 都會是 UserMode(1) 是不會有問題的。
此時我又想起來 James Forshaw 的 Windows Kernel Logic Bug Class: Access Mode Mismatch in IO Manager 這篇文章。
The overlooked bug class這邊我們必須先來提一下幾個名詞跟概念,不過如果你對 PreviousMode 及 RequestorMode 很熟悉,可以跳至 A logical bug class
PreviousMode第一個是 PreviousMode,在 Application 中如果使用者透過 Nt* 等 System Service Call 來對設備或檔案中操作時,進入 Kernel 後就會在 _ETHREAD 中的 PreviousMode 標註 UserMode(1) 表示這個 System Service Call 是來自 User mode 的 Application。如果你是從 Kernel mode 中,例如設備 driver 呼叫 Zw* System Service Call 的 API 就會標記成 KernelMode(0)。
RequestorMode另外一個類似的則是 IRP 中的 RequestorMode 這邊就是記錄你原始的 requests 是來自 UserMode 還是 KernelMode,在 Kernel driver 中的程式碼是非常常用到的欄位,通常會來自 PreviousMode。
很常被用來決定是否要對來自使用者的 requests 做額外檢查,像是 Memory Access Check 或是 Security Access Check,例如下面這個例子中,如果 requests 來自 UserMode 就會檢查使用者提供的位置,如果是從 Kernel 來的,就不做額外檢查增加效率。
但實際上這也出現了一些問題…
A logical bug class在 James Forshaw 的 Windows Kernel Logic Bug Class: Access Mode Mismatch in IO Manager 中,就提到了一種 Bug Class
這邊可以先想想看,使用者呼叫 NtDeviceIoControlFile 之類的 System Service Call 之後,如果處理的 driver 又去用使用者可控的資料來作為 ZwOpenFile 的參數,會發生什麼事
在 driver 呼叫 ZwOpenFile 之後, PreviousMode 會轉換成為 KernelMode,並且在 NtOpenFile 處理時,就會因為 PreviousMode 是 KernelMode 的關係少掉大部分的檢查,而後續的 Irp->RequestorMode 也會因此變成 KernelMode ,從而繞過 Security Access Check 及 Memory Access Check。不過這邊很看後續處理的 driver 怎麼去實作這些檢查,如果只依賴 RequestorMode 來決定要不要檢查,就可能會有問題。這邊省略了一些細節,實際上的狀況會稍微再複雜一點點,也會跟 CreateFile 的 flag 有關,細節可參考下列幾篇文章 :
- Windows Kernel Logic Bug Class: Access Mode Mismatch in IO Manager
- Hunting for Bugs in Windows Mini-Filter Drivers
- Local privilege escalation via the Windows I/O Manager: a variant finding collaboration
這邊有這樣的概念就好,原先這些研究主要是在 Zw* 系列的 System Service Call 上面,大家可以思考一下,有沒有其他類似的情況,也可能造成這種邏輯漏洞呢?
The new bug pattern事實上來說是有的,使用 IoBuildDeviceIoControlRequest 這個方法去創建一個 DeviceIoControl 的 IRP 時,萬一沒注意到也很容易有這樣的問題。這個 API 主要是 Kernel driver 用來呼叫 IOCTL 的其中一種方法,它會幫你建好 IRP,而後續在去呼叫 IofCallDriver,就可以在 Kernel driver 中呼叫 IOCTL。在 Microsoft Learn 中,有一段話特別值得注意 :
也就是預設情況下,如果你沒有特別去設置 RequestorMode 就會直接以 KernelMode 形式去呼叫 IOCTL。
按照這個思路,我們重新回頭審視一下我們的目標 Kernel Streaming,我們發現了一個吸引我們的地方。
在 Kernel Streaming 中使用這個 IoBuildDeviceIoControlRequest 地方是在 ks!KsSynchronousIoControlDevice ,而主要內容明顯就是在用剛剛提到的方法,在 Kernel 中呼叫 DeviceIoControl,不過這邊看似有好好的設置 Irp->RequestorMode,且會根據 KsSynchronousIoControlDevice 參數不同而去設置不同的數值,對於開發者來說會是一個方便的函式庫。
然而…
ks!CKsPin::GetState
ks!SerializePropertySet
ks!UnserializePropertySet
我們發現到在 Kernel Streaming 中,全部有使用到 KsSynchronousIoControlDevice 的地方都是固定的使用 KernelMode(0),到這邊就可以仔細的檢查看看,有用到的地方是否有安全上的問題了。因此我們將 Kernel Streaming 中的 bug pattern 轉換成下列幾點:
- 有使用 KsSynchronousIoControlDevice
- 有可控的
- InputBuffer
- OutputBuffer
- 第二次處理 IOCTL 的地方有依賴 RequestorMode 做安全檢查,並且有可以作為提權利用的地方。
按照這個 Pattern 我們很快地就找到了第一個洞
The vulnerability & exploitation CVE-2024-35250這個漏洞也是我們今年在 Pwn2Own Vancouver 2024 中所使用的漏洞。在 Kernel Streaming 的 IOCTL_KS_PROPERTY 功能中,為了讓效率增加,提供了 KSPROPERTY_TYPE_SERIALIZESET 和 KSPROPERTY_TYPE_UNSERIALIZESET 功能允許使用者透過單一呼叫與多個 Property 進行操作。當我們用這功能時,這些 requests 將被 KsPropertyHandler 函數分解成多個呼叫,詳情可參考這篇。
該功能實作在 ks.sys 中
上圖中可以看到,在 ks 處理 Property 時,如果有給上述的 flag 就會由 UnserializePropertySet 來處理你的 request
我們這邊就先來看一下 UnserializePropertySet
unsigned __int64 __fastcall UnserializePropertySet( PIRP irp, KSIDENTIFIER* UserProvideProperty, KSPROPERTY_SET* propertyset_) { ... New_KsProperty_req = ExAllocatePoolWithTag(NonPagedPoolNx, InSize, 0x7070534Bu); ... memmove(New_KsProperty_req, CurrentStackLocation->Parameters.DeviceIoControl.Type3InputBuffer, InSize); //------[1] ... status = KsSynchronousIoControlDevice( CurrentStackLocation->FileObject, 0, CurrentStackLocation->Parameters.DeviceIoControl.IoControlCode, New_KsProperty_req, InSize, OutBuffer, OutSize, &BytesReturned); //-----------[2] ... }可看到在處理過程中會先將原始的 request,複製到新分配出來的 Buffer 中 [1],而後續就會使用這個 Buffer 來使用 KsSynchronousIoControlDevice 呼叫新的 IOCTL [2]。其中 New_KsProperty_req 及 OutBuffer 都是使用者所傳入的內容。
而呼叫 UnserializePropertySet 時的流程,大概如下圖所示 :
這邊呼叫 IOCTL 時可以看到圖中第 2 步 I/O Manager 會將 Irp->RequestorMode 設成 UserMode(1),直到第 6 步時,ks 會去判斷使用者 requests 的 Property 是否存在於該 KS Object 中,如果該 KS Object 的 Property 存在,並且有設置 KSPROPERTY_TYPE_UNSERIALIZESET 就會用 UnserializePropertySet 來處理指定的 Property
而接下來第 7 步就會呼叫 KsSynchronousIoControlDevice 重新做一次 IOCTL,而此時新的 Irp->RequestorMode 就變成了 KernelMode(0) 了,而後續的處理就如一般的 IOCTL_KS_PROPERTY 相同,就不另外詳述了,總之我們到這裡已經有個可以任意做 IOCTL_KS_PROPERTY 的 primitive 了,接下來我們必須尋找看看是否有可以 EoP 的地方。
The EoP最先看到的想必就是入口點 ksthunk,我們這邊可以直接來看 ksthunk!CKSThunkDevice::DispatchIoctl
__int64 __fastcall CKSThunkDevice::DispatchIoctl(CKernelFilterDevice *a1, IRP *irp, unsigned int a3, NTSTATUS *a4) { ... if ( IoIs32bitProcess(irp) && irp->RequestorMode ) //------[3] { //Convert 32-bit requests to 64-bit requests } else if ( CurrentStackLocation->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_PROPERTY ) { return CKSThunkDevice::CheckIrpForStackAdjustmentNative((__int64)a1, irp, v11, a4) //-----[4]; } }ksthunk 會先判斷是否是 WoW64 的 Process 的 request,如果是就會將原本 32-bit 的 requests 轉換成 64-bit 的 [3],如果原本就是 64-bit 則會呼叫 CKSThunkDevice::CheckIrpForStackAdjustmentNative [4] 往下傳遞
__int64 __fastcall CKSThunkDevice::CheckIrpForStackAdjustmentNative(__int64 a1, struct _IRP *irp, __int64 a3, int *a4) { ... if ( *(_OWORD *)&Type3InputBuffer->Set == *(_OWORD *)&KSPROPSETID_DrmAudioStream && !type3inputbuf.Id && (type3inputbuf.Flags & 2) != 0 ) //-----[5] { if ( irp->RequestorMode ) //-------[6] { v14 = 0xC0000010; } else { UserBuffer = (unsigned int *)irp->UserBuffer; ... v14 = (*(__int64 (__fastcall **)(_QWORD, _QWORD, __int64 *))(Type3InputBuffer + 0x38))(// call Type3InputBuffer+0x38 *UserBuffer, 0LL, v19); //------------[7] } } }我們在 [5] 看到,如果我們給定的 Property Set 是 KSPROPSETID_DrmAudioStream ,就有特別的處理。而在 [6] 時,會先去判斷 Irp->RequestorMode 是否為 KernelMode(0),如果從 UserMode(1) 呼叫的 IOCTL 就會直接返回錯誤,但如果我們使用前面所說的 KSPROPERTY_TYPE_UNSERIALIZESET 來呼叫 IOCTL,並指定 KSPROPSETID_DrmAudioStream 這個 Property,那麼這裡 [6] 就會是 KerenlMode(0)。接下來就會在 [7] 直接使用使用者所傳入的內容做為 function 呼叫,甚至第一個參數是可控的,實際寫 PoC 後,驗證了我們的結果。
這邊可能會有人有疑惑,什麼設備或是情況下會有 KSPROPSETID_DrmAudioStream ?實際上來說音訊設備大多情況下都會有,主要是用來設置 DRM 相關內容用的。
Exploitation在有了任意呼叫之後,要達成 EoP 就不是太大的問題,雖然會遇到 kCFG、kASLR、SMEP 等等保護,但在 Medium IL 下唯一比較需要處理的就只有 kCFG。
- kCFG
- kASLR
- NtQuerySystemInformation
- SMEP
- Reuse Kernel Code
- …
那我們目標很簡單,就是從合法的 function 做出任意寫的 primitive,而之後就可以利用常見的方法用 System token 取代當前的 Process token 或是 Abuse token privilege 去做到 EoP。
直覺地會直接去找看看,kCFG 中合法的 function 名稱有 set 的 function,比較可能是可以寫入的。我們這裡是直接拿 ntoskrnl.exe 中 export fucntion 去尋找看看是否有合法的 function,這些大多情況下都是合法的。
而很快的我們就找到了 RtlSetAllBits
它是個非常好用的 gadget 而且是 kCFG 中合法的 function,另外也只要控制一個參數 _RTL_BITMAP
struct _RTL_BITMAP { ULONG SizeOfBitMap; ULONG* Buffer; };我們可將 Buffer 指定到任意位置並指定大小,就可以將一段範圍的 bits 全部設置起來,到這邊就差不多結束了,只要 將 Token->Privilege 全部設置起來,就可以利用 Abuse Privilege 方法來做到 EoP 了。
然而…在 Pwn2Own 比賽前,我們在 Hyper-V 上安裝一個全新 Windows 11 23H2 VM 測試 Exploit,結果失敗了。 而且是在開啟設備階段就失敗。
經過調查後發現到 Hyper-V 在預設情況下並不會有音訊設備,造成 exploit 會失敗。
在 Hyper-V 中,預設情況下只會有 MSKSSRV,然而 MSKSSRV 也沒有 KSPROPSETID_DrmAudioStream 這個 Property,使得我們無法成功利用這個漏洞達成 EoP,因此我們必須找其他方式觸發或者找新的漏洞,此時我們決定重新 Review 一遍整個流程,看看是否還有其他可能利用的地方。
CVE-2024-30084重新審視後,發現到 IOCTL_KS_PROPERTY 是使用 Neither I/O 來傳遞資料的,也就是說會直接拿使用者的 Input buffer 來做資料上的處理,一般來說不太建議使用這個方法,很常出現 Double Fetch 的問題。
我們可從上圖中 KspPropertyHandler 看到,在使用者呼叫 IOCTL 之後,會直接將 Type3InputBuffer 複製到新分配出來的 Buffer 中,其中會存有 KSPROPERTY 結構,接下來會用這結構中的 GUID 來查詢 Property 是否有在該設備所支援的 Property 中,若存在才會繼續往下呼叫 UnserializePropertySet。
這邊我們再回頭看一眼 UnserializePropertySet。
我們可以發現到,它又再次從 Type3InputBuffer 複製使用者所提供的資料做為新的 IOCTL 的輸入,很明顯的這邊就存在了一個 Double Fetch 的漏洞,因此我們將整個利用流程改成下圖的樣子
我們一開始發送 IOCTL_KS_PROPERTY 時,就會先以 MSKSSRV 既有的 Property KSPROPSETID_Service 來做後續操作,而在圖中第 6 步時,會先複製一份 Property 的 GUID 到 Kernel 中,而後再用這個 Property GUID 去查詢是否有在該 KS Object 的支援清單中,而這邊因為 MSKSSRV 有支援,就會往下呼叫 UnserializePropertySet。
在呼叫 UnserializePropertySet 後,因為有 Double Fetch 的漏洞,讓我們可以在檢查後到使用之間,將 KSPROPSETID_Service 換成 KSPROPSETID_DrmAudioStream ,而接下來就可以讓 ks 使用 KSPROPSETID_DrmAudioStream 作為 requests 來發送 IOCTL,從而觸發前述了 CVE-2024-35250 邏輯漏洞,使這個漏洞不論在甚麼環境下都可以使用。
最終我們成功在 Pwn2Own Vancouver 2024 中,成功攻下 Micorsoft Windows 11。
在 Pwn2Own 結束後,經過我們調查,發現到這個漏洞從 Windows 7 就存在了,至少存在將近 20 年,而且利用上非常穩定,有著百分之百的成功率,強烈建議大家盡速更新至最新版本 。
To be continued這篇主要著重在我們如何找到今年在 Pwn2Own 中所使用的漏洞及 Kernel Streaming 的攻擊面分析。在找到這個洞之後,我們後續也持續朝這個方向繼續研究,也發現了另外一個也是 Exploitable 的漏洞以及其他更多有趣的漏洞,我們預計在今年十月發表,敬請期待 Part II。
Reference- Critically Close to Zero-Day: Exploiting Microsoft Kernel Streaming Service
- Windows Kernel Security - A Deep Dive into Two Exploits Demonstrated at Pwn2Own
- CVE-2023-29360 Analysis
- Racing Round and Round: The Little Bug That Could
- Windows Kernel Logic Bug Class: Access Mode Mismatch in IO Manager
- Hunting for Bugs in Windows Mini-Filter Drivers
- Local Privilege Escalation via the Windows I/O Manager: A Variant Finding & Collaboration
CVE-2024-42769 | Kashipara Hotel Management System 1.0 /core/signup_user.php user_fname/user_lname cross site scripting
SecWiki News 2024-08-22 Review
更多最新文章,请访问SecWiki
硅谷 AI 泡沫逐渐消退
How Financial Institutions Can Manage Mounting Digital Sovereignty Requirements
Financial services is among the most highly regulated of any industry – and justifiably so. As critical infrastructure, the sector provides services which, if interrupted or destabilized, could have a catastrophic impact on economic and national security. Increasingly, these regulations mandate not only cyber-resilience (eg the EU’s DORA) but also digital sovereignty – which includes the idea that wherever data is collected or stored, it should be subject to local laws.
The post How Financial Institutions Can Manage Mounting Digital Sovereignty Requirements appeared first on Security Boulevard.
Intelligence Insights: August 2024
Slack Patches AI Bug That Let Attackers Steal Data From Private Channels
Cthulhu Stealer Malware Targets macOS With Deceptive Tactics
Comprehensive Guide to API Error Code Management
Mastering API error codes is essential for building robust and user-friendly applications. This comprehensive guide explores best practices for handling and documenting errors, ensuring clear communication between your API and its users.
The post Comprehensive Guide to API Error Code Management appeared first on Security Boulevard.
New 'ALBeast' Misconfiguration Exposes Weakness in AWS Application Load Balancer
CISA Warns of Critical SolarWinds RCE Vulnerability Exploited in Attacks
The U.S. Cybersecurity and Infrastructure Security Agency (CISA) has issued a critical warning regarding a newly discovered vulnerability in SolarWinds’ Web Help Desk solution, which has already been exploited in active attacks. Tell me more about the SolarWinds RCE Vulnerability SolarWinds’ Web Help Desk software is widely used by large enterprises, government agencies, healthcare providers and educational institutions to manage ... Read More
The post CISA Warns of Critical SolarWinds RCE Vulnerability Exploited in Attacks appeared first on Nuspire.
The post CISA Warns of Critical SolarWinds RCE Vulnerability Exploited in Attacks appeared first on Security Boulevard.