Contents

Part II: PowerShell Multithreading – Asyncronous Network and Host Discovery Scanner

Part II of my Get-SecNetMap “Mini-Module”:

  1. Get-SecNetMap
  2. Get-SecPortScan (This Post)
  3. Get-SecIPRange
  4. Convert-SecIPAddress
  5. Get-SecArpTable

Get-SecPortScan

You can download the module source files here: http://securekomodo.net/files/Get-SecNetMap.zip

Get-SecPortScan is unique in that it can scan a target host for many ports at once. This is done so by using .NET runspaces in PowerShell. This script will target a host and throw many asyncronous TCP socket connections over various ports either specified by the user, or using default top ports. It can be used standalone or in conjunction with the Get-SecNetMap function. Here is an example of the output when ran against healthcare.gov website…

PortScan

You see here that since I did not specify a list of ports to scan, the script used the default top 112 ports and found that 2 of those ports were open. Port 80 for HTTP and port 443 for HTTPS. You can see the potential for needing to know this information when undergoing a pentest of any kind.

 

Time for some code!

Get-SecPortScan

[sourcecode language=”powershell” wraplines=”false” collapse=”false”]
Function Get-SecPortScan {
<#
.SYNOPSIS
Scans a specified Server or IP for any open ports

.DESCRIPTION
Asyncronously scans multiple ports on a target and returns a list of the results

.PARAMETER Server
String to look up a server name by DNS

.PARAMETER IP
IP address to scan

.PARAMETER Ports
List of ports to be scanned, if not specified then the top ports will be scanned

.Example
Get-SecPortScan -IP securekomodo.net -Ports 80

Port IP Info Type Open
—- — —- —- —-
80 securekomodo.net TCP True

.EXAMPLE
Get-SecPortScan -IP securekomodo.net

Starting SecPortScan v1.0 ( Author: SecureKomodo ) at 04/06/2014 12:05:04

Port IP Info Type Open
—- — —- —- —-
80 securekomodo.net TCP True
443 securekomodo.net TCP True

SecPortScan done: 112 total ports ( 2 open ) scanned in 3.10 seconds

.NOTES
Name: Get-SecPortScan.ps1
Author: SecureKomodo
Version: 1.0

#>
[Cmdletbinding()]
Param (
[System.String]$Server,
[System.String]$IP,
[System.Array]$Ports,
$Throttle=50,
$Timeout=500,

# Switch to make output silent
[Parameter(Mandatory=$False)]
[Switch]$Silent
)

Begin {
$StartTime = Get-Date
if (!$Silent) {Write-Output (“Starting SecPortScan v1.0 ( Author: SecureKomodo ) at ” + $StartTime + “`n”) }

#Is Verbose Specified?
If ($PSCmdlet.MyInvocation.BoundParameters[“Verbose”].IsPresent) {
$Verb=$True
Write-Verbose “Starting Invoke-SecNetScan…”
}

# Static list of some top ports
if (!$Ports){$Ports=(
1,5,7,9,11,13,17,18,19,20,21,22,23,25,37,39,42,43,
49,50,53,63,67,68,69,70,71,72,73,73,79,80,88,95,101,
102,105,107,109,110,111,113,115,117,119,123,137,138,
139,143,161,162,163,164,174,177,178,179,191,194,199,
201,202,204,206,209,210,213,220,245,347,363,369,370,
372,389,427,434,435,443,444,445,464,468,487,488,496,
500,535,538,546,547,554,563,565,587,610,611,612,631,
636,674,694,749,750,765,767,873,992,993,994,995)}

If ($Server -and !$IP){$IP=[System.Net.Dns]::GetHostAddresses($Server)}

# Declaring Main Array
$Main=@()

# Runspace Array to hold jobs
$RunspaceArray = @()

#$ErrorActionPreference=’SilentlyContinue’

# Creating Runspace pool and Session States
$InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$RunspacePool = [Runspacefactory]::CreateRunspacePool(1, $Throttle, $InitialSessionState, $Host)
$RunspacePool.Open()

#Script to run in every runspace
$Script={
Param ($IP,$Port,$Timeout)

#Create temporary object
$TempObj = New-Object PSObject -Property @{
IP=”
Port=”
Type=”
Open=”
Info=”
}

#TCP Socket Object
$TCPSocket = New-Object System.Net.Sockets.TcpClient

#Connect to port
$BeginConnect = $TCPSocket.BeginConnect($IP,$Port,$null,$null)

#Configure a timeout before quitting
$WaitOne = $BeginConnect.AsyncWaitHandle.WaitOne($Timeout,$False)

#If/Else Timeout
If(!$WaitOne) {

#Close TCP Socket
$TCPSocket.Close()

#Log Info to Temp Object
$TempObj.IP = $IP
$TempObj.Port = $Port
$TempObj.Type = “TCP”
$TempObj.Open = $False
$TempObj.Info = “Connection to Port Timed Out”

} Else {
$Error.Clear()
$TCPSocket.EndConnect($BeginConnect) | Out-Null

#Shoutout to Boe Prox for helping make errors more readable for output
If($Error[0]){
[System.String]$ErrorException = ($Error[0].exception).message
$ErrorMessage = (($ErrorException.split(“:”)[1]).replace(‘”‘,””)).TrimStart()
$Failed = $True
} #End If Error

#Close TCP Socket
$TCPSocket.Close()

#If/Else Failed
If($Failed){

#Log Info to Temp Object
$TempObj.IP = $IP
$TempObj.Port = $Port
$TempObj.Type = “TCP”
$TempObj.Open = $False
$TempObj.Info = “$ErrorMessage”
} Else {

#Log Info to Temp Object
$TempObj.IP = $IP
$TempObj.Port = $Port
$TempObj.Type = “TCP”
$TempObj.Open = $True
$TempObj.Info = “”
} # End If/Else Failed

} #End If/Else Timeout

#Reset failed value
$Failed = $Null

#Send From TempObj Runspace to Main Output later
Return $TempObj.IP,$TempObj.Port,$TempObj.Type,$TempObj.Open,$TempObj.Info

} #End Script

} #End Begin

Process {
$pCount=0
# Start loop to create Runspaces
foreach ($Port in $Ports) {
$pCount++
Write-Progress -Id 2 -Activity “Scanning Ports…” -Status $Port -PercentComplete (($pCount/$Ports.Count)*100)

#Create the powershell instance and supply the scriptblock with the other parameters
$Powershell = [Powershell]::Create().AddScript($Script).AddArgument($IP).AddArgument($Port).AddArgument($Timeout)

#Add the runspace into the powershell instance
$Powershell.RunspacePool = $RunspacePool

#Create a TempArray for each runspace
$TempArray = New-Object PSObject -Property @{
PowerShell=$null
Runspace=$null
Port=$null
} #End Temporary Array

$TempArray.Port = $Port
$TempArray.PowerShell = $Powershell

#Save the handle output when calling BeginInvoke() that will be used later to end the runspace
$TempArray.Runspace = $Powershell.BeginInvoke()
$RunspaceArray += $TempArray

} #End Foreach IP in Subnet

#Retrieve runspaces from $runspace array
Do {

#On/Off Switch
$Complete=$False

Foreach($Runspace in $RunspaceArray) {

If ($Runspace.Runspace.isCompleted) {

#Job Done. Retrieve Output from the ScriptBlock
$Main += New-Object PSObject -Property @{
IP=($Runspace.Powershell.EndInvoke($Runspace.Runspace))[0]
Port=($Runspace.Powershell.EndInvoke($Runspace.Runspace))[1]
Type=($Runspace.Powershell.EndInvoke($Runspace.Runspace))[2]
Open=($Runspace.Powershell.EndInvoke($Runspace.Runspace))[3]
Info=($Runspace.Powershell.EndInvoke($Runspace.Runspace))[4]
} #End Main

$Runspace.Powershell.dispose()
$Runspace.Runspace = $Null
$Runspace.Powershell = $Null
$Complete=$True

} #End If Runspace is Complete

} #End Foreach Runspace

#Check to see if Runspaces exist
If ($Runspace | Where-Object {$_.PowerShell}) {$Complete=$False} Else {$Complete=$True}

} Until ($Complete)

} # End Process

End {

$EndTime = Get-Date
$TimeSpan = (New-TimeSpan -Start $StartTime -End $EndTime).TotalSeconds
$PortsOpen = $Main.Open | Where-Object {$_ -eq $True} | Group-Object
$PortsClosed = $Main.Open | Where-Object {$_ -ne $True} | Group-Object

if ($PortsClosed -and !$Silent) {Write-Output (“Not Shown: ” + $PortsClosed.Count + ” closed ports”) }
if ($PortsOpen) {$MainPortsOpen = $Main | Where-Object {$_.Open -eq $True} | Format-Table -AutoSize}

if (!$Silent) {Write-Output (“`nSecPortScan done: ” + $Ports.Count + ” total ports ( ” + ($PortsOpen.Count) + ” open ) ” + (“scanned in {0:N2}” -f $TimeSpan) + ” seconds”) }
Return $MainPortsOpen
} #End END

} #End Function
[/sourcecode]

Future revisions of this script will be released and will include UDP support, and more!

Tags// ,