Public/Invoke-NetworkScan.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#DOIT Replace PSNmap commands with own functions. This is to remove reliance on PSNmap module
#DOIT Take Invoke-PSIPCalc and add it as a native function of the Admintoolbox.Networking module after replacing reliance on PSNmap
function Invoke-NetworkScan {
    <#
    .Description
    Get Hostnames, IPAddresses, Open Ports, Mac Addresses, and Mac Vendors.

    Mac resolution only works on locally attached networks.

    .Parameter CIDR
    A CIDR Range to search. ex: 192.168.0.0/24

    .Parameter DeepScan
    Ports 21, 22, 23, 80, 443, 3389, 9100 are normally scanned. Use DeepScan to get open ports for 21, 22, 23, 25, 53, 67, 80, 139, 389, 443, 445, 902, 3389, 9100.

    .Parameter Threads
    Specify the number of threads that run on the port scan. Default is 64

    .Example
    Get details for all devices on the local subnet and sort by their OUI

    Invoke-NetworkScan -CIDR 192.168.0.0/24 | Sort-Object Vendor

    .Example
    Perform a more thorough port scan and output to gridview

    Invoke-NetworkScan -CIDR 192.168.0.0/24 -DeepScan | Out-GridView

    .Notes
    Does not work in Powershell Core

    .Link
    Invoke-PSipcalc https://www.powershellgallery.com/packages/PSnmap
    Invoke-PSNmap https://www.powershellgallery.com/packages/PSnmap
    #>

    [CmdletBinding()]
    [Alias('scan')]
    param(
        [Parameter (Mandatory = $true, Position = 1)]
        [ValidateScript( {
                if ($_ -match '^[0-9]{1,3}[.]{1}[0-9]{1,3}[.]{1}[0-9]{1,3}[.]{1}[0-9]{1,3}[/]{1}[0-9]{2}$') {
                    $true
                }
                else {
                    throw "$_ is an invalid pattern. CIDR format must be properly written like 192.168.0.1/24"
                }
            })]
        [string]$CIDR,
        [Parameter (Mandatory = $false)]
        [switch]$DeepScan,
        [Parameter (Mandatory = $false)]
        [switch]$SkipMac,
        [Parameter (Mandatory = $false)]
        [string]$Threads = '64'
    )

    begin {
        #Flush the local arp cache for most accurate results
        $ArpRefresh = Remove-ArpCache
        #Perform port scan first and put results in a script scope variable. This will also cause the arp cache to rebuild
        if ($null -ne $ArpRefresh) {
            if ($DeepScan) {
                $Script:SlowScan = Invoke-PSnmap -ComputerName $CIDR -Port 21, 22, 23, 25, 53, 67, 80, 139, 389, 443, 445, 902, 3389, 9100 -ScanOnPingFail -Dns -NoSummary -PortConnectTimeoutMs 500 -ThrottleLimit $Threads
            }
            else {
                $Script:QuickScan = Invoke-PSnmap -ComputerName $CIDR -Port 21, 22, 23, 80, 443, 3389, 9100 -ScanOnPingFail -Dns -NoSummary -PortConnectTimeoutMs 500 -ThrottleLimit $Threads
            }
        }

        else {
            Return
        }

        #Get path to the OUI list
        $ModPath = Get-Module AdminToolbox.Networking
        $P1 = $ModPath.path
        $P2 = $p1 -replace ("AdminToolbox.Networking.psm1", "")
        $Script:ScanOUI = "$P2" + "support\ScanOUI.txt"
    }

    process {
        #Start a Foreach loop against CIDR Addresses
        $IPList = (Invoke-PSipcalc -NetworkAddress $CIDR -Enumerate).IPEnumerated

        ForEach ($IP in $IPList) {
            #Get MAC Address if it exists
            try {
                $Script:Neighbor = Get-NetNeighbor $IP -ErrorAction 'stop' | Where-Object { ($_.LinkLayerAddress -ne '00-00-00-00-00-00') -and ($_.LinkLayerAddress -ne 'ff-ff-ff-ff-ff-ff') }
            }
            catch [Microsoft.PowerShell.Cmdletization.Cim.CimJobException] {
                $Script:Neighbor = $null
            }
            catch [System.Management.Automation.RemoteException] {
                $Script:Neighbor = $null
            }

            $MAC = $Script:Neighbor.LinkLayerAddress

            #If block runs against scan results where Mac Addresses are present
            if ($null -ne $MAC -and $SkipMac -eq $false) {
                #If Deepscan parameter is chosen
                if ($DeepScan) {
                    #Filter the port scan to return results for the currently enumerated subnet from where the network scan returned live network devices
                    $Scan = $Script:SlowScan | Where-Object { $_.Computername -eq $IP }
                    $Mac2 = $Mac.substring(0, 8)
                    #Invoke Mac Lookup against a text file in the support folder of this module
                    $OUI1 = Get-Content $Script:ScanOUI | Where-Object { $_ -like "$mac2*" }
                    #Sometimes the MAC won't be Null, but it's not an actual MAC address. This try catch was quicker to implement then troubleshooting the cause.
                    try {
                        $OUI2 = $OUI1.Substring(9)
                    }
                    catch {
                        $OUI2 = $null
                    }

                    ForEach-Object {
                        #Take the enumerated CIDR IP results from the API call and Port scan. Output using a PSCustomObject
                        [pscustomobject]@{
                            IP                 = $IP
                            Hostname           = $Scan.'IP/DNS'
                            MacAddress         = $MAC
                            Vendor             = $OUI2
                            Ping               = $Scan.Ping
                            'ssh(22)'          = $Scan.'Port 22'
                            'telnet(23)'       = $Scan.'Port 23'
                            'smtp(25)'         = $Scan.'Port 25'
                            'dns(53)'          = $Scan.'Port 53'
                            'dhcp(67)'         = $Scan.'Port 67'
                            'http(80)'         = $Scan.'Port 80'
                            'smb-netbios(139)' = $Scan.'Port 139'
                            'ldap(389)'        = $Scan.'Port 389'
                            'https(443)'       = $Scan.'Port 443'
                            'smb-cifs(445)'    = $Scan.'Port 445'
                            'vmware(902)'      = $Scan.'Port 902'
                            'rdp(3389)'        = $Scan.'Port 3389'
                            'print(9100)'      = $Scan.'Port 9100'
                        }
                    } | Where-Object { ($null -ne $scan.'IP/DNS') -or ($null -ne $MAC) -or ($Scan.Ping -eq $true) -or ($Scan.'Port 22' -eq $true) -or ($Scan.'Port 23' -eq $true) -or ($Scan.'Port 25' -eq $true) -or ($Scan.'Port 53' -eq $true) -or ($Scan.'Port 67' -eq $true) -or ($Scan.'Port 80' -eq $true) -or ($Scan.'Port 139' -eq $true) -or ($Scan.'Port 389' -eq $true) -or ($Scan.'Port 443' -eq $true) -or ($Scan.'Port 445' -eq $true) -or ($Scan.'Port 902' -eq $true) -or ($Scan.'Port 3389' -eq $true) -or ($Scan.'Port 9100' -eq $true) }
                }
                #If Deepscan parameter isn't chosen
                else {
                    #Filter the port scan to return results for the currently enumerated subnet from where the network scan returned live network devices
                    $Scan = $Script:QuickScan | Where-Object { $_.Computername -eq $IP }
                    #Invoke Mac Lookup
                    $Mac2 = $Mac.substring(0, 8)
                    $OUI1 = Get-Content $Script:ScanOUI | Where-Object { $_ -like "$mac2*" }
                    #Sometimes the MAC won't be Null, but it's not an actual MAC address. This try catch was quicker to implement then troubleshooting the cause.
                    try {
                        $OUI2 = $OUI1.Substring(9)
                    }
                    catch {
                        $OUI2 = $null
                    }
                    ForEach-Object {
                        #Take the enumerated CIDR IP results from the API call and Port scan. Output using a PSCustomObject
                        [pscustomobject]@{
                            IP            = $IP
                            Hostname      = $Scan.'IP/DNS'
                            MacAddress    = $MAC
                            Vendor        = $OUI2
                            Ping          = $Scan.Ping
                            'ssh(22)'     = $Scan.'Port 22'
                            'telnet(23)'  = $Scan.'Port 23'
                            'http(80)'    = $Scan.'Port 80'
                            'https(443)'  = $Scan.'Port 443'
                            'rdp(3389)'   = $Scan.'Port 3389'
                            'print(9100)' = $Scan.'Port 9100'
                        }
                    } | Where-Object { ($null -ne $scan.'IP/DNS') -or ($null -ne $MAC) -or ($Scan.Ping -eq $true) -or ($Scan.'Port 22' -eq $true) -or ($Scan.'Port 23' -eq $true) -or ($Scan.'Port 25' -eq $true) -or ($Scan.'Port 53' -eq $true) -or ($Scan.'Port 67' -eq $true) -or ($Scan.'Port 80' -eq $true) -or ($Scan.'Port 139' -eq $true) -or ($Scan.'Port 389' -eq $true) -or ($Scan.'Port 443' -eq $true) -or ($Scan.'Port 445' -eq $true) -or ($Scan.'Port 902' -eq $true) -or ($Scan.'Port 3389' -eq $true) -or ($Scan.'Port 9100' -eq $true) }
                }
            }

            #If Mac is Null or Skipmac parameter is used
            else {
                #If Deepscan parameter is chosen
                if ($DeepScan) {
                    #Filter the port scan to return results for the currently enumerated IP from the CIDR Range foreach-object loop
                    $Scan = $Script:SlowScan | Where-Object { $_.Computername -eq $IP }
                    #Take the enumerated CIDR IP results from the Port scan and Output using a PSCustomObject
                    [pscustomobject]@{
                        IP                 = $IP
                        Hostname           = $Scan.'IP/DNS'
                        MacAddress         = $MAC
                        Vendor             = $null
                        Ping               = $Scan.Ping
                        'ssh(22)'          = $Scan.'Port 22'
                        'telnet(23)'       = $Scan.'Port 23'
                        'smtp(25)'         = $Scan.'Port 25'
                        'dns(53)'          = $Scan.'Port 53'
                        'dhcp(67)'         = $Scan.'Port 67'
                        'http(80)'         = $Scan.'Port 80'
                        'smb-netbios(139)' = $Scan.'Port 139'
                        'ldap(389)'        = $Scan.'Port 389'
                        'https(443)'       = $Scan.'Port 443'
                        'smb-cifs(445)'    = $Scan.'Port 445'
                        'vmware(902)'      = $Scan.'Port 902'
                        'rdp(3389)'        = $Scan.'Port 3389'
                        'print(9100)'      = $Scan.'Port 9100'
                    } | Where-Object { ($null -ne $scan.'IP/DNS') -or ($null -ne $MAC) -or ($Scan.Ping -eq $true) -or ($Scan.'Port 22' -eq $true) -or ($Scan.'Port 23' -eq $true) -or ($Scan.'Port 25' -eq $true) -or ($Scan.'Port 53' -eq $true) -or ($Scan.'Port 67' -eq $true) -or ($Scan.'Port 80' -eq $true) -or ($Scan.'Port 139' -eq $true) -or ($Scan.'Port 389' -eq $true) -or ($Scan.'Port 443' -eq $true) -or ($Scan.'Port 445' -eq $true) -or ($Scan.'Port 902' -eq $true) -or ($Scan.'Port 3389' -eq $true) -or ($Scan.'Port 9100' -eq $true) }
                }
                #If Deepscan parameter isn't chosen
                else {
                    #Filter the port scan to return results for the currently enumerated IP from the CIDR Range foreach-object loop
                    $Scan = $Script:QuickScan | Where-Object { $_.Computername -eq $IP }
                    #Take the enumerated CIDR IP results from the Port scan and Output using a PSCustomObject
                    [pscustomobject]@{
                        IP            = $IP
                        Hostname      = $Scan.'IP/DNS'
                        MacAddress    = $MAC
                        Vendor        = $null
                        Ping          = $Scan.Ping
                        'ssh(22)'     = $Scan.'Port 22'
                        'telnet(23)'  = $Scan.'Port 23'
                        'http(80)'    = $Scan.'Port 80'
                        'https(443)'  = $Scan.'Port 443'
                        'rdp(3389)'   = $Scan.'Port 3389'
                        'print(9100)' = $Scan.'Port 9100'
                    } | Where-Object { ($null -ne $scan.'IP/DNS') -or ($null -ne $MAC) -or ($Scan.Ping -eq $true) -or ($Scan.'Port 22' -eq $true) -or ($Scan.'Port 23' -eq $true) -or ($Scan.'Port 25' -eq $true) -or ($Scan.'Port 53' -eq $true) -or ($Scan.'Port 67' -eq $true) -or ($Scan.'Port 80' -eq $true) -or ($Scan.'Port 139' -eq $true) -or ($Scan.'Port 389' -eq $true) -or ($Scan.'Port 443' -eq $true) -or ($Scan.'Port 445' -eq $true) -or ($Scan.'Port 902' -eq $true) -or ($Scan.'Port 3389' -eq $true) -or ($Scan.'Port 9100' -eq $true) }
                }
            }
        }
    }

    end {
    }
}