PowerShell is the most powerful tool in a Windows security professional's arsenal. It provides deep access to the Windows API, .NET Framework, WMI, COM objects, and virtually every aspect of the operating system. Both attackers and defenders rely heavily on PowerShell — and understanding it is essential for modern Windows security work.
In this hands-on lesson, you will learn PowerShell commands and scripts for system enumeration, security assessment, Active Directory reconnaissance, and defensive monitoring. All exercises are designed for authorized lab environments.
💡 PowerShell is now cross-platform (PowerShell 7+) and open-source. However, Windows PowerShell 5.1 (built into Windows) remains the most relevant for security work due to its deep integration with Windows-specific APIs and .NET Framework. This lesson focuses on Windows PowerShell 5.1 with notes on PowerShell 7 where relevant.
PowerShell commands follow a Verb-Noun naming convention (Get-Process, Set-Item, Invoke-Command). Understanding this structure helps you discover new commands. Key concepts:
# Discover all commands in a module
Get-Command -Module NetSecurity
# Get detailed help for any command
Get-Help Get-Process -Full
Get-Help Get-Process -Examples
# Find commands by verb or noun
Get-Command -Verb Get -Noun *process*
Get-Command -Verb Invoke
# Understand an object's properties and methods
Get-Process | Get-Member
Get-Process -Name powershell | Select-Object Id, Name, Path, StartTime⚠️ PowerShell's Execution Policy (Restricted, AllSigned, RemoteSigned, Unrestricted) is NOT a security boundary. It is designed to prevent accidental script execution, not to stop attackers. Any of these bypasses work: powershell -ep bypass, powershell -c "command", or encoding commands in Base64. Real PowerShell security requires constrained language mode, AMSI, Script Block Logging, and WDAC.
The first step in any security assessment is understanding the target. PowerShell provides comprehensive system enumeration capabilities:
# === SYSTEM INFORMATION ===
# Basic system info
Get-ComputerInfo | Select-Object
WindowsProductName, WindowsVersion, OsArchitecture,
CsName, CsDomain, OsLastBootUpTime
# Detailed hardware info
Get-CimInstance -ClassName Win32_Processor | Select-Object Name, NumberOfCores
Get-CimInstance -ClassName Win32_PhysicalMemory | Select-Object Capacity, Speed
# Installed software (common persistence locations)
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" |
Where-Object { $_.DisplayName } |
Select-Object DisplayName, DisplayVersion, Publisher, InstallLocation, InstallDate |
Sort-Object DisplayName | Format-Table -AutoSize
# Installed security products
Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct |
Select-Object displayName, productState
# Running processes with full paths
Get-Process | Select-Object Id, ProcessName, Path, Company |
Where-Object { $_.Path -and $_.Path -notmatch 'Windows' } |
Format-Table -AutoSize
# Network connections
Get-NetTCPConnection | Where-Object { $_.State -eq 'Established' } |
Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, OwningProcess, State |
Format-Table -AutoSize
# Firewall rules (look for overly permissive rules)
Get-NetFirewallRule | Where-Object { $_.Enabled -eq 'True' -and $_.Direction -eq 'Inbound' } |
Get-NetFirewallPortFilter | Where-Object { $_.LocalPort -in @(21,23,3389,445,5985) } |
Select-Object Protocol, LocalPort, RemotePortUnderstanding your current security context and the user landscape is critical for both offensive and defensive work:
# Current user context
whoami
whoami /user # Shows SID
whoami /groups # Shows group memberships
whoami /priv # Shows token privileges
# Current user via PowerShell
[Security.Principal.WindowsIdentity]::GetCurrent().Name
[Security.Principal.WindowsIdentity]::GetCurrent().Groups |
ForEach-Object { $_.Translate([Security.Principal.NTAccount]) }
# Check if running as admin
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
[Security.Principal.WindowsBuiltInRole] "Administrator"
)
Write-Host "Running as Admin: $isAdmin"
# List all local users and their properties
Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordLastSet,
PasswordRequired, SID | Format-Table -AutoSize
# List members of privileged groups
Get-LocalGroupMember -Group "Administrators"
Get-LocalGroupMember -Group "Remote Desktop Users"
Get-LocalGroupMember -Group "Backup Operators"
# Check for users with sensitive privileges
# (In domain context, use ActiveDirectory module)
Get-ADGroupMember -Identity "Domain Admins" -Recursive |
Select-Object Name, SamAccountNamePowerShell excels at finding security-relevant files and permission issues:
# Find files with sensitive extensions in user-writable locations
Get-ChildItem -Path "C:\Users" -Recurse -Include *.txt,*.csv,*.xml,*.config,*.ps1,*.bat -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) } |
Select-Object FullName, Length, LastWriteTime | Format-Table -AutoSize
# Find files containing potential credentials (use carefully in production)
Get-ChildItem -Path "C:\" -Recurse -Include *.xml,*.config,*.ini,*.txt -ErrorAction SilentlyContinue |
Select-String -Pattern "password|passwd|pwd|secret|key" -SimpleMatch -ErrorAction SilentlyContinue |
Select-Object Path, LineNumber, Line | Select-Object -First 20
# Find world-writable files in program directories
Get-ChildItem -Path "C:\Program Files","C:\Program Files (x86)" -Recurse -ErrorAction SilentlyContinue |
ForEach-Object {
$acl = Get-Acl $_.FullName
$acl.Access | Where-Object {
$_.IdentityReference -match "Everyone|Users|Authenticated Users" -and
$_.FileSystemRights -match "Write|FullControl|Modify"
} | Select-Object @{N='File';E={$_.FullName}}, IdentityReference, FileSystemRights
}
# Find recently created executables (potential malware)
Get-ChildItem -Path "C:\Users","C:\Temp","C:\Windows\Temp" -Recurse -Include *.exe,*.dll,*.ps1,*.bat,*.vbs -ErrorAction SilentlyContinue |
Where-Object { $_.CreationTime -gt (Get-Date).AddDays(-3) } |
Select-Object FullName, CreationTime, Length | Sort-Object CreationTime -DescendingIn domain environments, PowerShell with the ActiveDirectory module is the primary tool for AD enumeration. This is essential for both penetration testers and defenders:
# Import ActiveDirectory module (requires RSAT or domain controller)
Import-Module ActiveDirectory
# Domain information
Get-ADDomain | Select-Object Name, DomainMode, DNSRoot, PDCEmulator, RIDMaster
Get-ADForest | Select-Object Name, Domains, Sites, GlobalCatalogs
# Find all domain controllers
Get-ADDomainController -Filter * | Select-Object Name, IPv4Address, Site, OperatingSystem
# Enumerate users with specific properties
Get-ADUser -Filter * -Properties * | Where-Object {
$_.PasswordNeverExpires -eq $true -or
$_.TrustedForDelegation -eq $true
} | Select-Object Name, SamAccountName, PasswordNeverExpires, TrustedForDelegation
# Find accounts with SPNs (Kerberoasting targets)
Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName |
Select-Object Name, SamAccountName, ServicePrincipalName
# Find accounts with pre-authentication disabled (AS-REP Roasting)
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true} |
Select-Object Name, SamAccountName
# Find domain admins and their group memberships
Get-ADGroup -Filter {Name -like "*admin*"} |
ForEach-Object {
Write-Host "\n=== $($_.Name) ===" -ForegroundColor Yellow
Get-ADGroupMember -Identity $_ -Recursive | Select-Object Name, objectClass
}
# Find computers with unconstrained delegation
Get-ADComputer -Filter {TrustedForDelegation -eq $true} -Properties TrustedForDelegation |
Select-Object Name, DNSHostName, TrustedForDelegationBloodHound is the industry-standard tool for Active Directory attack path mapping. It uses PowerShell collectors (SharpHound) to ingest AD data and Neo4j to visualize attack paths. Understanding the underlying PowerShell enumeration commands helps you interpret BloodHound results and perform manual verification.
PowerShell is also a powerful defensive tool. Modern Windows security relies heavily on PowerShell for log analysis, threat hunting, and incident response:
# Enable PowerShell Script Block Logging (defensive)
# This logs ALL script blocks executed, including deobfuscated code
$path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"
New-Item -Path $path -Force
Set-ItemProperty -Path $path -Name "EnableScriptBlockLogging" -Value 1
# Enable PowerShell Transcription (logs all input/output)
$path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription"
New-Item -Path $path -Force
Set-ItemProperty -Path $path -Name "EnableTranscripting" -Value 1
Set-ItemProperty -Path $path -Name "OutputDirectory" -Value "C:\PSTranscripts"
# Hunt for suspicious PowerShell activity in event logs
# Event ID 4104 = Script Block Logging
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational'; ID=4104} -MaxEvents 20 |
ForEach-Object {
[PSCustomObject]@{
TimeCreated = $_.TimeCreated
ScriptBlock = $_.Properties[2].Value # The actual script content
}
}
# Detect encoded commands (common attacker technique)
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4688} -MaxEvents 100 |
Where-Object { $_.Message -match "powershell.*-enc" -or $_.Message -match "powershell.*-encoded" } |
Select-Object TimeCreated, Message
# Check for PowerShell remoting sessions
Get-PSSession
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-WinRM/Operational'} -MaxEvents 10💡 AMSI (Antimalware Scan Interface) is a critical defense against malicious PowerShell. It allows security products to inspect script content at runtime, even if the script is obfuscated or downloaded in memory. However, AMSI bypasses exist and are commonly used by advanced attackers. Defense in depth — combine AMSI with Script Block Logging, constrained language mode, and WDAC.
Verify exercises to earn ★ 150 XP and unlock next lab level.