Contents

Part I: Powershell Multithreading: Asynchronous Network and Host Discovery Scanner

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

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

A Quick Word about Powershell + Multithreading

At the time of this post, it has been just over a year since I started using Powershell as my “go-to” scripting language. As my skills developed and my scripts became more robust, I now see that what has been lacking in my Powershell journey was true performance metering. Many of the scripts I have written are targeting 5000+ endpoints and I simply do not have the time to wait multiple hours for a big job like that to run. I have been pidgeon-holing myself into endless for-each loops that wait for one task to finish before the next task can start. To answer the call of performance I started researching how Powershell can handle multitasking and concurrent connections. There are a few blogs here and there that show some samples of using “Jobs” but I was not impressed. I was able to find a post by the author Boe Prox who really helped open my eyes on what truly is the best way to multithread in Powershell. His research showed that using .NET runspaces are far superior in terms of performance when compared to PS Jobs because there is no overhead that is normally created through cmdlets like Start-Job which can really slow things down. I am not going to go into too much detail about the various ways Powershell can handle multithreading because that is a topic I will go into detail on in a later post. Rather I wanted to share a really cool tool I have been working for about 2 weeks now. I plan to add it to the ever-growing tool set for PoshSec which is a security module for Powershell created by some really cool dudes I have been helping on development with.

Get-SecNetmap

Introducing Get-SecNetMap, a Network and Host Discovery Scanner that could be the BEST nmap alternative for native Windows systems. Let me explain…

Nmap is one of the most well-know security tools there is for Linux systems and for good reason. It works well! I have been searching for a great Windows alternative that can provide at least the most basic features of nmap and had no success. All I found was simple one liners to Test-Connection against a computer or list of computers. Nothing that could come close to the features and performance of nmap at all. It was at that time I decided to stop searching and just start to develop my own. A true hacker mentality 😛

Lets take a look at some of one of the various features of this module.

ScanSubnet

You can see from the above output that no host was specified to scan. This means that it will automatically use the local IP address of the host running the script and calculate the network address and broadcast address to determine the subnet to scan. Once the range is determined, this particular command is set to randomize the target IPs so that you are not scanning incrementally. It makes all these determinations and provides you real time output in less then 7 seconds on my slow WLAN. I have seen speeds on faster LANs of scanning a range of 2500 hosts in less than 25 seconds!!!

I came up with the following tool that can be considered the “alpha” release for this module. Here are some of the modules key features.

  • Scans an IP specified to determine if it is active or inactive on a network
  • Scans an entire subnet to find all active/inactive hosts on a network
  • Scans a specified range of IP addresses to find active/inactive hosts on a network
  • Asynchronously scans a specified IP/host for top known ports
  • Converts an IP address to an integer (vice-verse)
  • Can randomize the IPs to be scanned to assist in throwing off Intrusion Detection Systems (IDS)
  • Allow a specified timeout between scanning to assist in throwing off any IDS
  • Retrieves the Address Resolution Protocol (ARP) table to determine if machines ARP cache is poisened
  • Can spoof an entry in the ARP Table
  • Does all of this asynchronously by a specified throttle limit for concurrent scans

In this “mini-module” I have 5 main functions, each will have a separate blog post with special details specific to the function

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

Time for some code!

Note: In order for the below code to work, you must have the module imported

Get-SecNetMap

[sourcecode language=”powershell” wraplines=”false” collapse=”false”]
Function Get-SecNetMap {
<#
.SYNOPSIS
Asnyconously scans a network range or single target to determine active network clients

.DESCRIPTION
Utilizes asyncronous runspaces to perform high performance network scan of devices on a given network. Also has built in
IDS counter-measures to allow for less detectable scanning.

.PARAMETER IP
An IP address or group of IP addresses to run the commands against. Can use aliases of ‘ComputerName’ or ‘Server’

.PARAMETER Throttle
Number of asynchonous jobs that will run concurrently. Default is set to 50

.PARAMETER Timeout
Wait time before creating another job. Rule of thumb: Faster you scan, more detectable you become.

.NOTES
Name: Get-SecNetMap.ps1
Author: SecureKomodo
Version: 1.0
#>

[Cmdletbinding()]
Param (
[parameter(ValueFromPipeline = $True)]
[System.Net.IPAddress]$IPAddress,

[parameter(Mandatory=$False)]
[String]$Hostname,

[Parameter(Mandatory=$False)]
[ValidateSet(‘Paranoid’,’Sneaky’,’Polite’,’Normal’,’Aggressive’,’Insane’)]
[String]$Timeout,

[Parameter(Mandatory=$False)]
[Int]$Throttle=50,

# Switch to Scan Range (-sR)
[Parameter(Mandatory=$False, ParameterSetName=”ScanRange”)]
[Switch]$sR,

# Manditory minimum ip (-minIP) if (-sR) is specified
[Parameter(Mandatory=$True, ParameterSetName=”ScanRange”)]
[System.Net.IPAddress]$minIP,

# Manditory maximum ip (-maxIP) if (-sR) is specified
[Parameter(Mandatory=$True, ParameterSetName=”ScanRange”)]
[System.Net.IPAddress]$maxIP,

# Switch to Scan Entire Specified Subnet (-sSN)
[Parameter(Mandatory=$False,ParameterSetName=”ScanSubnet”)]
[Switch]$sSN,

# Manditory Subnet Mask (-SubnetMask) if (-sSN) is specified
[Parameter(Mandatory=$False, ParameterSetName=”ScanSubnet”)]
[System.Net.IPAddress]$SubnetMask,

# Switch to Randomize Targets (-Randomize)
[Parameter(Mandatory=$False)]
[Switch]$Randomize,

# Switch to scan for Open ports
[Parameter(Mandatory=$False)]
[Switch]$sP,

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

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

# Is Verbose Specified?
If ($PSCmdlet.MyInvocation.BoundParameters[“Verbose”].IsPresent) {
$Verb=$True
Write-Verbose “Verbose Output Specified”
Write-Verbose “Randomize: $Randomize”
Write-Verbose “SubnetMask: $SubnetMask”
Write-Verbose “Throttle: $Throttle”
Write-Verbose “Timeout: $Timeout”
}

# Declaring Main Array
$Main=@()

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

$TargetRange=@()

# Get localhost info if IP not specified
if ((!$IPAddress) -and (!$Hostname)) {
$IPConfig = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $env:COMPUTERNAME | Where-Object {$_.IPEnabled} | Select IPAddress,IPSubnet
if ($IPConfig) {
[System.Net.IPAddress]$IPAddress=$IPConfig.IPAddress[0]
[System.Net.IPAddress]$SubnetMask=$IPConfig.IPSubnet[0]
} else {Write-Warning “You are not connected to any Networks”}
}

# Get IP info through DNS if Host specified
if ((!$IPAddress) -and $Hostname) {
$IPAddress=([System.Net.Dns]::GetHostAddresses($Hostname)).IPAddressToString | Out-Null
#If DNS fails then try to test if hostname and if not breaks out of script
if (!$IPAddress){
$IPAddress = (Test-Connection -ComputerName $Hostname -Count 1).IPV4Address.IPAddressToString
if (!$IPAddress){Break}
}
}

# Timeout between scan (ms). I know it is different from nmap. Deal with it.
if ($Timeout) {
if ($Timeout -eq ‘Paranoid’) {[int]$T=300000}
elseif ($Timeout -eq ‘Sneaky’) {[int]$T=15000}
elseif ($Timeout -eq ‘Polite’) {[int]$T=7500}
elseif ($Timeout -eq ‘Normal’) {[int]$T=1000}
elseif ($Timeout -eq ‘Aggressive’) {[int]$T=500}
elseif ($Timeout -eq ‘Insane’) {[int]$T=0}
} else {[int]$T=1000; } #Default if not specified

# 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,$Verb)

# Creates Object for Ping
$PObject = New-Object System.Net.NetworkInformation.Ping

# Sends Ping (ICMP) and stores variable
$Ping=$PObject.Send($IP,1,8)

# Show Current IP (If Specified)
if($Verb){Write-Verbose (“Scanning $IP”)}

# This will be placed into the Main array as [0] and [1] in the ‘Do loop’ later
Return $IP,$Ping.Status

} # End Script

} # End Begin

Process {

if ($sSN) {

Write-Verbose “Calculate NetworkAddress and BroadCastAddress”
# Calculate NetworkAddress and BroadCastAddress
[System.Net.IPAddress]$NetworkAddress = ($SubnetMask.Address -bAnd $IPAddress.Address)
[System.Net.IPAddress]$BroadCastAddress = ($SubnetMask.Address -bXor ([System.Net.IPAddress]::Broadcast).Address -bOr $NetworkAddress.Address)

# Shuffles the IP target range Only if specified so scan is not incremental. This is to assist with keeping the scan undetected
if ($Randomize){
Write-Verbose “Randomizing IP Addresses…”
$ToShuffle=@()

#Note: In order to Randomize properly, the IP address had to be converted into an array of integers, then those integers shuffled, and then converted back to IP addresses.

Write-Verbose “Calculate Minimum and Maximum Int64 representations of Addresses from randomization”
# Calculate Minimum and Maximum Int64 representations of Addresses
[System.Int64]$RangeMin = Convert-SecIPAddress -toINT $NetworkAddress
[System.Int64]$RangeMax = Convert-SecIPAddress -toINT $BroadCastAddress

Write-Verbose “INT Min: $RangeMin”
Write-Verbose “INT Max: $RangeMax”

# Loop to store range of integers (calculated from IP addresses) into array to be shuffled
[long]$i=$RangeMin
do {$i++;
$ToShuffle+=$i
} while ($i -lt [long]$RangeMax)

# Randomize
$Shuffled = Get-Random -Count ([int]::MaxValue) -InputObject ($ToShuffle)

# Convert back to IP
$Shuffled | ForEach-Object {
$TargetRange+= (Convert-SecIPAddress -fromINT $_)
}

Write-Verbose “Randomize Complete.”

} else {

$TargetRange = Get-SecIPRange -minIP $NetworkAddress -maxIP $BroadCastAddress

} # End If/Else Random

} # End if ScanSubnet specified
elseif($sR){

$TargetRange = Get-SecIPRange -minIP $minIP -maxIP $maxIP

} # End elseif Range specified
else {$TargetRange = $IPAddress} #End if sR or sSN

$Count = 0
# Start loop to create Runspaces
foreach ($IP in $TargetRange) {
$Count++
Write-Verbose “Starting to create runspaces”
if (!($Silent)){Write-Progress -Id 1 -Activity “Scanning…” -status $IP -PercentComplete (($Count/$TargetRange.Count)*100)}

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

# 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
IP=$null
} # End Temporary Array

$TempArray.IP = $IP
$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]
Status=($Runspace.Powershell.EndInvoke($Runspace.Runspace))[1]
} # 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
$HostsUP = $Main.Status | Where-Object {$_ -eq “Success”} | Group-Object
$NotShown = $Main.Status | Where-Object {$_ -eq “TimedOut”} | Group-Object

if ($NotShown) {Write-Output (“Not Shown: ” + $NotShown.Count + ” offline hosts”) }
if ($HostsUP) {$Main | Where-Object {$_.Status -eq “Success”} | Format-Table -AutoSize}

Write-Output (“`nSecNetMap done: ” + $TargetRange.Count + ” total hosts ( ” + ($HostsUp.Count) + ” hosts up ) ” + (“scanned in {0:N2}” -f $TimeSpan) + ” seconds”)

# Scans for ports if the port switch was specified. This code will likely go elsewhere at somepoint since it is not ideal to have it in the End statement.
$Count=0
if ($sP) {
foreach ($h in ($Main | Where-Object {$_.Status -eq “Success”})) {
$Count++
Write-Verbose “Starting to scan ports”
if (!($Silent)){Write-Progress -Id 1 -Activity “Port Scan in Progress…” -status $h.IP -PercentComplete (($Count/$HostsUp.Count)*100)}

Get-SecPortScan -IP $h.IP -Throttle $Throttle

} # End foreach active host

} # End if Portscan specified

} #End END

} # End Get-SecNetScan

[/sourcecode]

I hope that you will get as much enjoyment from

Tags// ,