PowerShell for Incident Responders - The Basics

PowerShell for Incident Responders - How to automate your work

What are the things you do every day over and over again? These are things you should automate.
Sometimes the best automation is to write a script - often times the best automation is to learn how to use a scripting language like PowerShell to do it.
The hints below are designed for the world of Incident Response.

1) How to get help
If you are unsure of what a commands name is you can use get-command

PS > get-command *write*

will list all commands that contain 'write'

To find out what a particular command does and what parameters it excepts

PS > man <command>
or
PS > get-help <command>
or
PS > help <command>

man and help are aliases for get-help

if you run a command like

PS > ps (or get-process)

it will display a list of running processes but that is only a small part of it's capabilities
Run

PS > ps | gm (or get-member)

and it will display a long list of other information it can provide as well as some methods it can run against the data
After running ps, I can choose a process by either its name or ID

PS > ps | where name -eq 'Services' 
or
PS > ps -id 1256

Once I have located the service I want I can stop the process with the kill command

PS > (ps -id 1256).kill()

The ps command is enclosed in parenthis to signify that the output of the command is what the kill() function is referencing

2) Working with select-string (grep) output
A common function when doing incident response is to use select-string (or sls) to search text files for information.
sls uses regular expressions (REGEX) to do its searches. REGEX is an obtuse but extremely powerfull language - it is worth all the time you spend learning it.

Everything in PowerShell is an object - when we ran the gm command earlier it listed all the properties and methods of the ps object
when you run a search with sls it also returns an object - understanding this object unlocks its power.

If you want to search one file the syntax is as follows:

PS > sls 'somestring' temp.txt
of course you can use the same syntax to search multiple files
PS > sls 'somestring' *.csv

The output of either of these commands will be in the format
Filename:LineNumber:Line

If you want to know what the parts of sls object is you can use:

PS > sls 'somestring' *.txt | gm

If you want to pick just parts of the standard output 

PS > sls 'somestring' *.txt | select filename,line

or if just one part

PS > (sls 'somestring' *.txt).line

or to display all the data

PS> sls 'somestring' *.csv | select *

What if you only want to know the filenames that string resides in

PS > (sls 'somestring' *.txt).filename

oh but if the word appears multiple times in a file it will list the filename for each time - to make a list of only the unique filenames 

PS > (sls 'somestring' *.txt).filename | select -unique

If you want to look at all the files in a directory and subdirectories

PS > dir * -recurse | sls 'somestring'
or only csv and txt files
PS > dir *.csv,*.txt -recurse | sls 'somestring'
or a particular file
PS > sls *system.csv -recurse | sls 'somestring'

Fancy PowerShell commands are built with small ones

PS > (dir *.csv,*.txt -recurse | sls 'somestring').path 
path returns the full path
PS > (dir *.csv,*.txt -recurse | sls 'somestring').path | select -unique
or even better open each file 
PS > (dir *.csv,*.txt -recurse | sls 'somestring').path | select -unique | %{& $_}

A little explanation for %{& $_}
% = foreach
$_ = current piece of data - in this case the path to the file
& = open the file with the associated program

If you wonder what kind of data .path is
PS > (dir *.csv,*.txt -recurse | sls 'somestring').path | gm

Using different combinations of these commands
Open all the *systems.csv files in all subdirectories
PS > dir *systems.csv -r | %{& $_}

If you only want a listing of the files 
PS > dir *.systems.csv | select filename

3) Filtering output
Output from commands can be done in two ways depending on what you need
PowerShell mostly emits objects - frequently what is returned is an array of objects.
For instance if you use the get-content (gc) command to read in a text file what results is an array of string objects with each line an object and then entire file an array of lines.
If you run 

PS > $s = gc test.txt

$s will contain an array with each element representing one line
You can address each line with $s[4] or whatever the line number is
This array starts with 0 so the first line is $s[0]
Note that the output of any command can be saved in a variable (here $s) you create a variable by just naming it.
	The variable thus created takes on the type of the first object you stick in it or you can specifically type the variable with either
	[int]$s or specifying an empty type like $s = @() which creates an array of objects
	You do need to be careful if you create a variable and add data to it which types it. If you try to add data of a different type it my cause issues
Back to filtering output - the import-csv command can be used for a good example
Lets be specific and work with some output from Volcano Surge.

PS > $s = import-csv DC1~networkconnections.csv

If you display $s

PS > $s

it returns a series of records like

EntryID      : 53
dwState      : 3
dwLocalAddr  : 127.0.0.1
dwLocalPort  : 56545
dwRemoteAddr : 127.0.0.1
dwRemotePort : 9050
dwOwningPid  : 19784

EntryID      : 54
dwState      : 5
dwLocalAddr  : 192.168.88.193
dwLocalPort  : 56546
dwRemoteAddr : 178.128.242.134
dwRemotePort : 3333
dwOwningPid  : 3616

EntryID      : 55
dwState      : 3
dwLocalAddr  : 127.0.0.1
dwLocalPort  : 56547
dwRemoteAddr : 127.0.0.1
dwRemotePort : 9050
dwOwningPid  : 8780

Here we have records with fields: EntryID, dwState, dwLocalAddr, dwLocalPort, dwRemoteAddr, dwRemotePort, dwOwningPid 

if we want to retrieve all the dwRemoteAddr

PS > $s.dwRemoteAddr 

will do it. You can save the output in a file with

PS > $s.dwRemoteAddr > ips.txt

or save it in a variable with

PS > $ip = $s.dwRemoteAddr

Once it is in a variable you can get only the unique entries with

PS > $ip | select -unique > ips.txt

or if you want to get fancy you can create a histogram with

PS > $ip | group | select count, name | sort count -descending

If you want to retrieve all the records containing a specific address you use the where command

PS > $s | where dwRemoteAddr -eq '169.198.0.13'

If you want to filter on more than one field

PS > $s | where {$_.dwLocalAddr -eq '169.198.0.13' -or $_.dwRemoteAddr -eq '169.198.0.13'}