mcpField
D
detecting-network-scanning-with-ids-signatures

detecting-network-scanning-with-ids-signatures

github.com/mukul975セキュリティ35.2k85 回インストール8 週間前に更新
リポジトリ

detecting-network-scanning-with-ids-signatures について

SOCアナリストやセキュリティエンジニア向けに、ネットワーク偵察を検知するスキルです。IDSシグネチャ、しきい値アラート、後続の攻撃兆候との相関分析を扱います。

主な機能

  1. 01Suricataのスキャン検知ルール作成
  2. 02しきい値とアラート抑制の設定
  3. 03スキャンアラートの分析とレポート生成
  4. 04スキャンと後続の攻撃兆候の相関分析

活用シーン

  1. 01ネットワークスキャンを伴うインシデント調査
  2. 02検知ルールと脅威ハンティングクエリの作成
  3. 03スキャン監視範囲の検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mukul975/anthropic-cybersecurity-skills --skill detecting-network-scanning-with-ids-signatures

インストール後、Claude Code で/detecting-network-scanning-with-ids-signaturesを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

LICENSE
references/
api-reference.md
scripts/
agent.py

詳細

IDS シグネチャによるネットワークスキャンの検出

概要

ネットワークスキャンは通常、攻撃の最初の段階であり、攻撃者は Nmap、Masscan、ZMap、カスタムスキャナーなどのツールを使って、稼働中のホスト、開いているポート、実行中のサービス、OS のバージョンを列挙します。この偵察活動を検出することで、攻撃の可能性を早期に警告できます。Suricata や Snort などの IDS/IPS システムは、シグネチャベースの検出(既知のスキャナーのパケットパターンとの照合)、しきい値ベースの検出(一定時間内の接続試行回数の計測)、異常検出(通常とは異なるトラフィックパターンの特定)によってスキャンを識別できます。このスキルでは、スキャン検出用の IDS シグネチャの作成と導入、しきい値ベースのアラート設定、スキャン活動と後続の攻撃指標との相関分析について説明します。

使用する場面

  • IDS シグネチャによるネットワークスキャンの検出が必要なセキュリティインシデントを調査するとき
  • この分野の検出ルールや脅威ハンティングクエリを作成するとき
  • SOC アナリストがこの種の分析のための体系的な手順を必要とするとき
  • 関連する攻撃手法に対するセキュリティ監視の範囲を検証するとき

前提条件

  • IDS/IPS モードで導入された Suricata 7.0+ または Snort 3.0+
  • トラフィックを可視化するためのネットワーク TAP または SPAN ポート
  • Emerging Threats ルールセットが有効になっていること
  • アラート分析用のログ基盤(ELK Stack、Splunk)
  • 通常のネットワークトラフィックパターンに関する基礎知識

基本概念

スキャン手法と検出指標
スキャンの種類Nmap フラグパケットの特徴検出方法
TCP SYN-sSSYN フラグのみ、ハンドシェイクを完了しないSYN に対する SYN/ACK 応答パターンがない
TCP Connect-sT3 ウェイハンドシェイクを完了する単一の送信元からの複数の接続
TCP FIN-sFFIN フラグのみ閉じたポートへの FIN(RST 応答)
TCP Xmas-sXFIN+PSH+URG フラグ通常とは異なるフラグの組み合わせ
TCP NULL-sNフラグなしフラグが設定されていない TCP パケット
UDP スキャン-sU多数のポートへの UDPICMP ポート到達不能応答
ACK スキャン-sAACK フラグのみ(ファイアウォールの調査)要求されていない ACK パケット
SYN/ACK スキャンカスタム先行する SYN なしの SYN+ACK状態違反
OS フィンガープリント-O通常とは異なる TCP オプション/ウィンドウサイズ特定のオプションの組み合わせ
バージョン検出-sVサービスプローブ文字列既知のプローブペイロード
Nmap のタイミングテンプレート
テンプレートNmap フラグ速度検出難易度
Paranoid-T05 分に 1 プローブ非常に難しい
Sneaky-T115 秒に 1 プローブ難しい
Polite-T20.4 秒に 1 プローブ中程度
Normal-T3デフォルトの並列処理簡単
Aggressive-T4並列処理、タイムアウト 1.25 秒非常に簡単
Insane-T5最大並列処理極めて簡単

ワークフロー

ステップ 1:Suricata スキャン検出ルールを導入する

/var/lib/suricata/rules/scan-detection.rules を作成します。

# === TCP Scan Detection ===

# Detect TCP SYN scan (high volume SYN without completion)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN TCP SYN Scan Detected"; flags:S,12; threshold:type both,track by_src,count 30,seconds 10; classtype:attempted-recon; sid:5000001; rev:2;)

# Detect TCP FIN scan
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN TCP FIN Scan"; flags:F,12; threshold:type both,track by_src,count 20,seconds 60; classtype:attempted-recon; sid:5000002; rev:1;)

# Detect TCP Xmas scan (FIN+PSH+URG)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN TCP Xmas Tree Scan"; flags:FPU,12; classtype:attempted-recon; sid:5000003; rev:1;)

# Detect TCP NULL scan (no flags)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN TCP NULL Scan"; flags:0,12; classtype:attempted-recon; sid:5000004; rev:1;)

# Detect ACK scan (firewall probing)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN TCP ACK Scan"; flags:A,12; flow:stateless; threshold:type both,track by_src,count 50,seconds 30; classtype:attempted-recon; sid:5000005; rev:1;)

# Detect SYN+ACK scan (unusual stateless probe)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN TCP SYN-ACK Scan"; flags:SA,12; flow:stateless; threshold:type both,track by_src,count 30,seconds 30; classtype:attempted-recon; sid:5000006; rev:1;)

# === UDP Scan Detection ===

# Detect UDP port scan
alert udp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN UDP Port Scan"; threshold:type both,track by_src,count 30,seconds 10; classtype:attempted-recon; sid:5000010; rev:1;)

# === Nmap Specific Detection ===

# Detect Nmap OS fingerprinting (T1 probe - ECN SYN)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN Nmap OS Fingerprint Probe"; flags:SEC,12; window:1; classtype:attempted-recon; sid:5000020; rev:1;)

# Detect Nmap window scan (specific window size patterns)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN Nmap Window Size Probe"; flags:A,12; flow:stateless; window:1024; threshold:type both,track by_src,count 10,seconds 30; classtype:attempted-recon; sid:5000021; rev:1;)

# Detect Nmap version detection probes
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN Nmap Service Version Probe"; flow:established; content:"HELP"; depth:4; threshold:type both,track by_src,count 5,seconds 60; classtype:attempted-recon; sid:5000022; rev:1;)

# Detect Nmap scripting engine (NSE)
alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN Nmap NSE HTTP Script"; http.user_agent; content:"Nmap Scripting Engine"; classtype:attempted-recon; sid:5000023; rev:1;)

# === Masscan Detection ===

# Detect Masscan SYN scan (specific window size)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN Masscan SYN Scan Detected"; flags:S,12; window:1024; threshold:type both,track by_src,count 100,seconds 10; classtype:attempted-recon; sid:5000030; rev:1;)

# === Internal Scan Detection ===

# Detect internal host scanning (lateral movement recon)
alert tcp $HOME_NET any -> $HOME_NET any (msg:"SCAN Internal Network Scan Detected"; flags:S,12; threshold:type both,track by_src,count 50,seconds 30; classtype:attempted-recon; sid:5000040; rev:1;)

# Detect internal ICMP sweep
alert icmp $HOME_NET any -> $HOME_NET any (msg:"SCAN Internal ICMP Sweep"; itype:8; threshold:type both,track by_src,count 30,seconds 10; classtype:attempted-recon; sid:5000041; rev:1;)
ステップ 2:しきい値ベースの検出を設定する

/etc/suricata/threshold.config を編集します。

# Suppress scan alerts from authorized vulnerability scanners
suppress gen_id 1, sig_id 5000001, track by_src, ip 10.0.5.100
suppress gen_id 1, sig_id 5000001, track by_src, ip 10.0.5.101

# Rate-limit scan alerts to prevent log flooding
rate_filter gen_id 1, sig_id 5000001, track by_src, count 5, seconds 300, new_action alert, timeout 600
rate_filter gen_id 1, sig_id 5000040, track by_src, count 3, seconds 300, new_action alert, timeout 600

# Event filter for critical internal scans
event_filter gen_id 1, sig_id 5000040, type both, track by_src, count 1, seconds 60
ステップ 3:スキャン検出分析スクリプト
#!/usr/bin/env python3
"""Analyze IDS alerts for network scanning activity and generate reports."""

import json
import sys
from collections import defaultdict
from datetime import datetime


class ScanDetector:
    """Correlate IDS alerts to identify scanning campaigns."""

    def __init__(self):
        self.scan_events = defaultdict(lambda: {
            'source_ip': '',
            'target_ips': set(),
            'target_ports': set(),
            'scan_types': set(),
            'alert_count': 0,
            'first_seen': None,
            'last_seen': None,
            'signatures': defaultdict(int),
        })

    def process_eve_json(self, filepath: str):
        """Process Suricata EVE JSON alert log."""
        with open(filepath, 'r') as f:
            for line in f:
                try:
                    event = json.loads(line)
                    if event.get('event_type') != 'alert':
                        continue

                    alert = event.get('alert', {})
                    sig = alert.get('signature', '')

                    if 'SCAN' not in sig:
                        continue

                    src_ip = event.get('src_ip', '')
                    dst_ip = event.get('dest_ip', '')
                    dst_port = event.get('dest_port', 0)
                    ts = datetime.fromisoformat(
                        event['timestamp'].replace('Z', '+00:00')
                    )

                    scanner = self.scan_events[src_ip]
                    scanner['source_ip'] = src_ip
                    scanner['target_ips'].add(dst_ip)
                    scanner['target_ports'].add(dst_port)
                    scanner['alert_count'] += 1
                    scanner['signatures'][sig] += 1

                    if 'SYN' in sig:
                        scanner['scan_types'].add('SYN Scan')
                    elif 'FIN' in sig:
                        scanner['scan_types'].add('FIN Scan')
                    elif 'Xmas' in sig:
                        scanner['scan_types'].add('Xmas Scan')
                    elif 'NULL' in sig:
                        scanner['scan_types'].add('NULL Scan')
                    elif 'UDP' in sig:
                        scanner['scan_types'].add('UDP Scan')
                    elif 'Nmap' in sig:
                        scanner['scan_types'].add('Nmap Detected')
                    elif 'Masscan' in sig:
                        scanner['scan_types'].add('Masscan Detected')
                    elif 'Internal' in sig:
                        scanner['scan_types'].add('Internal Scan')

                    if scanner['first_seen'] is None or ts < scanner['first_seen']:
                        scanner['first_seen'] = ts
                    if scanner['last_seen'] is None or ts > scanner['last_seen']:
                        scanner['last_seen'] = ts

                except (json.JSONDecodeError, KeyError, ValueError):
                    continue

    def generate_report(self):
        """Generate scan detection report."""
        scanners = sorted(
            self.scan_events.values(),
            key=lambda x: x['alert_count'],
            reverse=True
        )

        print(f"\n{'='*70}")
        print("NETWORK SCAN DETECTION REPORT")
        print(f"{'='*70}")
        print(f"Unique Scanning Sources: {len(scanners)}\n")

        for scanner in scanners:
            targets = len(scanner['target_ips'])
            ports = len(scanner['target_ports'])
            duration = (scanner['last_seen'] - scanner['first_seen']).total_seconds() \
                if scanner['first_seen'] and scanner['last_seen'] else 0

            is_internal = scanner['source_ip'].startswith(('10.', '172.', '192.168.'))
            severity = "CRITICAL" if is_internal else \
                       "HIGH" if targets > 50 or ports > 100 else "MEDIUM"

            print(f"[{severity}] Scanner: {scanner['source_ip']}")
            print(f"  Type: {'INTERNAL' if is_internal else 'EXTERNAL'}")
            print(f"  Scan Types: {', '.join(scanner['scan_types'])}")
            print(f"  Target Hosts: {targets}, Target Ports: {ports}")
            print(f"  Total Alerts: {scanner['alert_count']}")
            print(f"  Duration: {duration:.0f}s")
            print(f"  First Seen: {scanner['first_seen']}")
            print(f"  Top Signatures:")
            for sig, count in sorted(
                scanner['signatures'].items(), key=lambda x: x[1], reverse=True
            )[:5]:
                print(f"    - {sig}: {count}")
            print()


if __name__ == '__main__':
    detector = ScanDetector()
    log_file = sys.argv[1] if len(sys.argv) > 1 else '/var/log/suricata/eve.json'
    detector.process_eve_json(log_file)
    detector.generate_report()

対応手順

  1. トリアージ - スキャンが許可されたスキャナーからのものか、未知の送信元からのものかを判断します
  2. 情報を補完 - 脅威インテリジェンスフィードで送信元 IP を検索します
  3. 範囲を評価 - スキャンの広さを把握するため、対象の数とポート数を数えます
  4. ブロック - 侵入性の高い外部スキャナーをファイアウォールのブロックリストに追加します
  5. 内部を調査 - 内部スキャンはホストの侵害を示している可能性があります。ホストを隔離して調査します
  6. 相関分析 - スキャンの後にエクスプロイトの試行があったか確認します

ベストプラクティス

  • 許可されたスキャナーをホワイトリストに登録 - 既知の脆弱性スキャナーの IP からのアラートを抑制します
  • 内部スキャンを重視 - 内部スキャンは外部スキャンより重大度が高くなります(侵害を示すため)
  • しきい値の調整 - 環境に応じてしきい値を調整します。/16 ネットワークではスキャンノイズが増加します
  • 他のアラートと相関分析 - スキャン検知とエクスプロイトのアラートを組み合わせ、キルチェーンを把握します
  • 時間帯に基づく分析 - 通常と異なる時間帯(午前 3 時)のスキャンは、優先度を高くする必要があります
  • アラートのレート制限 - スキャンの集中によって SIEM がノイズに埋もれるのを防ぎます

参考資料

コメント