LEARN >> Hacking With PowerShell

Table of Contents

This room is all about Windows PowerShell. In this room we will explore the following concepts:

  • What is PowerShell and how it works
  • Basic PowerShell commands
  • Windows enumeration with PowerShell
  • PowerShell scripting

Task 2 – What is PowerShell?

PowerShell is a Windows scripting language and shell environment that is built using the .NET framework.

This also allows PowerShell to execute .NET functions directly from it’s shell. Most PowerShell commands, called "cmdlets", are written in .NET. Unlike other scripting languages and shell environments, the output of these cmdlets are objects – making PowerShell somewhat object orientated. This also means that running cmdlets allows you to perform actions on the output object (which makes it convenient to pass output from one cmdlet to another). The normal format of a cmdlet is represented using Verb-Noun; for example the cmdlet to list commands is called Get-Command.

Common verbs to use include:

  • Get
  • Start
  • Stop
  • Read
  • Write
  • New
  • Out

To get the full list of approved verbs, visit this link: https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands?view=powershell-7

Task 3 – Basic PowerShell commands

Now that we’ve understood how cmdlets work – let’s explore how to use them!

The main thing to remember is that Get-Command and Get-Help are your best friends!

Using Get-Help

Get-Help displays information about a cmdlet. To get help about a particular command, run the following:

Get-Help Command-Name

You can also understand how exactly to use the command by passing in the -examples flag. This would return output like the following:

Using Get-Command

Get-Command gets all the cmdlets installed on the current computer. The great thing about this cmdlet is that it allows for pattern matching like the following:

Get-Command Verb-* or Get-Command *-Noun

Running Get-Command New-* to view all the cmdlets for the verb "New" displays the following:

Object Manipulation

In the previous task, we saw how the output of every cmdlet is an object. If we want to actually manipulate the output, we need to figure out a few things:

  • passing output to other cmdlets
  • using specific object cmdlets to extract information

The Pipeline (|) is used to pass output from one cmdlet to another. A major difference compared to other shells is that instead of passing text or string to the command after the pipe, PowerShell passes the object to the next cmdlet. Like every object in object-oriented frameworks, an object will contain methods and properties. You can think of methods as functions that can be applied to output from the cmdlet, and you can think of properties as variables in the output from a cmdlet.

To view these details, pass the output of a cmdlet to the Get-Member cmdlet:

Verb-Noun | Get-Member

An example of running this to view the members for Get-Command is:

Get-Command | Get-Member -MemberType Method

From the above flag in the command, you can see that you can also select between methods and properties.

Creating objects from previous cmdlets

One way of manipulating objects is pulling out the properties from the output of a cmdlet and creating a new object. This is done by using the Select-Object cmdlet.

Here’s an example of listing the directories and just selecting the mode and the name:

Get-ChildItem | Select-Object -Property Mode, Name

You can also use the following flags to select particular information:

  • first – gets the first X object(s)
  • last – gets the last X object(s)
  • unique – shows only the unique objects
  • skip – skips X object(s)

Filtering objects

When retrieving output objects, you may want to select objects that match a very specific value. You can do this using the Where-Object cmdlet to filter based on the value of properties.

The general format of using this cmdlet is:

Verb-Noun | Where-Object -Property PropertyName -operator Value

Verb-Noun | Where-Object {$_.PropertyName -operator Value}

The second version uses the $_ operator to iterate through every object passed to the Where-Object cmdlet.

NOTE: PowerShell is quite sensitive so make sure you don’t put quotes around the command!

Where -operator is a list of the following operators:

  • -Contains – if any item in the property value is an exact match for the specified value
  • -EQ – if the property value is the same as the specified value
  • -GT – if the property value is greater-than the specified value

For a full list of operators, use this link: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/where-object?view=powershell-6

Here is an example of checking the stopped processes:

Sort Object

When a cmdlet outputs a lot of information, you may need to sort it to extract the information more effectively. You do this by pipelining the output of a cmdlet to the Sort-Object cmdlet.

The format of the command would be:

Verb-Noun | Sort-Object

Here is an example that sorts the list of directories:

Get-ChildItem | Sort-Object

Now that you’ve understood the basics of how PowerShell works, lets try some commands to apply this knowledge!

PS C:\Users\Administrator> Get-ChildItem -Filter *.txt -Path \ -Recurse

    Directory: C:\Program Files

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        10/3/2019  11:38 PM             23 interesting-file.txt.txt
PS C:\Users\Administrator> cat "C:\Program Files\interesting-file.txt.txt"
PS C:\Users\Administrator> Get-Command | Where-Object -Property CommandType -EQ Cmdlet | Measure

Count    : 6638
Average  :
Sum      :
Maximum  :
Minimum  :
Property :
PS C:\Users\Administrator> Get-FileHash -Path "C:\Program Files\interesting-file.txt.txt" -Algorithm MD5 | Select-Object -Property Hash

PS C:\Users\Administrator> Get-Location

PS C:\Users\Administrator> Test-Path -Path "C:\Users\Administrator\Documents\Passwords"

PS C:\Users\Administrator> $file = "C:\Users\Administrator\Desktop\b64.txt"
PS C:\Users\Administrator> $data = Get-Content $file
PS C:\Users\Administrator> [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($data))
this is the flag - ihopeyoudidthisonwindows
the rest is garbage

Task 4 – Enumeration

PS C:\Users\Administrator> Get-LocalUser

Name           Enabled Description
----           ------- -----------
Administrator  True    Built-in account for administering the computer/domain
DefaultAccount False   A user account managed by the system.
duck           True
duck2          True
Guest          False   Built-in account for guest access to the computer/domain
PS C:\Users\Administrator> Get-LocalUser -SID S-1-5-21-1394777289-3961777894-1791813945-501

Name  Enabled Description
----  ------- -----------
Guest False   Built-in account for guest access to the computer/domain
PS C:\Windows\WinSxS\Backup> Get-LocalUser | Where-Object -Property PasswordRequired -Match false

Name           Enabled Description
----           ------- -----------
DefaultAccount False   A user account managed by the system.
duck           True
duck2          True
Guest          False   Built-in account for guest access to the computer/domain

NOTE: PowerShell for some reason (I am too n00b at this stage) refuses to work this out using -EQ False, regardless of whether "False" is wrapped in single or double quotes or none… however the number 0 happens to equal to false anyway… 😉

PS C:\Users\Administrator> Get-LocalGroup | Measure

Count    : 24                                      
PS C:\Users\Administrator> Get-Command Get-*IP* | Where-Object -Property Source -EQ NetTCPIP

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        Get-NetIPAddress                             NetTCPIP
Function        Get-NetIPConfiguration                       NetTCPIP
Function        Get-NetIPInterface                           NetTCPIP
Function        Get-NetIPv4Protocol                          NetTCPIP
Function        Get-NetIPv6Protocol                          NetTCPIP
PS C:\Users\Administrator> Get-NetTCPConnection | Where-Object -Property State -EQ Listen | Measure

Count    : 20
PS C:\Users\Administrator> Get-NetTCPConnection | Where-Object -Property LocalPort -EQ 445 | Select-Object -Property RemoteAddress


NOTE: Yes the answer is :: – if you look closely at the character amount hint you usually get it actually gives you the answer!

PS C:\Users\Administrator> Get-WmiObject -Class win32_quickfixengineering | Measure

Count    : 20
PS C:\Users\Administrator> Get-CimInstance -Class win32_quickfixengineering | Where-Object -Property HotFixID -EQ KB4023834

Source        Description      HotFixID      InstalledBy          InstalledOn
------        -----------      --------      -----------          -----------
              Update           KB4023834     EC2AMAZ-5M13VM2\A... 6/15/2017 12:00:00 AM
PS C:\Windows\WinSxS\Backup> Get-ChildItem -Filter *.bak* -Path \ -Recurse

    Directory: C:\Program Files (x86)\Internet Explorer

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        10/4/2019  12:42 AM             12 passwords.bak.txt

PS C:\Windows\WinSxS\Backup> Get-Content "C:\Program Files (x86)\Internet Explorer\passwords.bak.txt"
PS C:\Windows\WinSxS\Backup> Get-ChildItem -Filter * -Path \Users -Recurse | Select-String -pattern API_KEY

PS C:\Windows\WinSxS\Backup> Get-Process

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    120       8    20976      12852       0.23   1776   0 amazon-ssm-agent
    190      13     4468      18312      13.91   3748   2 conhost
    195      10     1752       3932       0.20    524   0 csrss
PS C:\Windows\WinSxS\Backup> Get-ScheduledTask | Where-Object -Property TaskName -EQ new-sched-task

TaskPath                                       TaskName                          State
--------                                       --------                          -----
\                                              new-sched-task                    Ready

NOTE: The answer above is correct, however it is incorrectly set as / on THM… it also gives you the answer as the hint on number of characters:

PS C:\Windows\WinSxS\Backup> Get-Acl C:\


Path Owner                       Access
---- -----                       ------
C:\  NT SERVICE\TrustedInstaller CREATOR OWNER Allow  268435456...

Task 5 – Basic Scripting Challenge

Now that we have run PowerShell commands, let’s actually try to write and run a script to do more complex and powerful actions!

For this, we will be using PowerShell ISE (which is the PowerShell Integrated Scripting Environment).

To show an example of this scripting environment, let’s use a particular scenario. Given a list of port numbers, we want to use this list to see if the local port is listening. Open the listening-ports.ps1 script on the Desktop using PowerShell ISE. PowerShell scripts usually have the .ps1 extension.

$system_ports = Get-NetTCPConnection -State Listen

$text_port = Get-Content -Path C:\Users\Administrator\Desktop\ports.txt

foreach($port in $text_port){

    if($port -in $system_ports.LocalPort){
        echo $port


On the first line, we want to ge a list of all the ports on the system that are listening. We do this using the Get-NetTCPConnection cmdlet. We are then saving the output of this cmdlet into a variable. The convention to create variables is used as:

$variable_name = value

On the next line, we want to read a list of ports from a file. We do this using the Get-Content cmdlet. Again, we store the output in a variable. The next step is to iterate through all the ports in the file to see if the ports are listening locally. To iterate through the ports in the file, we use the following:

foreach($new_var in $existing_var){}

This particular code block is used to loop through a set of objects. Once we have each individual port, we want to check if this port is listening locally. Instead of doing another for loop, we just use an if statement with the -in operator to check if the port exists in the LocalPort property of any object. A full list of if statement comparison operators can be found here: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comparison_operators?view=powershell-6

To run the script, just call the script path using PowerShell or click the green "play" button on the top button bar of PowerShell ISE:

Now that we have seen what a basic script looks like – it’s time to write one of your own. The emails folder on the Desktop contains copies of the emails John, Martha and Mary have been sending to each other (and themselves). Answer the following questions with regards to these emails (try not to open the files and use a script to answer the questions).

Scripting may be a bit difficult, but here is a good resource to use: https://learnxinyminutes.com/docs/powershell/

The code…

echo "------------------- -  -"
echo "emailSCAN > stimpz0r [2021]"
echo "------------------------ -  -    -"
$password = "password is "
$https = "https"
$emails = Get-ChildItem "C:\Users\Administrator\Desktop\emails" *.txt -Recurse | Select-Object FullName
foreach($email in $emails){
    $line_no = 0
    $text = Get-Content $email.FullName
    foreach($line in $text){
        $line_no = $line_no + 1
        if($line -match $password){
            if($line_no -ne 6){
                echo "PASS >> $email ($line_no): $line"
        if ($line -match $https){
            echo "HTTPS >> $email ($line_no): $line"

NOTE: the extra if($line_no -ne 6) nested if was to skip over a false-positive, so the output only shows the 2 relevant lines.

The output…

------------------- -  -
emailSCAN > stimpz0r [2021]
------------------------ -  -    -
PASS >> @{FullName=C:\Users\Administrator\Desktop\emails\martha\Doc3M.txt} (106): password is johnisalegend99
HTTPS >> @{FullName=C:\Users\Administrator\Desktop\emails\mary\Doc2Mary.txt} (5): https://www.howtoworkwell.rand/

PS C:\Users\Administrator>

Task 6 – Intermediate Scripting

Now that you’ve learnt a little bit about how scripting works – let’s try something a bit more interesting!

Sometimes we may not have utilities like nmap and python available, and we are forced to write scripts to do very rudimentary tasks. Why don’t you try writing a simple port scanner using PowerShell. Here’s the general approach to use:

  • Determine IP ranges to scan (in this case it will be localhost) and you can provide the input in anyway you like
  • Determine the port ranges to scan
  • Determine the type of scan to run (in this case it will be a simple TCP Connect Scan)

The code…

$pok = 0
$pfi = 0
$pno = 0
$sport = 0
$eport = 0
echo "------------------ --- -  -"
echo "portSKAM > stimpz0r [2021]"
echo "-------------------- ---- -- -  -"
echo ""
$ip = Read-Host "  TARGET IP > "
$s_port = Read-Host " START PORT > "
$e_port = Read-Host "FINISH PORT > "
$sport = [int]$s_port
$eport = [int]$e_port
echo ""
echo "------------------ --- -  -"
echo "starting portSKAM >> $ip (from $sport to $eport)"
echo "-------------------- ---- -- -  -"
echo ""
while ($sport -lt $eport + 1) {
    $sock = New-Object System.Net.Sockets.TcpClient
    $conn = $sock.ConnectAsync($ip,$sport)
    for ($i=0; $i -lt 10; $i++) {
        if ($conn.IsCompleted) { break; }
        sleep -Milliseconds 100
    if ($conn.IsFaulted -and $conn.Exception -match "actively refused") {
        $rsp = "[-]   CLOSED - $sport"
    } elseif ($conn.Status -eq "RanToCompletion") {
        $rsp = "[+] IS OPEN! - $sport"
    } else {
        $rsp = "[*] FILTERED - $sport"
    echo $rsp
echo ""
echo "------------------ --- -  -"
echo "[+] portSKAM COMPLETE >> $ip - $pok OPEN / $pno CLOSED ($pfi FILTERED)" 
echo "-------------------- ---- -- -  -"

The result…

------------------ --- -  -
portSKAM > stimpz0r [2021]
-------------------- ---- -- -  -

 START PORT > : 130

------------------ --- -  -
starting portSKAM >> (from 130 to 140)
-------------------- ---- -- -  -

[-]   CLOSED - 130
[-]   CLOSED - 131
[-]   CLOSED - 132
[-]   CLOSED - 133
[-]   CLOSED - 134
[+] IS OPEN! - 135
[-]   CLOSED - 136
[-] FILTERED - 137
[-]   CLOSED - 138
[-]   CLOSED - 139
[-]   CLOSED - 140

------------------ --- -  -
-------------------- ---- -- -  -

PS C:\Users\Administrator> 

NOTE: yes, clearly that answer is not what the portscan showed… but it seems the creator of this room mistook "open" for "total" ports in the range… correct answer is 1 (filtered is not considered open)

Leave a Reply

Your email address will not be published. Required fields are marked *