Public/Network/Scan-NetworkforWebLogon.ps1

FUNCTION Scan-NetworkforWebLogon {

    [CmdletBinding()]
    PARAM (
        $CIDR,
        $TimeountSeconds = 3
    )

    FUNCTION Get-IPRangeFromCIDR {
    Param(
        [Parameter(Mandatory=$true, HelpMessage="Enter a subnet in the form a.b.c.d/m")]
        [string]$Cidr
    )
    
    # Split the CIDR notation into IP and prefix length
    $parts = $Cidr -split "/"
    $ipAddressString = $parts[0]
    [int]$prefixLength = $parts[1]
    
    # Convert the IP address to a 32-bit integer for bitwise operations
    $ip = [System.Net.IPAddress]::Parse($ipAddressString)
    [byte[]]$ipBytes = $ip.GetAddressBytes()
    [Array]::Reverse($ipBytes) # Reverse to match little-endian byte order
    [UInt32]$ipUint = [System.BitConverter]::ToUInt32($ipBytes, 0)
    
    # Calculate the network mask (e.g., /24 is 255.255.255.0)
    [UInt32]$maskUint = [UInt32]::MaxValue -shr (32 - $prefixLength) -shl (32 - $prefixLength)

    # Calculate the network ID (first IP) and broadcast address (last IP) using bitwise operations
    [UInt32]$networkIdUint = $ipUint -band $maskUint
    [UInt32]$broadcastUint = $ipUint -bor (-bnot $maskUint)

    # Convert the integer results back to IPAddress objects
    [byte[]]$networkBytes = [System.BitConverter]::GetBytes($networkIdUint)
    [Array]::Reverse($networkBytes)
    [System.Net.IPAddress]$firstIP = [System.Net.IPAddress]::New($networkBytes)

    [byte[]]$broadcastBytes = [System.BitConverter]::GetBytes($broadcastUint)
    [Array]::Reverse($broadcastBytes)
    [System.Net.IPAddress]$lastIP = [System.Net.IPAddress]::New($broadcastBytes)
    
    # Return a custom object with the results
    return [pscustomobject][ordered]@{
        CIDR        = $Cidr
        FirstIP     = $firstIP.IPAddressToString
        LastIP      = $lastIP.IPAddressToString
        NetworkID   = $firstIP.IPAddressToString # First IP is generally the network ID
        Broadcast   = $lastIP.IPAddressToString # Last IP is the broadcast address
    }
    }
    FUNCTION Get-IPRange {
    param(
        [Parameter(Mandatory=$true)]
        [string]$StartIP,
        [Parameter(Mandatory=$true)]
        [string]$EndIP
    )
    
    # Convert the IP addresses to numerical values for iteration
    # The bytes need to be reversed due to Endianness differences between network order and system's UInt32 representation
    $start_bytes = ([System.Net.IPAddress]$StartIP).GetAddressBytes()
    [Array]::Reverse($start_bytes)
    $start_int = [System.BitConverter]::ToUInt32($start_bytes, 0)

    $end_bytes = ([System.Net.IPAddress]$EndIP).GetAddressBytes()
    [Array]::Reverse($end_bytes)
    $end_int = [System.BitConverter]::ToUInt32($end_bytes, 0)

    # Ensure start IP is not greater than end IP
    if ($start_int -gt $end_int) {
        Write-Error "Start IP address must be less than or equal to End IP address."
        return
    }

    $ip_range = @()
    for ($i = $start_int+1; $i -le $end_int-1; $i++) {
        # Convert the integer back to an IP address byte array, reverse it again, and join the octets
        $ip_bytes = [System.BitConverter]::GetBytes([UInt32]$i)
        [Array]::Reverse($ip_bytes)
        $ip_string = ($ip_bytes -join '.')
        $ip_range += $ip_string
    }
    
    return $ip_range
}
    FUNCTION Test-TCPPorts {
    <#
    .SYNOPSIS
    Tests TCP connectivity to a specified host on multiple ports asynchronously.
 
    .DESCRIPTION
    Tests TCP connectivity to a specified hostname or IP address on ports 1-1024, 4443, 4444, 8000, 8080, 8443, and 10443.
    Runs tests asynchronously with a maximum of 100 concurrent tests and displays a progress bar.
 
    .PARAMETER ComputerName
    The hostname or IP address to test connectivity against.
 
    .PARAMETER Timeout
    The timeout in milliseconds for each TCP connection attempt. Default is 2000ms.
 
    .EXAMPLE
    Test-TCPPorts -ComputerName "example.com"
    Tests TCP connectivity to example.com on specified ports.
 
    .EXAMPLE
    Test-TCPPorts -ComputerName "192.168.1.1" -Timeout 1000
    Tests TCP connectivity to 192.168.1.1 with a 1000ms timeout.
 
    .OUTPUTS
    PSCustomObject with properties: ComputerName, Port, IsOpen, Error (if any).
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [string]$ComputerName,

        [Parameter()]
        [int]$Timeout = 1200
    )

    # Define the ports to test
    $ports = @()
    $ports = 80, 443, 4433, 4443, 4444, 8000, 8080, 8443, 10443
    $ports = $ports | Sort-Object -Unique

    $totalPorts = $ports.Count
    $results = [System.Collections.ArrayList]::new()
    $completed = 0

    # Create a runspace pool
    $runspacePool = [RunspaceFactory]::CreateRunspacePool(1, 50)
    $runspacePool.Open()

    $tasks = [System.Collections.ArrayList]::new()

    # ScriptBlock for testing a single port
    $scriptBlock = {
        param ($ComputerName, $Port, $Timeout)
        $result = [PSCustomObject]@{
            ComputerName = $ComputerName
            Port         = $Port
            IsOpen       = $false
        }

        try {
            $tcpClient = New-Object System.Net.Sockets.TcpClient
            $connection = $tcpClient.BeginConnect($ComputerName, $Port, $null, $null)
            $success = $connection.AsyncWaitHandle.WaitOne($Timeout, $false)

            if ($success) {
                $tcpClient.EndConnect($connection)
                $result.IsOpen = $true
            }
            $tcpClient.Close()
        }
        catch {
            $result.Error = $_.Exception.Message
        }
        finally {
            if ($null -ne $tcpClient) {
                $tcpClient.Dispose()
            }
        }

        return $result | Where-Object { $_.IsOpen -eq $True }
    }

    # Create async tasks for each port
    foreach ($port in $ports) {
        $powershell = [PowerShell]::Create()
        $powershell.RunspacePool = $runspacePool
        [void]$powershell.AddScript($scriptBlock).AddArgument($ComputerName).AddArgument($port).AddArgument($Timeout)
        $handle = $powershell.BeginInvoke()
        $tasks.Add([PSCustomObject]@{
            PowerShell = $powershell
            Handle     = $handle
            Port       = $port
        }) | Out-Null
    }

    # Process tasks and update progress
    while ($tasks.Count -gt 0) {
        $completedTasks = $tasks | Where-Object { $_.Handle.IsCompleted }
        foreach ($task in $completedTasks) {
            $result = $task.PowerShell.EndInvoke($task.Handle)
            if ($result.IsOpen) {
                [void]$results.Add($result)
            }
            $task.PowerShell.Dispose()
            $tasks.Remove($task)
            $completed++

            # Update progress bar
            $percentComplete = [math]::Round(($completed / $totalPorts) * 100, 2)
            Write-Progress -Activity "Testing TCP Ports on $ComputerName" `
                          -Status "Tested $completed of $totalPorts ports" `
                          -PercentComplete $percentComplete -Id 120932315
        }

        # Small sleep to prevent excessive CPU usage
        Start-Sleep -Milliseconds 100
    }

    # Clean up
    $runspacePool.Close()
    $runspacePool.Dispose()

    # Complete the progress bar
    Write-Progress -Id 120932315 -Activity "Testing TCP Ports on $ComputerName" -Completed

    # Return results sorted by port
    return $results | Sort-Object Port
    }
    FUNCTION ParseWebContentforDeviceInfo {

        [CmdletBinding()]
        PARAM (
            $Page
        )

        $Result = [PSCustomObject]@{            
            DeviceType = $null
            DeviceManufacturer = $null
            DeviceModel = $null
            LoginPage = "Unknown"
        }

        IF ($Page.Content -like "* ARRIS *") { $Result.DeviceManufacturer = "ARRIS" ; $Result.DeviceType = "Modem\Router" }

        RETURN $Result
    }

    $IPs = Get-IPRange -StartIP (Get-IPRangeFromCIDR $CIDR).FirstIP -EndIP (Get-IPRangeFromCIDR $CIDR).LastIP

    $ScanResults = @()
    $DeviceInfo = $Null
    $IPCount = $IPs.Count
    $IPCountCurrent = 0
    $Protocol = $null
    FOREACH ($IP in $IPs) {
        

        $IPCountCurrent++
        $PercentProgress = [Math]::Round($($(($IPCountCurrent-1)/$IPCount)*100),0)
        Write-Progress -Id 120932316 -Activity "$PercentProgress% - Checking for Open Ports on $IP" -Status "Checking IP $IPCountCurrent of $IPCount" -PercentComplete $PercentProgress
        $Protocol = $null

        $OpenPorts = (Test-TCPPorts $IP).Port
        FOREACH ($OpenPort in $OpenPorts) {            

            $Page = $null
            TRY {
                #IF ($OpenPort -ne 80) {
                    [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }                
                    $Page = Invoke-WebRequest -Uri "https://$($IP):$OpenPort/" -UseBasicParsing -TimeoutSec $TimeountSeconds -ErrorAction SilentlyContinue
                    $Protocol = "HTTPS"
                #}
            }
            CATCH {
                TRY {
                    #IF ($OpenPort -ne 433) {
                        [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
                        $Page = Invoke-WebRequest -Uri "http://$($IP):$OpenPort/" -UseBasicParsing -TimeoutSec $TimeountSeconds -ErrorAction SilentlyContinue
                        $Protocol = "HTTP"
                    #}
                }
                CATCH {

                }
            }

            IF ($Page -eq $null -or $Page -eq "") {
                $ScanResults += [PSCustomObject]@{    
                    PSTypeName = 'IntegrisPowerShell.NetworkforWebLogonPages'
                    IPAddress = $IP
                    Port = $OpenPort
                    Protocol = $Protocol     
                    Type = $null
                    Manufacturer = $null
                    Model = $null
                    LoginPage = "No"
                }                
            }
            ELSE {
                $DeviceInfo = ParseWebContentforDeviceInfo $Page

                $ScanResults += [PSCustomObject]@{    
                    PSTypeName = 'IntegrisPowerShell.NetworkforWebLogonPages'
                    IPAddress = $IP
                    Port = $OpenPort
                    Protocol = $Protocol
                    Type = $DeviceInfo.DeviceType
                    Manufacturer = $DeviceInfo.DeviceManufacturer
                    Model = $DeviceInfo.DeviceModel
                    LoginPage = $DeviceInfo.LoginPage
                }
            }
        }
    }

    Write-Progress -Id 120932316 -Activity "(($IPCountCurrent/$IPCount)*100)% - Checking for Open Ports on $IP" -Completed

    RETURN $ScanResults
}