PowerShellで作るネットワークスキャナー:ネットワークセキュリティ評価レポート作成を追加

Powershell

はじめに

前回作成したネットワークスキャナーに、ネットワークセキュリティ評価レポート作成機能を追加しました。同一ネットワーク上の全デバイスのMACアドレスを収集し、各デバイスのベンダー(製造元)情報を自動で取得できるだけでなく、ポートスキャン機能も備えているため、デバイスのセキュリティ状態も確認できます。さらに今回は、スキャン結果から分かりやすいHTMLレポートを自動生成し、サービスバージョン情報も詳細に取得できるようになりました。

「知らないデバイスが接続されていないか」のチェック、「デバイスが不要なポートを開放していないか」のチェック、そして「使用中のサービスのバージョン情報」の可視化に活用するためのスクリプトです。

<ネットワークセキュリティ評価レポートのサンプル>

<検出されたデバイス一覧>

このスクリプトで出来ること

  • ローカルネットワーク内の全IPアドレスに対してPingを実行
  • 応答があったデバイスのMACアドレスを取得
  • IEEE OUIデータベースを使用して、MACアドレスからベンダー情報を特定
  • 各デバイスに対してポートスキャンを実行し、オープンポートを検出
  • オープンポートからサービスバナー情報を取得し、サービスのバージョンを特定(新)
  • 詳細なサービス分析(SMB, RDP, VNC, MySQLなど)を実行(新)
  • 検出したデバイス情報とポート情報をCSVファイルに出力
  • スキャン結果を包括的なHTMLセキュリティレポートとして自動生成(新)
  • 将来的にはサービスの脆弱性スキャンも実施予定(実装中)(新)
  • 結果をコンソール上にも分かりやすく表示

全スクリプトのダウンロード

<ダウンロードファイルの構成>

C:.
│  networkscanner.ps1
│
├─Data
└─Modules
        Banner-Analyzer.ps1
        Banner-Grabber.ps1
        Enhanced-Scanner.ps1
        MAC-Vendor.ps1
        MySQL-Detailed-Scanner.ps1
        Port-Scanner.ps1
        RDP-Detailed-Scanner.ps1
        Report-Generator.ps1
        SMB-Detailed-Scanner.ps1
        VNC-Detailed-Scanner.ps1
        Vulnerability-Scanner.ps1

スクリプトの解説

主要機能

ネットワークスキャナーは、複数のPowerShellモジュールから構成されており、各モジュールが特定の機能を担当しています。

  1. Port-Scanner.ps1: TCP接続を使ったポートスキャン機能を提供
  2. Banner-Grabber.ps1: オープンポートからサービスバナーを取得
  3. Banner-Analyzer.ps1: 取得したバナーからソフトウェア情報を解析
  4. MAC-Vendor.ps1: MACアドレスからベンダー情報を識別
  5. Enhanced-Scanner.ps1: Nmapを活用した高度なスキャン機能
  6. [サービス名]-Detailed-Scanner.ps1: 各種サービス(SMB, RDP, VNC, MySQL)の詳細分析
  7. Report-Generator.ps1: 包括的なHTMLセキュリティレポート生成機能
  8. Vulnerability-Scanner.ps1: バナー情報と脆弱性データベースを照合(開発中)

パラメータ設定

ネットワークスキャナーは、豊富なパラメータを持ち、ニーズに合わせたカスタマイズが可能です。これらのパラメータを組み合わせることで、軽量なスキャンから詳細な脆弱性診断まで、様々なレベルのスキャンを実行できます。

オプション 説明
StartIP “192.168.1.1” スキャン開始IPアドレス
EndIP “192.168.1.254” スキャン終了IPアドレス
Subnet “192.168.1.0/24” サブネット指定(代替方法)
PortsToScan @(22, 80, 443, 3389) スキャン対象ポート指定
EnablePortScan ポートスキャンを有効化
EnableBannerGrabbing バナー取得を有効化
EnableDetailedBannerAnalysis バナー詳細解析を有効化
EnableVulnScan 脆弱性スキャンを有効化(開発中)
UpdateVulnDatabase 脆弱性DBを更新
SuppressVulnDetails 脆弱性の詳細表示を抑制
EnableDetailedSMBScan SMB詳細スキャン
EnableDetailedRDPScan RDP詳細スキャン
EnableDetailedVNCScan VNC詳細スキャン
EnableDetailedMySQLScan MySQL詳細スキャン
MySQLUsername “user” MySQL認証用ユーザー名
MySQLPassword “pass” MySQL認証用パスワード
EnableEnhancedScanning Nmapを使用した拡張スキャン
NoConsoleOutput コンソール出力を抑制
OutputPath “C:\Scan\Results” 出力先ディレクトリ

使用方法

基本的な実行方法

  1. スクリプトをダウンロードして任意の場所に保存します(例:networkscanner.ps1)
  2.  PowerShellを管理者権限で起動
  3. スクリプトのあるディレクトリに移動
  4. 以下のコマンドを実行
.\networkscanner.ps1

実行時のオプション例

基本的なポートスキャン(軽量版)

.\networkscanner.ps1 -EnablePortScan -PortsToScan @(22, 80, 443, 3389)

バナー情報取得とバージョン解析

.\networkscanner.ps1 -EnablePortScan -EnableBannerGrabbing -EnableDetailedBannerAnalysis

セキュリティレポート付きの詳細スキャン

.\networkscanner.ps1 -EnablePortScan -EnableBannerGrabbing -EnableDetailedBannerAnalysis

RDPとSMBに特化した詳細スキャン

.\networkscanner.ps1 -Subnet "192.168.1.0/24" -EnablePortScan -EnableDetailedSMBScan -EnableDetailedRDPScan

完全な詳細スキャン(すべての機能を有効化)

.\networkscanner.ps1 -EnablePortScan -EnableBannerGrabbing -EnableDetailedBannerAnalysis -EnableEnhancedScanning -EnableDetailedSMBScan -EnableDetailedRDPScan -EnableDetailedVNCScan -EnableDetailedMySQLScan

実行結果

スクリプトを実行すると、以下のような出力が表示されます。

拡張ネットワークデバイス検出ツール(脆弱性スキャン・バナー詳細解析機能付き)
-----------------------------------------------------
ポートスキャンが有効: 20, 21, 22, 23, 25, 53, 67, 68, 69, 80, 110...
バナー取得が有効: オープンポートのサービス情報を取得します
バナー詳細解析が有効: ソフトウェアバージョン情報を抽出します

スキャンを開始: 192.168.1.1 から 192.168.1.254
192.168.1.1 は応答中...
  MAC: 00-11-22-33-44-55  ベンダー: Cisco Systems
  ポートスキャン中... 完了
  オープンポート: 22(SSH), 80(HTTP), 443(HTTPS)
  バナー詳細解析結果:
    ポート 22(SSH): openssh:openssh バージョン: 8.2p1
    ポート 80(HTTP): nginx:nginx バージョン: 1.18.0
    ポート 443(HTTPS): nginx:nginx バージョン: 1.18.0

192.168.1.25 は応答中...
  MAC: AA-BB-CC-DD-EE-FF  ベンダー: Microsoft
  詳細SMBスキャンを実行中...
  SMB詳細情報: OS=Windows 10 or Server 2016+, Version=SMB 3.1.1
  ポートスキャン中... 完了
  オープンポート: 135(EPMAP), 139(NetBIOS), 445(SMB), 3389(RDP)
  詳細RDPスキャンを実行中... 完了
  RDP詳細情報: OS=Windows 10, Version=RDP 10.0 (Windows 10/11/Server 2016+), Security=CredSSP
  バナー詳細解析結果:
    ポート 445(SMB): microsoft:smb バージョン: SMB 3.1.1
    ポート 3389(RDP): microsoft:rdp バージョン: 10.0

...

検出結果: 25台

IPAddress     MACAddress       Vendor          HostName     OpenPorts
---------     ----------       ------          --------     ---------
192.168.1.1   00-11-22-33-44-55 Cisco Systems   router1.local 22(SSH), 80(HTTP), 443(HTTPS)
192.168.1.25  AA-BB-CC-DD-EE-FF Microsoft       win10-pc.local 135(EPMAP), 139(NetBIOS), 445(SMB), 3389(RDP)
...

バナー詳細解析結果:
  192.168.1.1 (router1.local):
    ポート 22(SSH): openssh:openssh バージョン: 8.2p1 [SSH Authentication]
    ポート 80(HTTP): nginx:nginx バージョン: 1.18.0 [HTTP Header]
    ポート 443(HTTPS): nginx:nginx バージョン: 1.18.0 [HTTP Header]
  ...

CSVファイル出力完了: C:\Path\To\Results\network_devices.csv
セキュリティレポート出力完了: C:\Path\To\Results\security_report.html
レポートをブラウザで確認するには、以下のコマンドを実行してください:
Start-Process C:\Path\To\Results\security_report.html

ネットワークセキュリティ評価レポート(実装中)

本スクリプトの最大の特長の一つが、自動生成される「ネットワークセキュリティ評価レポート」です。このレポートはHTML形式で出力され、以下のような情報が視覚的に整理されています。

レポートの主な内容

  1. スキャン概要:
    • 検出されたデバイス数
    • スキャン実施日時
    • 検出された脆弱性の総数と重要度別の内訳(脆弱性スキャンが有効な場合)
  2. ホスト別セキュリティ評価:
    • IPアドレス、ホスト名、MACアドレス、ベンダー情報
    • オープンポートとサービス情報のリスト
    • バナー解析で特定されたサービス識別情報
      • ポート番号
      • サービスタイプ
      • 製品名とベンダー
      • バージョン情報
  3. 脆弱性情報 (脆弱性スキャン機能実装後):
    • 重要度別に分類された脆弱性リスト
      • 重要(Critical)
      • 高リスク(High)
      • 中リスク(Medium)
      • 低リスク(Low)
    • 各脆弱性の詳細情報
      • CVE ID
      • CVSSスコア
      • 影響を受けるバージョン
      • 脆弱性の説明

<ネットワークセキュリティ評価レポートのサンプル>

各スクリプトの解説

networkscanner.ps1:メインスクリプト

すべてのモジュールを統合して実行するメインスクリプトです。

<ポイント>

  • IP範囲の柔軟な指定方法(開始-終了IP、サブネット表記)
  • アクティブなホストのみを詳細スキャンして効率化
  • 結果をCSVファイルとHTMLレポートの両方で出力
  • スキャンの実行順序を最適化(ポートスキャン→バナー取得→詳細スキャン)
# NetworkScanner.ps1
# パラメータ定義
param (
    [string]$StartIP = "192.168.1.1",
    [string]$EndIP = "192.168.1.254",
    [string]$Subnet = "",
    [int[]]$PortsToScan = @(20, 21, 22, 23, 25, 53, 80, 443, 445, 3306, 3389, 5900, 8080),
    [switch]$EnablePortScan = $false,
    [switch]$EnableBannerGrabbing = $false,
    [switch]$EnableDetailedBannerAnalysis = $false,
    [switch]$EnableEnhancedScanning = $false,
    [switch]$EnableVulnScan = $false,
    [switch]$EnableDetailedSMBScan = $false,
    [switch]$EnableDetailedRDPScan = $false
    # その他のパラメータ...
)

# 必要なモジュールの読み込み
$modulesDir = Join-Path $PSScriptRoot "Modules"
$modulesToLoad = @(
    "$modulesDir\Port-Scanner.ps1",
    "$modulesDir\Banner-Grabber.ps1",
    # その他のモジュール...
)

# モジュールのロード
foreach ($module in $modulesToLoad) {
    if (Test-Path $module) {
        . $module
    } else {
        Write-Host "モジュールファイルが見つかりません: $module" -ForegroundColor Red
        exit
    }
}

# IPアドレス範囲の設定
$IPRange = @()
if ($Subnet) {
    # サブネット表記の場合
    if ($Subnet -match "^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/(\d{1,2})$") {
        # サブネット処理...
    }
} elseif ($StartIP -and $EndIP) {
    # 開始IPと終了IPが指定されている場合
    # IPレンジ処理...
}

# スキャン結果を格納する配列
$scanResults = @()

# 各IPをスキャン
foreach ($ip in $IPRange) {
    # pingでホストの存在を確認
    $ping = Test-Connection -ComputerName $ip -Count 1 -Quiet
    
    if ($ping) {
        # MACアドレスの取得
        $arpResult = arp -a $ip | Out-String
        $macPattern = "([0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2})"
        $macAddress = if ($arpResult -match $macPattern) { $matches[1] } else { "不明" }
        
        # ベンダー情報の取得
        $vendorInfo = if ($macAddress -ne "不明") { 
            Get-MACVendor -MACAddress $macAddress -OuiDatabasePath $OuiDatabasePath 
        } else { 
            "不明" 
        }
        
        # オープンポートのスキャン
        $openPorts = @()
        $bannerDetails = @()
        
        if ($EnablePortScan) {
            $openPorts = Get-OpenPorts -IPAddress $ip -Ports $PortsToScan -GetBanners:$EnableBannerGrabbing
            
            if ($EnableDetailedBannerAnalysis -and $EnableBannerGrabbing -and $openPorts) {
                # バナー解析処理...
            }
        }
        
        # SMB詳細スキャン
        $smbDetailedResults = $null
        if ($EnableDetailedSMBScan -and ($openPorts | Where-Object { $_.Port -eq 139 -or $_.Port -eq 445 })) {
            $smbDetailedResults = Get-SMBDetailedInfo -IPAddress $ip
        }
        
        # RDP詳細スキャン
        $rdpDetailedResults = $null
        if ($EnableDetailedRDPScan -and ($openPorts | Where-Object { $_.Port -eq 3389 })) {
            $rdpDetailedResults = Get-RDPDetailedInfo -IPAddress $ip
        }
        
        # その他の詳細スキャン...
        
        # スキャン結果オブジェクトの作成
        $scanResult = [PSCustomObject]@{
            IPAddress = $ip
            HostName = $hostName
            MACAddress = $macNormalized
            Vendor = $vendorInfo
            OpenPorts = $openPortsStr
            BannerDetails = $bannerDetails
            Vulnerabilities = $vulnerabilities
            DetectedTime = Get-Date -Format "yyyy/MM/dd HH:mm:ss"
        }
        
        # 結果を配列に追加
        $scanResults += $scanResult
    }
}

# CSVファイルに出力
$csvPath = Join-Path -Path $OutputPath -ChildPath "network_devices.csv"
$scanResults | Select-Object IPAddress, HostName, MACAddress, Vendor, OpenPorts, DetectedTime | 
    Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8

# セキュリティレポートの生成
$reportPath = Join-Path -Path $OutputPath -ChildPath "security_report.html"
Generate-VulnerabilityReport -ScanResults $scanResults -ReportPath $reportPath

    Port-Scanner.ps1:ポートスキャン機能

    TCP接続を使って指定したIPアドレスのポート開放状況を確認します。

    <ポイント>

    • 非同期TCP接続を使用してタイムアウト処理を効率的に実装
    • よく知られたポートとサービスのマッピングを提供
    • オプションでバナー情報の取得も可能(Get-ServiceBanner関数を呼び出し)
    # 指定したIPアドレスとポートの接続テスト
    function Test-Port {
        param(
            [string]$IPAddress,
            [int]$Port,
            [int]$Timeout = 100
        )
        
        $tcpClient = New-Object System.Net.Sockets.TcpClient
        try {
            $connectionResult = $tcpClient.BeginConnect($IPAddress, $Port, $null, $null)
            $waitResult = $connectionResult.AsyncWaitHandle.WaitOne($Timeout, $false)
            
            if ($waitResult) {
                try {
                    $tcpClient.EndConnect($connectionResult)
                    return $true
                } catch {
                    return $false
                }
            }
        } catch {
            return $false
        } finally {
            $tcpClient.Close()
        }
        return $false
    }
    
    # IPアドレスに対してポートスキャンを実行し、オープンポートのリストを返す関数
    function Get-OpenPorts {
        param(
            [string]$IPAddress,
            [int[]]$Ports,
            [switch]$GetBanners = $false,
            [int]$BannerTimeout = 2000
        )
        
        $openPorts = @()
        
        foreach ($port in $Ports) {
            if (Test-Port -IPAddress $IPAddress -Port $port) {
                $serviceName = if ($portServices.ContainsKey($port)) { $portServices[$port] } else { "不明" }
                
                # バナー情報
                $bannerInfo = "取得なし"
                
                if ($GetBanners) {
                    $bannerResult = Get-ServiceBanner -IPAddress $IPAddress -Port $port -Timeout $BannerTimeout
                    $bannerInfo = $bannerResult.Banner
                }
                
                $openPorts += [PSCustomObject]@{
                    Port = $port
                    Service = $serviceName
                    Banner = $bannerInfo
                    Protocol = if ($portServices.ContainsKey($port)) { $portServices[$port] } else { "不明" }
                }
            }
        }
        
        return $openPorts
    }

    Banner-Grabber.ps1:バナー取得機能

    オープンポートから返されるバナー情報(サービス識別情報)を取得します。

    <ポイント>

    • サービスタイプに応じた専用の処理方法を実装
    • HTTP/HTTPSではHEADリクエストを使用して効率的にヘッダー情報を取得
    • SSHやFTPなど各種プロトコルに応じたハンドシェイクを実装
    • タイムアウト処理を実装してハングを防止
    # バナー情報を取得する関数
    function Get-ServiceBanner {
        param(
            [string]$IPAddress,
            [int]$Port,
            [int]$Timeout = 2000
        )
        
        # バナーを初期化
        $banner = "バナー取得失敗"
        
        # ポートに応じてバナー取得ロジックを変更
        switch ($Port) {
            # HTTP/HTTPSの場合は、HTTP GETリクエストを送信
            { $_ -in 80, 8000, 8080, 443, 8443 } {
                try {
                    # HTTPSの場合、証明書検証を無効化
                    if ($_ -in 443, 8443) {
                        [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
                    }
                    
                    $protocol = if ($_ -in 443, 8443) { "https" } else { "http" }
                    $uri = "$protocol`://$IPAddress`:$Port"
                    $request = [System.Net.WebRequest]::Create($uri)
                    $request.Timeout = $Timeout
                    $request.Method = "HEAD"  # HEADリクエストでヘッダーのみを取得
                    
                    try {
                        $response = $request.GetResponse()
                        $banner = "$($response.ProtocolVersion) $([int]$response.StatusCode) $($response.StatusDescription)"
                        
                        # サーバー情報が存在する場合
                        if ($response.Headers["Server"]) {
                            $banner += " | Server: $($response.Headers["Server"])"
                        }
                        
                        $response.Close()
                    } catch [System.Net.WebException] {
                        # エラーレスポンスからも情報を取得
                        if ($_.Exception.Response) {
                            $banner = "接続エラー: $($_.Exception.Message)"
                        }
                    }
                } catch {
                    $banner = "バナー取得エラー: $($_.Exception.Message)"
                }
            }
            # SSHの場合
            22 {
                try {
                    $client = New-Object System.Net.Sockets.TcpClient
                    if ($client.ConnectAsync($IPAddress, $Port).Wait($Timeout)) {
                        $stream = $client.GetStream()
                        $buffer = New-Object byte[] 1024
                        $read = $stream.Read($buffer, 0, 1024)
                        if ($read -gt 0) {
                            $banner = [System.Text.Encoding]::ASCII.GetString($buffer, 0, $read).Trim()
                        }
                        $stream.Close()
                    }
                    $client.Close()
                } catch {
                    $banner = "バナー取得エラー: $($_.Exception.Message)"
                }
            }
            # その他多数のプロトコル処理(省略)...
        }
        
        return [PSCustomObject]@{
            Port = $Port
            Protocol = $protocol
            Banner = $banner
        }
    }

    Banner-Analyzer.ps1:バナー解析機能

    バナー文字列から製品名とバージョン情報を抽出するエンジンです。

    <ポイント>

    • サービスタイプごとに正規表現パターンのライブラリを構築
    • バージョン文字列の標準化によりバージョン比較が容易に
    • ベンダーと製品の区別を明確にするデータ構造設計
    • HTTP/SSHなど各種プロトコルに対応したパターンを用意
    # バナーシグネチャファイルの初期化
    function Initialize-BannerSignatures {
        param (
            [string]$BannerSignaturesPath
        )
        
        # 基本的なシグネチャを作成
        $bannerSignatures = @(
            @{
                ServiceType = "HTTP"
                Patterns = @(
                    @{
                        RegEx = "Server: Apache/(\d+\.\d+\.\d+)"
                        VendorProduct = "apache:http_server"
                    },
                    @{
                        RegEx = "Server: nginx/(\d+\.\d+\.\d+)"
                        VendorProduct = "nginx:nginx"
                    },
                    # 他多数のパターン...
                )
            },
            @{
                ServiceType = "SSH"
                Patterns = @(
                    @{
                        RegEx = "SSH-\d+\.\d+-OpenSSH[_-](\d+\.\d+[p\d]*)"
                        VendorProduct = "openssh:openssh"
                    },
                    # 他のSSHパターン...
                )
            },
            # 他のサービスタイプ...
        )
        
        return $bannerSignatures
    }
    
    # バージョン文字列を標準形式に変換する関数
    function Convert-VersionString {
        param (
            [string]$Version
        )
        
        # 空または無効なバージョン文字列の場合
        if ([string]::IsNullOrEmpty($Version)) {
            return $null
        }
        
        # バージョン文字列を分割して標準化
        if ($Version -match "(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?") {
            $major = if ($matches[1]) { [int]$matches[1] } else { 0 }
            $minor = if ($matches[2]) { [int]$matches[2] } else { 0 }
            $patch = if ($matches[3]) { [int]$matches[3] } else { 0 }
            $build = if ($matches[4]) { [int]$matches[4] } else { 0 }
            
            return [PSCustomObject]@{
                Major = $major
                Minor = $minor
                Patch = $patch
                Build = $build
                FullString = "$major.$minor.$patch.$build"
                OriginalString = $Version
            }
        }
        
        return $null
    }
    
    # バナー情報から製品・バージョン情報を抽出する関数
    function Extract-BannerInfo {
        param (
            [string]$Banner,
            [string]$ServiceType,
            [array]$BannerSignatures
        )
        
        # サービスタイプに対応するシグネチャパターンを検索
        $signatureSet = $BannerSignatures | Where-Object { $_.ServiceType -eq $ServiceType }
        
        if ($null -eq $signatureSet) {
            return $null
        }
        
        # 各パターンに対して正規表現マッチングを実行
        foreach ($pattern in $signatureSet.Patterns) {
            if ($Banner -match $pattern.RegEx) {
                # バージョン文字列を抽出(正規表現の最初のキャプチャリンググループ)
                $versionString = if ($matches.Count -gt 1) { $matches[1] } else { "unknown" }
                
                # ベンダー:製品を分析
                $vendorProductParts = $pattern.VendorProduct -split ":"
                $vendor = $vendorProductParts[0]
                $product = $vendorProductParts[1]
                
                # 結果を返す
                return [PSCustomObject]@{
                    Vendor = $vendor
                    Product = $product
                    Version = $versionString
                    ParsedVersion = Convert-VersionString -Version $versionString
                    FullBanner = $Banner
                    MatchPattern = $pattern.RegEx
                    ServiceType = $ServiceType
                }
            }
        }
        
        return $null
    }

    MAC-Vendor.ps1:MACベンダー解決

    MACアドレスからベンダー(製造元)情報を特定するモジュールです。

    <ポイント>

    • IEEE OUIデータベースを自動的にダウンロードして最新状態を維持
    • ダウンロード失敗時には最小限のデータベースをフォールバックとして提供
    • MACアドレスの形式正規化(ハイフンやコロンを除去)
    • 大文字・小文字を問わず検索できるよう正規化
    • MACベンダー情報はCSVファイルとして保存
    # OUIデータベースの初期化と作成
    function Initialize-OUIDatabase {
        param (
            [string]$OuiDatabasePath
        )
        
        # IEEE OUIデータベースをダウンロード
        $ouiUrl = "http://standards-oui.ieee.org/oui/oui.txt"
        $tempFile = "$env:TEMP\oui_temp.txt"
        
        try {
            Invoke-WebRequest -Uri $ouiUrl -OutFile $tempFile
            Write-Host "ダウンロード完了" -ForegroundColor Green
            
            # OUIデータを解析してCSVに変換
            $ouiData = @()
            
            Get-Content -Path $tempFile | ForEach-Object {
                # 新しい形式に対応した正規表現
                if ($_ -match "([0-9A-F]{2})-([0-9A-F]{2})-([0-9A-F]{2})\s+\(hex\)\s+(.+?)(\s{2,}|$)") {
                    $oui = "$($matches[1])$($matches[2])$($matches[3])"
                    $vendor = $matches[4].Trim()
                    
                    $ouiData += [PSCustomObject]@{
                        OUI = $oui
                        Vendor = $vendor
                    }
                }
            }
            
            # CSVとして保存
            $ouiData | Export-Csv -Path $OuiDatabasePath -NoTypeInformation
            Remove-Item -Path $tempFile -ErrorAction SilentlyContinue
            
            return $OuiDatabasePath
        }
        catch {
            Write-Host "ダウンロード失敗: $($_.Exception.Message)" -ForegroundColor Red
            Write-Host "代替データベースの作成を試みます..." -ForegroundColor Yellow
            
            # 最小限のベンダーリストを作成(省略)
            $minimalOuiData = @(
                [PSCustomObject]@{OUI = "000000"; Vendor = "Xerox Corporation"},
                [PSCustomObject]@{OUI = "00000C"; Vendor = "Cisco Systems"},
                # 他の主要ベンダー...
            )
            
            $minimalOuiData | Export-Csv -Path $OuiDatabasePath -NoTypeInformation
            return $OuiDatabasePath
        }
    }
    
    # MACアドレスからベンダー情報を取得する関数
    function Get-MACVendor {
        param (
            [string]$MACAddress,
            [string]$OuiDatabasePath
        )
        
        $oui = ($MACAddress -replace '[-:]', '').Substring(0, 6).ToUpper()
        $vendor = Import-Csv -Path $OuiDatabasePath | 
                  Where-Object { $_.OUI -eq $oui } | 
                  Select-Object -ExpandProperty Vendor -First 1
        
        if ($vendor) {
            return $vendor
        } else {
            return "不明"
        }
    }

    Report-Generator.ps1:HTMLレポート生成

    スキャン結果からHTMLレポートを生成するモジュールです。

    • HTML/CSSを使用してレポートを生成
    • 脆弱性を重要度別に色分けして直感的に把握できるデザイン
    • ホスト情報、ポート情報、脆弱性情報などを階層的に整理
    • セキュアなHTML生成(特殊文字のエスケープ処理)
    # HTML特殊文字をエスケープする関数
    function Escape-HtmlSpecialChars {
        param (
            [string]$text
        )
        
        if ([string]::IsNullOrEmpty($text)) {
            return ""
        }
        
        return $text.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;").Replace('"', "&quot;").Replace("'", "&#39;")
    }
    
    # ポートスキャン結果から脆弱性レポートを生成する関数
    function Generate-VulnerabilityReport {
        param (
            [array]$ScanResults,
            [string]$ReportPath
        )
        
        # レポートのHTMLヘッダー(CSSスタイルは省略)
        $htmlHeader = @"
    <!DOCTYPE html>
    <html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>ネットワークセキュリティ評価レポート</title>
        <style>
            /* スタイル定義(省略) */
        </style>
    </head>
    <body>
        <div class="container">
            <h1>ネットワークセキュリティ評価レポート</h1>
            <p>生成日時: $(Get-Date -Format "yyyy年MM月dd日 HH:mm:ss")</p>
    "@
        
        # 脆弱性集計情報の追加
        $totalVulns = 0
        $vulnerableHosts = 0
        # 脆弱性の集計処理...
        
        # サマリーセクションの生成
        $htmlSummary = @"
            <div class="summary">
                <h2>スキャン概要</h2>
                <p><strong>検出されたデバイス数:</strong> $($ScanResults.Count) 台</p>
                <p><strong>検出された脆弱性数:</strong> $totalVulnerabilities 件</p>
                <p>
                    <span class="severity critical">重要的: $criticalVulnerabilities</span>
                    <span class="severity high">高: $highVulnerabilities</span>
                    <span class="severity medium">中: $mediumVulnerabilities</span>
                    <span class="severity low">低: $lowVulnerabilities</span>
                </p>
            </div>
    "@
    
        # 各ホストの詳細情報セクション
        $htmlHosts = ""
        foreach ($hostItem in $ScanResults) {
            $hostVulnCount = if ($hostItem.Vulnerabilities) { $hostItem.Vulnerabilities.Count } else { 0 }
            
            $htmlHosts += @"
            <div class="host-section">
                <div class="host-header">
                    <h3>$($hostItem.IPAddress) $(if ($hostItem.HostName -ne "不明") { "($($hostItem.HostName))" })</h3>
                    <div>
                        <span class="severity $(if ($hostVulnCount -gt 0) { "high" } else { "low" })">
                            脆弱性: $hostVulnCount 件
                        </span>
                    </div>
                </div>
                
                <div class="host-info">
                    <p><strong>MACアドレス:</strong> $($hostItem.MACAddress)</p>
                    <p><strong>ベンダー:</strong> $($hostItem.Vendor)</p>
                    <p><strong>検出時刻:</strong> $($hostItem.DetectedTime)</p>
                </div>
                
                <h4>オープンポート情報</h4>
                <!-- ポート情報の表示 -->
                
                <!-- バナー情報の表示 -->
                
                <!-- 脆弱性情報の表示 -->
                
                <!-- 脆弱性がない場合のメッセージ -->
                <div class="security-ok">
                    <strong>セキュリティ評価:</strong> このホストでは既知の脆弱性は検出されませんでした。
                    最新のパッチが適用されているか、このサービスは現在の脆弱性データベースに登録されている脆弱性の影響を受けません。
                </div>
            </div>
    "@
        }
        
        # レポートのフッター
        $htmlFooter = @"
            <div class="footer">
                <p>このレポートはセキュリティ診断ツールによって自動生成されました</p>
                <p>スキャン日時: $(Get-Date -Format "yyyy年MM月dd日 HH:mm:ss")</p>
            </div>
        </div>
    </body>
    </html>
    "@
        
        # HTML全体を組み立てて保存
        $reportHtml = $htmlHeader + $htmlSummary + $htmlHosts + $htmlFooter
        $reportHtml | Out-File -FilePath $ReportPath -Encoding UTF8 -Force
        
        Write-Host "セキュリティレポートを作成しました: $ReportPath" -ForegroundColor Green
    }

    SMB-Detailed-Scanner.ps1:SMB詳細スキャン

    SMBサービスの詳細情報を取得するモジュールです。

    <ポイント>

    • Nmapを活用してSMBプロトコルの詳細情報を取得
    • SMBバージョン(方言)情報を解析して標準化
    • バージョン情報からOSバージョンを推定
    • 複数のNmapスクリプト(smb-os-discovery, smb-protocols等)を組み合わせて詳細情報を収集
    function Get-SMBDetailedInfo {
        param(
            [string]$IPAddress
        )
        
        # nmapが利用可能か確認
        $nmapPath = Get-Command nmap -ErrorAction SilentlyContinue
        if ($null -eq $nmapPath) {
            Write-Host "  SMB詳細スキャンエラー: nmapが見つかりません" -ForegroundColor Red
            return $fallbackResult
        }
        
        try {
            # nmapコマンドを実行
            $nmapCommand = "nmap -p 139,445 --script=smb-os-discovery,smb-protocols,smb-security-mode,smb-enum-shares $IPAddress"
            $nmapResult = & nmap -p 139,445 --script=smb-os-discovery,smb-protocols,smb-security-mode,smb-enum-shares $IPAddress 2>&1
            
            # OS情報を抽出
            $osLine = $nmapResult | Where-Object { $_ -match "OS:" }
            $sambaVersion = "不明"
            $osInfo = "不明"
            
            if ($osLine -match "OS: (.+)") {
                $osInfo = $matches[1].Trim()
                if ($osLine -match "Samba ([\d\.]+)") {
                    $sambaVersion = $matches[1].Trim()
                }
            }
            
            # プロトコル情報を抽出(SMBバージョン対応)
            [System.Collections.ArrayList]$dialectsList = @()
            $resultText = $nmapResult -join "`n"
            
            # SMB方言(バージョン)情報の抽出と標準化
            if ($resultText -match "\|\s+dialects:[^\|]+((?:\|\s+[^\|]+)+)") {
                $dialectsSection = $matches[1]
                
                foreach ($line in ($dialectsSection -split "`n")) {
                    if ($line -match "\|\s+(\d+:\d+:\d+)") {
                        $dialectsList.Add($matches[1]) | Out-Null
                    } elseif ($line -match "\|\s+(NT LM 0\.12)") {
                        if (-not ($dialectsList -contains "1:0:0")) {
                            $dialectsList.Add("1:0:0") | Out-Null  # SMB 1.0を標準表記に変換
                        }
                    }
                }
            }
            
            # 方言リストを値でソート
            $sortedDialects = @()
            # ソート処理...
            
            # バージョン判定ロジック - フェイルセーフ付き
            $dialectsStr = "不明"
            $dialectValue = 0
            
            if ($sortedDialects.Count -gt 0) {
                $highestDialect = $sortedDialects[0]
                $dialectsStr = $sortedDialects -join ", "
                
                # 方言に基づいてSMBバージョンとWindowsバージョンを判定
                if ($dialectValue -ge 30101) {  # 3:1:1
                    $sambaVersion = "SMB 3.1.1"
                    $osInfo = "Windows 10/11 or Server 2016+"
                } elseif ($dialectValue -ge 30000) {  # 3:0:0 または 3:0:2
                    $sambaVersion = "SMB 3.0"
                    $osInfo = "Windows 8/8.1 or Server 2012/R2"
                } 
                # その他のバージョン判定...
            }
            
            # ポート139用と445用の結果オブジェクトを作成
            $port139Result = [PSCustomObject]@{
                Port = 139
                Vendor = $vendor
                Product = "netbios-ssn"
                Version = $sambaVersion
                OSInfo = $osInfo
                ComputerName = $computerName
                DomainName = $domainName
                Dialects = $dialectsStr
                FullInfo = "OS: $osInfo, SMB Version: $sambaVersion" + $(if ($dialectsStr -ne "不明") { ", Dialects: $dialectsStr" } else { "" })
            }
            
            $port445Result = [PSCustomObject]@{
                Port = 445
                Vendor = $vendor
                Product = "smb"
                Version = $sambaVersion
                OSInfo = $osInfo
                ComputerName = $computerName
                DomainName = $domainName
                Dialects = $dialectsStr
                FullInfo = "OS: $osInfo, SMB Version: $sambaVersion" + $(if ($dialectsStr -ne "不明") { ", Dialects: $dialectsStr" } else { "" })
            }
            
            return @($port139Result, $port445Result)
        }
        catch {
            Write-Host "  SMB詳細スキャンエラー: $($_.Exception.Message)" -ForegroundColor Red
            return $fallbackResult
        }
    }

    以上です。

    コメント

    タイトルとURLをコピーしました