function Invoke-Portscan { <# .SYNOPSIS Simple portscan module PowerSploit Function: Invoke-Portscan Author: Rich Lundeen (http://webstersProdigy.net) License: BSD 3-Clause Required Dependencies: None Optional Dependencies: None .DESCRIPTION Does a simple port scan using regular sockets, based (pretty) loosely on nmap .PARAMETER Hosts Include these comma seperated hosts (supports IPv4 CIDR notation) or pipe them in .PARAMETER HostFile Input hosts from file rather than commandline .PARAMETER ExcludeHosts Exclude these comma seperated hosts .PARAMETER Ports Include these comma seperated ports (can also be a range like 80-90) .PARAMETER PortFile Input ports from a file .PARAMETER TopPorts Include the x top ports - only goes to 1000, default is top 50 .PARAMETER ExcludedPorts Exclude these comma seperated ports .PARAMETER SkipDiscovery Treat all hosts as online, skip host discovery .PARAMETER PingOnly Ping scan only (disable port scan) .PARAMETER DiscoveryPorts Comma separated ports used for host discovery. -1 is a ping .PARAMETER Threads number of max threads for the thread pool (per host) .PARAMETER nHosts number of hosts to concurrently scan .PARAMETER Timeout Timeout time on a connection in miliseconds before port is declared filtered .PARAMETER SleepTimer Wait before thread checking, in miliseconds .PARAMETER SyncFreq How often (in terms of hosts) to sync threads and flush output .PARAMETER T [0-5] shortcut performance options. Default is 3. higher is more aggressive. Sets (nhosts, threads,timeout) 5 {$nHosts=30; $Threads = 1000; $Timeout = 750 } 4 {$nHosts=25; $Threads = 1000; $Timeout = 1200 } 3 {$nHosts=20; $Threads = 100; $Timeout = 2500 } 2 {$nHosts=15; $Threads = 32; $Timeout = 3000 } 1 {$nHosts=10; $Threads = 32; $Timeout = 5000 } .PARAMETER GrepOut Greppable output file .PARAMETER XmlOut output XML file .PARAMETER ReadableOut output file in 'readable' format .PARAMETER AllformatsOut output in readable (.nmap), xml (.xml), and greppable (.gnmap) formats .PARAMETER noProgressMeter Suppresses the progress meter .PARAMETER quiet supresses returned output and don't store hosts in memory - useful for very large scans .PARAMETER ForceOverwrite Force Overwrite if output Files exist. Otherwise it throws exception .EXAMPLE Invoke-Portscan -Hosts "webstersprodigy.net,google.com,microsoft.com" -TopPorts 50 Description ----------- Scans the top 50 ports for hosts found for webstersprodigy.net,google.com, and microsoft.com .EXAMPLE echo webstersprodigy.net | Invoke-Portscan -oG test.gnmap -f -ports "80,443,8080" Description ----------- Does a portscan of "webstersprodigy.net", and writes a greppable output file .EXAMPLE Invoke-Portscan -Hosts 192.168.1.1/24 -T 4 -TopPorts 25 -oA localnet Description ----------- Scans the top 20 ports for hosts found in the 192.168.1.1/24 range, outputs all file formats .LINK http://webstersprodigy.net #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseApprovedVerbs', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseLiteralInitializerForHashtable', '')] [CmdletBinding()] Param ( #Host, Ports [Parameter(ParameterSetName="cmdHosts", ValueFromPipeline=$True, Mandatory = $True)] [String[]] $Hosts, [Parameter(ParameterSetName="fHosts", Mandatory = $True)] [Alias("iL")] [String] $HostFile, [Parameter(Mandatory = $False)] [Alias("exclude")] [String] $ExcludeHosts, [Parameter(Mandatory = $False)] [Alias("p")] [String] $Ports, [Parameter(Mandatory = $False)] [Alias("iP")] [String] $PortFile, [Parameter(Mandatory = $False)] [String] $TopPorts, [Parameter(Mandatory = $False)] [Alias("xPorts")] [String] $ExcludedPorts, #Host Discovery [Parameter(Mandatory = $False)] [Alias("Pn")] [Switch] $SkipDiscovery, [Parameter(Mandatory = $False)] [Alias("sn")] [Switch] $PingOnly, [Parameter(Mandatory = $False)] [Alias("PS")] [string] $DiscoveryPorts = "-1,445,80,443", #Timing and Performance [Parameter(Mandatory = $False)] [int] $Threads = 100, [Parameter(Mandatory = $False)] [int] $nHosts = 25, [Parameter(Mandatory = $False)] [int] $Timeout = 2000, [Parameter(Mandatory = $False)] [int] $SleepTimer = 500, [Parameter(Mandatory = $False)] [int] $SyncFreq = 1024, [Parameter(Mandatory = $False)] [int] $T, #Output [Parameter(Mandatory = $False)] [Alias("oG")] [String] $GrepOut, [Parameter(Mandatory = $False)] [Alias("oX")] [String] $XmlOut, [Parameter(Mandatory = $False)] [Alias("oN")] [String] $ReadableOut, [Parameter(Mandatory = $False)] [Alias("oA")] [String] $AllformatsOut, [Parameter(Mandatory = $False)] [Switch] $noProgressMeter, [Parameter(Mandatory = $False)] [Alias("q")] [Switch] $quiet, [Parameter(Mandatory = $False)] [Alias("F")] [Switch] $ForceOverwrite #TODO add script parameter #TODO add resume parameter ) PROCESS { Set-StrictMode -Version 2.0 $version = .13 $hostList = New-Object System.Collections.ArrayList $portList = New-Object System.Collections.ArrayList $hostPortList = New-Object System.Collections.ArrayList $scannedHostList = @() function Parse-Hosts { Param ( [Parameter(Mandatory = $True)] [String] $Hosts ) [String[]] $iHosts = $Hosts.Split(",") $IPRangeRegex = "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}-\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" foreach($iHost in $iHosts) { $iHost = $iHost.Replace(" ", "") if(!$iHost) { continue } if($iHost.contains("/")) { $netPart = $iHost.split("/")[0] [uint32]$maskPart = $iHost.split("/")[1] $address = [System.Net.IPAddress]::Parse($netPart) if ($maskPart -ge $address.GetAddressBytes().Length * 8) { throw "Bad host mask" } $numhosts = [System.math]::Pow(2,(($address.GetAddressBytes().Length *8) - $maskPart)) $startaddress = $address.GetAddressBytes() [array]::Reverse($startaddress) $startaddress = [System.BitConverter]::ToUInt32($startaddress, 0) [uint32]$startMask = ([System.math]::Pow(2, $maskPart)-1) * ([System.Math]::Pow(2,(32 - $maskPart))) $startAddress = $startAddress -band $startMask #in powershell 2.0 there are 4 0 bytes padded, so the [0..3] is necessary $startAddress = [System.BitConverter]::GetBytes($startaddress)[0..3] [array]::Reverse($startaddress) $address = [System.Net.IPAddress] [byte[]] $startAddress $hostList.Add($address.IPAddressToString) for ($i=0; $i -lt $numhosts-1; $i++) { $nextAddress = $address.GetAddressBytes() [array]::Reverse($nextAddress) $nextAddress = [System.BitConverter]::ToUInt32($nextAddress, 0) $nextAddress ++ $nextAddress = [System.BitConverter]::GetBytes($nextAddress)[0..3] [array]::Reverse($nextAddress) $address = [System.Net.IPAddress] [byte[]] $nextAddress $hostList.Add($address.IPAddressToString) } } if($iHost -match $IPRangeRegex) { $iHostPart1 = ($iHost.Split("-"))[0] $iHostPart2 = ($iHost.Split("-"))[1] $LowerBound = $iHostPart1.Split(".") $UpperBound = $iHostPart2.Split(".") $LowerBoundInt = ($LowerBound[0].ToInt32($null),$LowerBound[1].ToInt32($null),$LowerBound[2].ToInt32($null),$LowerBound[3].ToInt32($null)) $UpperBoundInt = ($UpperBound[0].ToInt32($null),$UpperBound[1].ToInt32($null),$UpperBound[2].ToInt32($null),$UpperBound[3].ToInt32($null)) $CurrentIP = $LowerBoundInt $CurrentIPString = $null $ControlArray = @(0,0,0,0) $null = $hostList.Add($iHostPart1) while($CurrentIPString -ne $iHostPart2) { for($i=0;$i -lt 4;$i++) { if(($CurrentIP[$i] -eq $UpperBoundInt[$i]) -and (($i -eq 0) -or $ControlArray[$i-1] -eq 1)) { $ControlArray[$i] = 1 continue } else { $Max = 254 if(($i -ne 0) -and ($ControlArray[$i-1] -eq 1)) { $Max = $UpperBoundInt[$i] } if(($i -ne 3) -and ($CurrentIP[$i+1] -eq 254)) { $CurrentIP[$i]++ $CurrentIP[$i+1]=0 $CurrentIPString = ($CurrentIP[0].ToString() + "." + $CurrentIP[1].ToString() + "." + $CurrentIP[2].ToString() + "." + $CurrentIP[3].ToString()) $null = $hostList.Add($CurrentIPString) } if(($i -eq 3) -and ($CurrentIP[$i] -lt $Max)) { $CurrentIP[$i]++ $CurrentIPString = ($CurrentIP[0].ToString() + "." + $CurrentIP[1].ToString() + "." + $CurrentIP[2].ToString() + "." + $CurrentIP[3].ToString()) $null = $hostList.Add($CurrentIPString) } } } } } else { $hostList.Add($iHost) } } } function Parse-ILHosts { Param ( [Parameter(Mandatory = $True)] [String] $HostFile ) Get-Content $HostFile | ForEach-Object { Parse-Hosts $_ } } function Exclude-Hosts { Param ( [Parameter(Mandatory = $True)] [String] $excludeHosts ) [String[]] $iHosts = $excludeHosts.Split(",") foreach($iHost in $iHosts) { $iHost = $iHost.Replace(" ", "") $hostList.Remove($iHost) } } function Get-TopPort { Param ( [Parameter(Mandatory = $True)] [ValidateRange(1,1000)] [int] $numPorts ) #list of top 1000 ports from nmap from Jun 2013 [int[]] $topPortList = @(80,23,443,21,3389,110,445,139,143,53,135,3306,8080,22 1723,111,995,993,5900,1025,1720,548,113,81,6001,179,1026,2000,8443, 8000,32768,554,26,1433,49152,2001,515,8008,49154,1027,5666,646,5000, 5631,631,49153,8081,2049,88,79,5800,106,2121,1110,49155,6000,513, 990,5357,49156,543,544,5101,144,7,389,8009,9999,5009,7070,5190,3000, 5432,1900,3986,13,1029,9,5051,6646,49157,1028,873,1755,2717,4899,9100, 119,37,1000,3001,5001,82,10010,1030,9090,2107,1024,2103,6004,1801, 5050,19,8031,1041,255,1048,1049,1053,1054,1056,1064,3703,17,808,3689, 1031,1044,1071,5901,100,9102,2869,4001,5120,8010,9000,2105,636,1038, 2601,1,7000,1066,1069,625,311,280,254,4000,1761,5003,2002,1998,2005, 1032,1050,6112,1521,2161,6002,2401,902,4045,787,7937,1058,2383,1033, 1040,1059,50000,5555,1494,3,593,2301,3268,7938,1022,1234,1035,1036,1037, 1074,8002,9001,464,497,1935,2003,6666,6543,24,1352,3269,1111,407,500, 20,2006,1034,1218,3260,15000,4444,264,33,2004,1042,42510,999,3052,1023, 222,1068,888,7100,1717,992,2008,7001,2007,8082,512,1043,2009,5801,1700, 7019,50001,4662,2065,42,2602,3333,9535,5100,2604,4002,5002,1047,1051,1052, 1055,1060,1062,1311,3283,4443,5225,5226,6059,6789,8089,8651,8652,8701,9415, 9593,9594,9595,16992,16993,20828,23502,32769,33354,35500,52869,55555,55600, 64623,64680,65000,65389,1067,13782,366,5902,9050,85,1002,5500,1863,1864, 5431,8085,10243,45100,49999,51103,49,90,6667,1503,6881,27000,340,1500,8021, 2222,5566,8088,8899,9071,5102,6005,9101,163,5679,146,648,1666,83,3476,5004, 5214,8001,8083,8084,9207,14238,30,912,12345,2030,2605,6,541,4,1248,3005,8007, 306,880,2500,1086,1088,2525,4242,8291,9009,52822,900,6101,2809,7200,211,800, 987,1083,12000,705,711,20005,6969,13783,1045,1046,1061,1063,1070,1072,1073, 1075,1077,1078,1079,1081,1082,1085,1093,1094,1096,1098,1099,1100,1104,1106, 1107,1108,1148,1169,1272,1310,1687,1718,1783,1840,2100,2119,2135,2144,2160, 2190,2260,2381,2399,2492,2607,2718,2811,2875,3017,3031,3071,3211,3300,3301, 3323,3325,3351,3404,3551,3580,3659,3766,3784,3801,3827,3998,4003,4126,4129, 4449,5222,5269,5633,5718,5810,5825,5877,5910,5911,5925,5959,5960,5961,5962, 5987,5988,5989,6123,6129,6156,6389,6580,6901,7106,7625,7777,7778,7911,8086, 8181,8222,8333,8400,8402,8600,8649,8873,8994,9002,9011,9080,9220,9290,9485, 9500,9502,9503,9618,9900,9968,10002,10012,10024,10025,10566,10616,10617,10621, 10626,10628,10629,11110,13456,14442,15002,15003,15660,16001,16016,16018,17988, 19101,19801,19842,20000,20031,20221,20222,21571,22939,24800,25734,27715,28201, 30000,30718,31038,32781,32782,33899,34571,34572,34573,40193,48080,49158,49159, 49160,50003,50006,50800,57294,58080,60020,63331,65129,691,212,1001,1999,2020, 2998,6003,7002,50002,32,2033,3372,99,425,749,5903,43,458,5405,6106,6502,7007, 13722,1087,1089,1124,1152,1183,1186,1247,1296,1334,1580,1782,2126,2179,2191,2251, 2522,3011,3030,3077,3261,3493,3546,3737,3828,3871,3880,3918,3995,4006,4111,4446, 5054,5200,5280,5298,5822,5859,5904,5915,5922,5963,7103,7402,7435,7443,7512,8011, 8090,8100,8180,8254,8500,8654,9091,9110,9666,9877,9943,9944,9998,10004,10778,15742, 16012,18988,19283,19315,19780,24444,27352,27353,27355,32784,49163,49165,49175, 50389,50636,51493,55055,56738,61532,61900,62078,1021,9040,666,700,84,545,1112, 1524,2040,4321,5802,38292,49400,1084,1600,2048,2111,3006,6547,6699,9111,16080, 555,667,720,801,1443,1533,2106,5560,6007,1090,1091,1114,1117,1119,1122,1131,1138, 1151,1175,1199,1201,1271,1862,2323,2393,2394,2608,2725,2909,3003,3168,3221,3322, 3324,3390,3517,3527,3800,3809,3814,3826,3869,3878,3889,3905,3914,3920,3945,3971, 4004,4005,4279,4445,4550,4567,4848,4900,5033,5080,5087,5221,5440,5544,5678,5730, 5811,5815,5850,5862,5906,5907,5950,5952,6025,6510,6565,6567,6689,6692,6779,6792, 6839,7025,7496,7676,7800,7920,7921,7999,8022,8042,8045,8093,8099,8200,8290,8292, 8300,8383,9003,9081,9099,9200,9418,9575,9878,9898,9917,10003,10180,10215,11111, 12174,12265,14441,15004,16000,16113,17877,18040,18101,19350,25735,26214,27356, 30951,32783,32785,40911,41511,44176,44501,49161,49167,49176,50300,50500,52673, 52848,54045,54328,55056,56737,57797,60443,70,417,714,722,777,981,1009,2022,4224, 4998,6346,301,524,668,765,2041,5999,10082,259,1007,1417,1434,1984,2038,2068,4343, 6009,7004,44443,109,687,726,911,1461,2035,4125,6006,7201,9103,125,481,683,903, 1011,1455,2013,2043,2047,6668,6669,256,406,843,2042,2045,5998,9929,31337,44442, 1092,1095,1102,1105,1113,1121,1123,1126,1130,1132,1137,1141,1145,1147,1149,1154, 1164,1165,1166,1174,1185,1187,1192,1198,1213,1216,1217,1233,1236,1244,1259,1277, 1287,1300,1301,1309,1322,1328,1556,1641,1688,1719,1721,1805,1812,1839,1875,1914, 1971,1972,1974,2099,2170,2196,2200,2288,2366,2382,2557,2800,2910,2920,2968,3007, 3013,3050,3119,3304,3307,3376,3400,3410,3514,3684,3697,3700,3824,3846,3848,3859, 3863,3870,3872,3888,3907,3916,3931,3941,3957,3963,3968,3969,3972,3990,3993,3994, 4009,4040,4080,4096,4143,4147,4200,4252,4430,4555,4600,4658,4875,4949,5040,5063, 5074,5151,5212,5223,5242,5279,5339,5353,5501,5807,5812,5818,5823,5868,5869,5899, 5905,5909,5914,5918,5938,5940,5968,5981,6051,6060,6068,6203,6247,6500,6504,6520, 6550,6600) $numPorts-- $portList.AddRange($topPortList[0..$numPorts]) } function Parse-Ports { Param ( [Parameter(Mandatory = $True)] [String] $Ports, [Parameter(Mandatory = $True)] $pList ) foreach ($pRange in $Ports.Split(",")) { #-1 is a special case for ping if ($pRange -eq "-1") { $pList.Add([int]$pRange) } elseif ($pRange.Contains("-")) { [int[]] $range = $pRange.Split("-") if ($range.Count -ne 2 -or $pRange.Split("-")[0] -eq "" -or $pRange.split("-")[1] -eq "") { throw "Invalid port range" } $pList.AddRange($range[0]..$range[1]) } else { $pList.Add([int]$pRange) } } foreach ($p in $pList) { if ($p -lt -1 -or $p -gt 65535) { throw "Port $p out of range" } } } function Parse-IpPorts { Param ( [Parameter(Mandatory = $True)] [String] $PortFile ) Get-Content $PortFile | ForEach-Object { Parse-Ports -Ports $_ -pList $portList } } function Remove-Ports { Param ( [Parameter(Mandatory = $True)] [string] $ExcludedPorts ) [int[]] $ExcludedPorts = $ExcludedPorts.Split(",") foreach ($x in $ExcludedPorts) { $portList.Remove($x) } } function Write-PortscanOut { Param ( [Parameter(Mandatory = $True, ParameterSetName="Comment")] [string] $comment, [Parameter(Mandatory = $True, ParameterSetName="HostOut")] [string] $outhost, [Parameter(Mandatory = $True, ParameterSetName="HostOut")] [bool] $isUp, [Parameter(Mandatory = $True, ParameterSetName="HostOut")] $openPorts, [Parameter(Mandatory = $True, ParameterSetName="HostOut")] $closedPorts, [Parameter(Mandatory = $True, ParameterSetName="HostOut")] $filteredPorts, [Parameter()] [bool] $SkipDiscovery, [Parameter()] [System.IO.StreamWriter] $grepStream, [Parameter()] [System.Xml.XmlWriter] $xmlStream, [Parameter()] [System.IO.StreamWriter] $readableStream ) switch ($PSCmdlet.ParameterSetName) { "Comment" { Write-Verbose $comment if ($grepStream) { $grepStream.WriteLine("# " + $comment) } if ($xmlStream) { $xmlStream.WriteComment($comment) } if ($readableStream) { $readableStream.WriteLine($comment) } } "HostOut" { $oPort = [string]::join(",", $openPorts.ToArray()) $cPort = [string]::join(",", $closedPorts.ToArray()) $fPort = [string]::join(",", $filteredPorts.ToArray()) if ($grepStream) { #for grepstream use tabs - can be ugly, but easier for regex if ($isUp -and !$SkipDiscovery) { $grepStream.writeline("Host: $outhost`tStatus: Up") } if ($isUp -or $SkipDiscovery) { if ($oPort -ne "") { $grepStream.writeline("Host: $outhost`tOpen Ports: $oPort") } if ($cPort -ne "") { $grepStream.writeline("Host: $outhost`tClosed Ports: $cPort") } if ($fPort -ne "") { $grepStream.writeline("Host: $outhost`tFiltered Ports: $fPort") } } elseif (!$SkipDiscovery) { $grepStream.writeline("Host: $outhost`tStatus: Down") } } if ($xmlStream) { $xmlStream.WriteStartElement("Host") $xmlStream.WriteAttributeString("id", $outhost) if (!$SkipDiscovery) { if ($isUp) { $xmlStream.WriteAttributeString("Status", "Up") } else { $xmlStream.WriteAttributeString("Status", "Downs") } } $xmlStream.WriteStartElement("Ports") foreach($p in $openPorts) { $xmlStream.writestartElement("Port") $xmlStream.WriteAttributeString("id", [string]$p) $xmlStream.WriteAttributeString("state", "open") $xmlStream.WriteEndElement() } foreach ($p in $closedPorts) { $xmlStream.writestartElement("Port") $xmlStream.WriteAttributeString("id", [string]$p) $xmlStream.WriteAttributeString("state", "closed") $xmlStream.WriteEndElement() } foreach ($p in $filteredPorts) { $xmlStream.writestartElement("Port") $xmlStream.WriteAttributeString("id", [string]$p) $xmlStream.WriteAttributeString("state", "filtered") $xmlStream.WriteEndElement() } $xmlStream.WriteEndElement() $xmlStream.WriteEndElement() } if ($readableStream) { $readableStream.writeline("Porscan.ps1 scan report for $outhost") if ($isUp) { $readableStream.writeline("Host is up") } if ($isUp -or $SkipDiscovery) { $readableStream.writeline(("{0,-10}{1,0}" -f "PORT", "STATE")) [int[]]$allports = $openPorts + $closedPorts + $filteredPorts foreach($p in ($allports| Sort-Object)) { if ($openPorts.Contains($p)) { $readableStream.writeline(("{0,-10}{1,0}" -f $p, "open")) } elseif ($closedPorts.Contains($p)) { $readableStream.writeline(("{0,-10}{1,0}" -f $p, "closed")) } elseif ($filteredPorts.Contains($p)) { $readableStream.writeline(("{0,-10}{1,0}" -f $p, "filtered")) } } } elseif(!$SkipDiscovery) { $readableStream.writeline("Host is Down") } $readableStream.writeline("") } } } } #function for Powershell v2.0 to work function Convert-SwitchtoBool { Param ( [Parameter(Mandatory = $True)] $switchValue ) If ($switchValue) { return $True } return $False } try { [bool] $SkipDiscovery = Convert-SwitchtoBool ($SkipDiscovery) [bool] $PingOnly = Convert-SwitchtoBool ($PingOnly) [bool] $quiet = Convert-SwitchtoBool ($quiet) [bool] $ForceOverwrite = Convert-SwitchtoBool ($ForceOverwrite) ######### #parse arguments ######### [Environment]::CurrentDirectory=(Get-Location -PSProvider FileSystem).ProviderPath if ($PsCmdlet.ParameterSetName -eq "cmdHosts") { foreach($h in $Hosts) { Parse-Hosts($h) | Out-Null } } else { Parse-ILHosts($HostFile) | Out-Null } if($ExcludeHosts) { Exclude-Hosts($ExcludeHosts) } if (($TopPorts -and $Ports) -or ($TopPorts -and $PortFile)) { throw "Cannot set topPorts with other specific ports" } if($Ports) { Parse-Ports -Ports $Ports -pList $portList | Out-Null } if($PortFile) { Parse-IpPorts($PortFile) | Out-Null } if($portList.Count -eq 0) { if ($TopPorts) { Get-TopPort($TopPorts) | Out-Null } else { #if the ports still aren't set, give the deftault, top 50 ports Get-TopPort(50) | Out-Null } } if ($ExcludedPorts) { Remove-Ports -ExcludedPorts $ExcludedPorts | Out-Null } if($T) { switch ($T) { 5 {$nHosts=30; $Threads = 1000; $Timeout = 750 } 4 {$nHosts=25; $Threads = 1000; $Timeout = 1200 } 3 {$nHosts=20; $Threads = 100; $Timeout = 2500 } 2 {$nHosts=15; $Threads = 32; $Timeout = 3000 } 1 {$nHosts=10; $Threads = 32; $Timeout = 5000 } default { throw "Invalid T parameter" } } } $grepStream = $null $xmlStream = $null $readableStream = $null if($AllformatsOut) { if ($GrepOut -or $XmlOut -or $ReadableOut) { Write-Warning "Both -oA specified with other output... going to ignore -oG/-oN/-oX" } $GrepOut = $AllformatsOut + ".gnmap" $XmlOut = $AllformatsOut + ".xml" $ReadableOut = $AllformatsOut + ".nmap" } if ($GrepOut) { if (!$ForceOverwrite -and (Test-Path $GrepOut)) { throw "Error: $AllformatsOut already exists. Either delete the file or specify the -f flag" } $grepStream = [System.IO.StreamWriter] $GrepOut } if ($ReadableOut) { if (!$ForceOverwrite -and (Test-Path $ReadableOut)) { throw "Error: $ReadableOut already exists. Either delete the file or specify the -f flag" } $readableStream = [System.IO.StreamWriter] $ReadableOut } if ($XmlOut) { if (!$ForceOverwrite -and (Test-Path $XmlOut)) { throw "Error: $XmlOut already exists. Either delete the file or specify the -f flag" } $xmlStream = [System.xml.xmlwriter]::Create([string]$XmlOut) $xmlStream.WriteStartDocument() $xmlStream.WriteStartElement("Portscanrun") $xmlStream.WriteAttributeString("version", $version) } Parse-Ports -Ports $DiscoveryPorts -pList $hostPortList | Out-Null $startdate = Get-Date $myInvocationLine = $PSCmdlet.MyInvocation.Line $startMsg = "Invoke-Portscan.ps1 v$version scan initiated $startdate as: $myInvocationLine" #TODO deal with output Write-PortscanOut -comment $startMsg -grepStream $grepStream -xmlStream $xmlStream -readableStream $readableStream # #converting back from int array gives some argument error checking # $sPortList = [string]::join(",", $portList) # $sHostPortList = [string]::join(",", $hostPortList) ######## #Port Scan Code - run on a per host basis ######## $portScanCode = { param ( [Parameter( Mandatory = $True)] [string] $thost, [Parameter( Mandatory = $True)][bool] $SkipDiscovery, [Parameter( Mandatory = $True)][bool] $PingOnly, [Parameter( Mandatory = $True)][int] $Timeout, [Parameter( Mandatory = $True)] $PortList, [Parameter( Mandatory = $True)] $hostPortList, [Parameter( Mandatory = $True)][int] $maxthreads) Process { $openPorts = New-Object System.Collections.ArrayList $closedPorts = New-Object System.Collections.ArrayList $filteredPorts = New-Object System.Collections.ArrayList $sockets = @{} $timeouts = New-Object Hashtable #set maximum $async threads $fThreads = New-Object int $aThreads = New-Object int [System.Threading.ThreadPool]::GetMaxThreads([ref]$fThreads, [ref]$aThreads) | Out-Null [System.Threading.ThreadPool]::SetMaxThreads($fthreads,$maxthreads) | Out-Null function New-ScriptBlockCallback { param( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [scriptblock]$Callback ) #taken from http://www.nivot.org/blog/post/2009/10/09/PowerShell20AsynchronousCallbacksFromNET if (-not ("CallbackEventBridge" -as [type])) { Add-Type @" using System; public sealed class CallbackEventBridge { public event AsyncCallback CallbackComplete = delegate { }; private CallbackEventBridge() {} private void CallbackInternal(IAsyncResult result) { CallbackComplete(result); } public AsyncCallback Callback { get { return new AsyncCallback(CallbackInternal); } } public static CallbackEventBridge Create() { return new CallbackEventBridge(); } } "@ } $bridge = [CallbackEventBridge]::Create() Register-ObjectEvent -InputObject $bridge -EventName CallbackComplete -Action $Callback | Out-Null $bridge.Callback } function Test-Port { Param ( [Parameter(Mandatory = $True)] [String] $h, [Parameter(Mandatory = $True)] [int] $p, [Parameter(Mandatory = $True)] [int] $timeout ) try { $pAddress = [System.Net.IPAddress]::Parse($h) $sockets[$p] = new-object System.Net.Sockets.TcpClient $pAddress.AddressFamily } catch { #we're assuming this is a host name $sockets[$p] = new-object System.Net.Sockets.TcpClient } $scriptBlockAsString = @" #somewhat of a race condition with the timeout, but I don't think it matters if ( `$sockets[$p] -ne `$NULL) { if (!`$timeouts[$p].Disposed) { `$timeouts[$p].Dispose() } `$status = `$sockets[$p].Connected; if (`$status -eq `$True) { #write-host "$p is open" `$openPorts.Add($p) } else { #write-host "$p is closed" `$closedPorts.Add($p) } `$sockets[$p].Close(); `$sockets.Remove($p) } "@ $timeoutCallback = @" #write-host "$p is filtered" `$sockets[$p].Close() if (!`$timeouts[$p].Disposed) { `$timeouts[$p].Dispose() `$filteredPorts.Add($p) } `$sockets.Remove($p) "@ $timeoutCallback = [scriptblock]::Create($timeoutCallback) $timeouts[$p] = New-Object System.Timers.Timer Register-ObjectEvent -InputObject $timeouts[$p] -EventName Elapsed -Action $timeoutCallback | Out-Null $timeouts[$p].Interval = $timeout $timeouts[$p].Enabled = $true $myscriptblock = [scriptblock]::Create($scriptBlockAsString) $Null = $sockets[$p].beginConnect($h, $p,(New-ScriptBlockCallback($myscriptblock)) , $null) } function PortScan-Alive { Param ( [Parameter(Mandatory = $True)] [String] $h ) Try { #ping if ($hostPortList.Contains(-1)) { $ping = new-object System.Net.NetworkInformation.Ping $pResult = $ping.send($h) if ($pResult.Status -eq "Success") { return $True } } foreach($Port in $hostPortList) { if ($Port -ne -1) { Test-Port -h $h -p $Port -timeout $Timeout } } do { Start-Sleep -Milli 100 if (($openPorts.Count -gt 0) -or ($closedPorts.Count -gt 0)) { return $True } } While ($sockets.Count -gt 0) } Catch { Write-Error "Exception trying to host scan $h" Write-Error $_.Exception.Message; } return $False } function Portscan-Port { Param ( [Parameter(Mandatory = $True)] [String] $h ) [string[]]$Ports = @() foreach($Port in $Portlist) { Try { Test-Port -h $h -p $Port -timeout $Timeout } Catch { Write-Error "Exception trying to scan $h port $Port" Write-Error $_.Exception.Message; } } } [bool] $hostResult = $False if(!$SkipDiscovery) { [bool] $hostResult = PortScan-Alive $thost $openPorts.clear() $closedPorts.clear() $filteredPorts.Clear() } if((!$PingOnly) -and ($hostResult -or $SkipDiscovery)) { Portscan-Port $thost } while ($sockets.Count -gt 0) { Start-Sleep -Milli 500 } return @($hostResult, $openPorts, $closedPorts, $filteredPorts) } } # the outer loop is to flush the loop. # Otherwise Get-Job | Wait-Job could clog, etc [int]$saveIteration = 0 [int]$computersDone=0 [int]$upHosts=0 while (($saveIteration * $SyncFreq) -lt $hostList.Count) { Get-Job | Remove-Job -Force $sIndex = ($saveIteration*$SyncFreq) $eIndex = (($saveIteration+1)*$SyncFreq)-1 foreach ($iHost in $hostList[$sIndex..$eIndex]) { $ctr = @(Get-Job -state Running) while ($ctr.Count -ge $nHosts) { Start-Sleep -Milliseconds $SleepTimer $ctr = @(Get-Job -state Running) } $computersDone++ if(!$noProgressMeter) { Write-Progress -status "Port Scanning" -Activity $startMsg -CurrentOperation "starting computer $computersDone" -PercentComplete ($computersDone / $hostList.Count * 100) } Start-Job -ScriptBlock $portScanCode -Name $iHost -ArgumentList @($iHost, $SkipDiscovery, $PingOnly, $Timeout, $portList, $hostPortList, $Threads) | Out-Null } Get-Job | Wait-Job | Out-Null foreach ($job in Get-Job) { $jobOut = @(Receive-Job $job) [bool]$hostUp = $jobOut[0] $jobName = $job.Name $openPorts = $jobOut[1] $closedPorts = $jobOut[2] $filteredPorts = $jobOut[3] if($hostUp) { $upHosts ++ } if (!$quiet) { $hostDate = Get-Date $hostObj = New-Object System.Object $hostObj | Add-Member -MemberType Noteproperty -Name Hostname -Value $jobName $hostObj | Add-Member -MemberType Noteproperty -Name alive -Value $hostUp $hostObj | Add-Member -MemberType Noteproperty -Name openPorts -Value $openPorts $hostObj | Add-Member -MemberType Noteproperty -Name closedPorts -Value $closedPorts $hostObj | Add-Member -MemberType Noteproperty -Name filteredPorts -Value $filteredPorts $hostObj | Add-Member -MemberType NoteProperty -Name finishTime -Value $hostDate $scannedHostList += $hostobj } Write-PortscanOut -outhost $jobName -isUp $hostUp -openPorts $openPorts -closedPorts $closedPorts -filteredPorts $filteredPorts -grepStream $grepStream -xmlStream $xmlStream -readableStream $readableStream -SkipDiscovery $SkipDiscovery } if ($grepStream) { $grepStream.flush() } if ($xmlStream) { $xmlStream.flush() } if($readableStream) { $readableStream.flush() } $saveIteration ++ } $enddate = Get-Date $totaltime = ($enddate - $startdate).TotalSeconds $endMsg = "Port scan complete at $enddate ($totaltime seconds)" if (!$SkipDiscovery) { $endMsg += ", $upHosts hosts are up" } Write-PortscanOut -comment $endMsg -grepStream $grepStream -xmlStream $xmlStream -readableStream $readableStream if($grepStream) { $grepStream.Close() } if ($xmlStream) { $xmlStream.Close() } if($readableStream) { $readableStream.Close() } return $scannedHostList } Catch { Write-Error $_.Exception.Message; } } }