Sploitus

Exploit for Out-of-bounds Read in Microsoft

githubexploit · 2024-03-21

Exploit Code

README126 lines
## https://sploitus.com/exploit?id=A2BE8232-66D2-5723-AE64-9DBB703F1D41
# Information
==============

Windows Kernel Pool (clfs.sys) Corruption Privilege Escalation. (CVE-2023-36424)

This repo contains technical analysis & working exploit.

Author: Nassim Asrir (@p1k4l4) || https://www.linkedin.com/in/nassim-asrir-b73a57122/

# Vulnerability
================

There is a pool overflow in clfs.sys mini filter driver. Information on this can be read on:

1 - https://googleprojectzero.blogspot.com/2021/01/hunting-for-bugs-in-windows-mini-filter.html

2 - https://www.zerodayinitiative.com/blog/2021/7/19/cve-2021-31969-underflowing-in-the-clouds

The reason is that the driver is not sufficiently checks a data that comes from NTFS reparse point.

We will consider clfs.sys version 10.0.22621.2134 (Windows 11 22H2 22621.2215)

The function HsmFltProcessHSMControl is responsible for processing cloud filter FSCTLs. For an operation with code 0xC0000003 , it will eventually call HsmFltProcessUpdatePlaceholder . 

After some processing the execution flow will reach HsmiOpUpdatePlaceholderDirectory and then finally HsmpRpCommitNoLock: 

```c
__int64 __fastcall HsmpRpCommitNoLock(__int64 a1, __int64 a2, struct _FILE_OBJECT *a3, char a4, char a5)

{ .....

v26 = FileObject; LODWORD(v9) = HsmpRpReadBuffer(*(PFLT_INSTANCE *)(v164 + 32), FileObject, (unsigned __int16 **)&P); // [1*]

HsmDbgBreakOnStatus((unsigned int)v9);

if ( (_DWORD)v9 == -1073741195 ) .....

goto LABEL_55; }

if ( (v9 & 0x80000000) != 0i64 )

goto LABEL_9;}
 

if ( (*(_DWORD *)P & 0xFFFF0FFF) != dword_1C0027650 )// Is
Cloud Reparse Tag?
{
LODWORD(v9) = 0xC000CF0B;
.....
goto LABEL_54;
}
v32 = *((unsigned __int16 *)P + 2);
v9 = (unsigned int)HsmpRpValidateBuffer((__int64)P + 8, v32); [2*]
.....
Pool2 = ExAllocatePool2(0x100i64, 0x4000i64, 'pRsH'); // [3*]
v146 = (_DWORD *)Pool2;
v13 = (void *)Pool2;
if ( Pool2 )
{
v64 = v159_10;
v65 = Pool2 + 4;
if ( v8 && *((_WORD *)v8 + 7) > 0xAu )
v64 = *((_WORD *)v8 + 7);
v9 = Pool2 + 20;
v66 = (unsigned int *)(Pool2 + 12);
*(_OWORD *)v65 = 0i64;
*(_WORD *)(Pool2 + 16) = 0;
*(_WORD *)(Pool2 + 18) = v64;
*(_DWORD *)(Pool2 + 12) = 8 * v64 + 16;
*(_DWORD *)v65 = 'pReF';
memset((void *)(Pool2 + 20), 0, 8i64 * v64);
.....
if ( v8 )
{
v127 = 10;
if ( *((_WORD *)v8 + 7) > 0xAu ) // [4*]
{
if ( WPP_GLOBAL_Control !=
(PDEVICE_OBJECT)&WPP_GLOBAL_Control
&& (HIDWORD(WPP_GLOBAL_Control->Timer) & 1) != 0

&& BYTE1(WPP_GLOBAL_Control->Timer) >= 4u )
{
WPP_SF_qiq(WPP_GLOBAL_Control->AttachedDevice, v86,
v87, a2, *(_QWORD *)(v156 + 32), FileObject);
}
while ( v127  1 )
{
....
}
v9 = 0;
v66 = 0;
if ( v2 = v11 )
goto ERROR_EXIT;
v9 = 0x10000;
for ( i = 0; ; ++i )
{
v15 = *(unsigned __int16 *)(pBuf2 + 0xE);
if ( (unsigned int)v12 >= 0xA ) // [1*]
v15 = 10;
if ( i >= v15 )
break;
}
```

As we can see [1*] the code will verify only the first 10 items and ignores the case when there are more records.

# Exploitation
=================

The size of the vulnerable pool is 0x4000. The size is a multiple of page and therefore the segment allocation will be used [3].

For exploitation was used the technique described here[4]. Calling NtAlpcCreateResourceReserve will create a lot of handles and ovewriting one of them with the pointer to constructed fake _KALPC_RESERVE object will give us the ability to write to arbitrary kernel address.

To prepare the memory, we sequentially allocating pools of 0x4000 size, using pipes[5]. Then we will free every second pool, provide a place for vulnerable buffer. 

![Alt text](analysis1.png)

To read an arbitrary kernel address, the exploit utilised pipes. For this purpose we will overwrite AttributeValue pointer of the PipeAttribute structure. 

![Alt text](analysis2.png)

And afterwards, we can steal system token to overwrite token in the target process. 

Thanks for reading.