mcpField
D
detecting-arp-poisoning-in-network-traffic

detecting-arp-poisoning-in-network-traffic

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

detecting-arp-poisoning-in-network-traffic について

IPとMACの対応関係の変化やGratuitous ARPなどの異常からARPポイズニングを検出するネットワークセキュリティスキルです。ARPWatch、スイッチのDynamic ARP Inspection、パケットキャプチャ、独自のPythonツールによる監視と分析を扱います。

主な機能

  1. 01ARPWatchによるARPマッピング監視
  2. 02Dynamic ARP Inspectionの適用
  3. 03Wiresharkフィルターでのパケット分析
  4. 04独自のPython ARP監視ツールの作成
  5. 05代表的なポイズニング兆候の把握

活用シーン

  1. 01ARPポイズニングの疑いの調査
  2. 02ネットワーク脅威ハンティングクエリの作成
  3. 03ARP異常の監視範囲の検証
  4. 04レイヤー2防御の見直し

インストール

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

$npx skills add https://github.com/mukul975/anthropic-cybersecurity-skills --skill detecting-arp-poisoning-in-network-traffic

インストール後、Claude Code で/detecting-arp-poisoning-in-network-trafficを入力して読み込まれたか確認します。

同梱ファイル

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

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

詳細

ネットワークトラフィックにおけるARPポイズニングの検知

概要

ARPポイズニング(ARPスプーフィング)は、攻撃者が偽造したARPメッセージを送信し、自身のMACアドレスを正規ホストのIPアドレスに関連付けるレイヤー2攻撃です。これにより、中間者(MitM)による傍受、セッションハイジャック、またはサービス拒否が可能になります。ARPには組み込みの認証メカニズムがないため、ブロードキャストドメイン内のどのデバイスでもARP応答を偽造できます。検知には、Gratuitous ARPの大量送信、IPとMACの対応関係の変化、重複IPアドレスなどの異常を見つけるため、ARPトラフィックを監視する必要があります。このスキルでは、ARPWatch、Dynamic ARP Inspection(DAI)、Wiresharkベースの分析、カスタムPython監視ツールなど、複数の検知レイヤーの導入方法を説明します。

使用する場面

  • ネットワークトラフィックにおけるARPポイズニングの検知が必要なセキュリティインシデントを調査する場合
  • この領域の検知ルールや脅威ハンティングクエリを作成する場合
  • SOCアナリストが、この種の分析のための体系的な手順を必要とする場合
  • 関連する攻撃手法に対するセキュリティ監視のカバレッジを検証する場合

前提条件

  • 対象ネットワークセグメント(ブロードキャストドメイン)へのアクセス
  • ARPWatchおよびカスタム監視ツールを実行するLinuxホスト
  • Dynamic ARP Inspectionに対応したマネージドスイッチ(Cisco Catalyst、Aruba、Juniper EX)
  • パケットキャプチャ用のWiresharkまたはtcpdump
  • DHCPスヌーピングが設定済みであること(DAIの前提条件)
  • ネットワーク監視インフラストラクチャ(SIEM、syslogサーバー)

基本概念

ARPプロトコルの基礎

ARPは、ローカルネットワークセグメント上でIPアドレスをMACアドレスに対応付けます。このプロトコルはステートレスに動作し、認証機能はありません。

Normal ARP Process:
1. Host A broadcasts: "Who has 10.0.1.1? Tell 10.0.1.100"
2. Router replies: "10.0.1.1 is at AA:BB:CC:DD:EE:01"
3. Host A caches the mapping

ARP Poisoning Attack:
1. Attacker sends unsolicited ARP reply to Host A:
   "10.0.1.1 is at EV:IL:MA:CA:DD:RR" (attacker's MAC)
2. Host A updates cache, sends traffic to attacker
3. Attacker forwards to real gateway (MitM position)
攻撃の兆候
兆候説明深刻度
MACアドレスの頻繁な切り替わり同じIPアドレスが異なるMACアドレスに短時間で対応付けられる高
Gratuitous ARPの大量送信複数のホストを対象とする未要求のARP応答高
重複IPアドレス2つの異なるMACアドレスが同じIPアドレスを使用している重大
通常と異なるARPトラフィック量1秒あたりのARPパケット数の急増中
DHCP以外の送信元からのARP不明なデバイスによる静的IPアドレスの申告中
ゲートウェイのMACアドレスの変化デフォルトゲートウェイのMACアドレスが変更された重大

ワークフロー

ステップ1:継続的な監視のためにARPWatchを導入する
# Install ARPWatch
sudo apt-get install -y arpwatch

# Configure ARPWatch
sudo vi /etc/default/arpwatch
# INTERFACES="eth0"
# ARGS="-N -p -i eth0 -f /var/lib/arpwatch/arp.dat"

# Start monitoring
sudo systemctl enable arpwatch
sudo systemctl start arpwatch

# View current ARP database
cat /var/lib/arpwatch/arp.dat

# Monitor logs for changes
tail -f /var/log/syslog | grep arpwatch

ARPWatchのアラートタイプ:

  • new station - これまで確認されていないMACアドレス
  • changed ethernet address - IPアドレスが別のMACアドレスに対応付けられた(ポイズニングの可能性)
  • flip flop - MACアドレスが2つのアドレス間で交互に切り替わる(進行中の攻撃)
  • reused old ethernet address - 以前確認された対応関係が再び使用された
ステップ2:スイッチでDynamic ARP Inspection(DAI)を設定する

Cisco Catalystの設定:

! Enable DHCP snooping (prerequisite for DAI)
ip dhcp snooping
ip dhcp snooping vlan 10,20,30

! Configure trusted ports (uplinks, DHCP servers)
interface GigabitEthernet1/0/1
 description Uplink to Distribution
 ip dhcp snooping trust

interface GigabitEthernet1/0/48
 description DHCP Server
 ip dhcp snooping trust

! Enable Dynamic ARP Inspection
ip arp inspection vlan 10,20,30

! Configure trusted ports for DAI
interface GigabitEthernet1/0/1
 ip arp inspection trust

! Set rate limits to prevent ARP flood DoS
interface range GigabitEthernet1/0/2-47
 ip arp inspection limit rate 15

! Enable additional validation checks
ip arp inspection validate src-mac dst-mac ip

! Configure ARP ACL for static IP devices (servers, printers)
arp access-list STATIC-ARP-ENTRIES
 permit ip host 10.0.10.100 mac host 0011.2233.4455
 permit ip host 10.0.10.101 mac host 0011.2233.4456

ip arp inspection filter STATIC-ARP-ENTRIES vlan 10

! Verify DAI status
show ip arp inspection vlan 10
show ip arp inspection statistics
show ip dhcp snooping binding
ステップ3:Wiresharkの検知フィルター
# Detect gratuitous ARP (sender and target IP are the same)
arp.src.proto_ipv4 == arp.dst.proto_ipv4

# Detect ARP replies (focus on unsolicited)
arp.opcode == 2

# Detect duplicate IP address claims
arp.duplicate-address-detected

# Detect ARP packets from specific attacker MAC
eth.src == ev:il:ma:ca:dd:rr

# Detect ARP storms (high volume)
# Use Statistics > I/O Graphs > Display filter: arp

# Detect gateway impersonation
arp.src.proto_ipv4 == 10.0.1.1 && arp.src.hw_mac != aa:bb:cc:dd:ee:01
ステップ4:カスタムPython ARP監視ツール
#!/usr/bin/env python3
"""
Real-time ARP poisoning detection using packet capture.
Monitors ARP traffic for spoofing indicators and alerts on anomalies.
"""

import subprocess
import sys
import json
import time
from collections import defaultdict
from datetime import datetime

try:
    from scapy.all import sniff, ARP, Ether, get_if_hwaddr, conf
    SCAPY_AVAILABLE = True
except ImportError:
    SCAPY_AVAILABLE = False


class ARPPoisonDetector:
    def __init__(self, interface: str, gateway_ip: str, gateway_mac: str):
        self.interface = interface
        self.gateway_ip = gateway_ip
        self.gateway_mac = gateway_mac.lower()
        self.arp_table = {}  # IP -> MAC mapping
        self.arp_history = defaultdict(list)  # IP -> list of (MAC, timestamp)
        self.alerts = []
        self.arp_count = defaultdict(int)  # Source MAC -> count per interval
        self.last_reset = time.time()
        self.arp_rate_threshold = 50  # ARP packets per 10 seconds

    def alert(self, severity: str, message: str, details: dict):
        """Generate alert for detected anomaly."""
        alert_data = {
            'timestamp': datetime.now().isoformat(),
            'severity': severity,
            'message': message,
            'details': details,
        }
        self.alerts.append(alert_data)
        print(f"\n[{severity}] {datetime.now().strftime('%H:%M:%S')} - {message}")
        for key, value in details.items():
            print(f"  {key}: {value}")

    def check_gateway_spoofing(self, src_ip: str, src_mac: str):
        """Check if someone is spoofing the gateway."""
        if src_ip == self.gateway_ip and src_mac != self.gateway_mac:
            self.alert('CRITICAL', 'Gateway ARP Spoofing Detected', {
                'gateway_ip': self.gateway_ip,
                'expected_mac': self.gateway_mac,
                'spoofed_mac': src_mac,
                'action': 'Potential MitM attack on default gateway',
            })
            return True
        return False

    def check_mac_change(self, src_ip: str, src_mac: str):
        """Check if IP-to-MAC mapping has changed."""
        if src_ip in self.arp_table:
            known_mac = self.arp_table[src_ip]
            if known_mac != src_mac:
                self.alert('HIGH', 'ARP Cache Poisoning Attempt', {
                    'ip_address': src_ip,
                    'previous_mac': known_mac,
                    'new_mac': src_mac,
                    'action': 'IP-to-MAC mapping changed unexpectedly',
                })
                return True
        return False

    def check_flip_flop(self, src_ip: str, src_mac: str):
        """Check for MAC address flip-flopping (active attack indicator)."""
        self.arp_history[src_ip].append((src_mac, time.time()))

        # Keep only last 60 seconds of history
        cutoff = time.time() - 60
        self.arp_history[src_ip] = [
            (mac, ts) for mac, ts in self.arp_history[src_ip]
            if ts > cutoff
        ]

        unique_macs = set(mac for mac, ts in self.arp_history[src_ip])
        if len(unique_macs) > 2:
            self.alert('CRITICAL', 'ARP Flip-Flop Detected (Active Attack)', {
                'ip_address': src_ip,
                'mac_addresses': list(unique_macs),
                'changes_in_60s': len(self.arp_history[src_ip]),
            })
            return True
        return False

    def check_arp_rate(self, src_mac: str):
        """Check for ARP flood (DoS or scanning)."""
        self.arp_count[src_mac] += 1

        # Reset counters every 10 seconds
        if time.time() - self.last_reset > 10:
            for mac, count in self.arp_count.items():
                if count > self.arp_rate_threshold:
                    self.alert('MEDIUM', 'ARP Flood Detected', {
                        'source_mac': mac,
                        'arp_packets_10s': count,
                        'threshold': self.arp_rate_threshold,
                    })
            self.arp_count.clear()
            self.last_reset = time.time()

    def process_packet(self, packet):
        """Process captured ARP packet."""
        if not packet.haslayer(ARP):
            return

        arp = packet[ARP]

        # Only process ARP replies (opcode 2) and requests (opcode 1)
        if arp.op not in (1, 2):
            return

        src_ip = arp.psrc
        src_mac = arp.hwsrc.lower()

        # Run detection checks
        self.check_gateway_spoofing(src_ip, src_mac)
        self.check_mac_change(src_ip, src_mac)
        self.check_flip_flop(src_ip, src_mac)
        self.check_arp_rate(src_mac)

        # Update ARP table
        self.arp_table[src_ip] = src_mac

    def start_monitoring(self):
        """Start real-time ARP monitoring."""
        print(f"[*] Starting ARP Poison Detection on {self.interface}")
        print(f"[*] Gateway: {self.gateway_ip} ({self.gateway_mac})")
        print(f"[*] Monitoring... (Ctrl+C to stop)\n")

        if SCAPY_AVAILABLE:
            sniff(
                iface=self.interface,
                filter="arp",
                prn=self.process_packet,
                store=False,
            )
        else:
            print("[-] Scapy not available. Install with: pip install scapy")
            print("[*] Falling back to tcpdump-based monitoring...")
            self._monitor_with_tcpdump()

    def _monitor_with_tcpdump(self):
        """Fallback monitoring using tcpdump."""
        cmd = ['tcpdump', '-i', self.interface, '-l', '-n', 'arp']
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                stderr=subprocess.DEVNULL, text=True)
        try:
            for line in proc.stdout:
                parts = line.strip().split()
                if 'is-at' in parts:
                    try:
                        ip_idx = parts.index('is-at') - 1
                        mac_idx = parts.index('is-at') + 1
                        src_ip = parts[ip_idx]
                        src_mac = parts[mac_idx].lower()
                        self.check_gateway_spoofing(src_ip, src_mac)
                        self.check_mac_change(src_ip, src_mac)
                        self.arp_table[src_ip] = src_mac
                    except (IndexError, ValueError):
                        continue
        except KeyboardInterrupt:
            proc.terminate()

    def generate_report(self) -> dict:
        """Generate summary report of detected anomalies."""
        return {
            'monitoring_interface': self.interface,
            'gateway': {'ip': self.gateway_ip, 'mac': self.gateway_mac},
            'total_alerts': len(self.alerts),
            'arp_table_size': len(self.arp_table),
            'alerts': self.alerts,
        }


if __name__ == '__main__':
    if len(sys.argv) < 4:
        print("Usage: python process.py <interface> <gateway_ip> <gateway_mac>")
        print("Example: python process.py eth0 10.0.1.1 aa:bb:cc:dd:ee:01")
        sys.exit(1)

    detector = ARPPoisonDetector(
        interface=sys.argv[1],
        gateway_ip=sys.argv[2],
        gateway_mac=sys.argv[3],
    )

    try:
        detector.start_monitoring()
    except KeyboardInterrupt:
        print("\n\n[*] Monitoring stopped.")
        report = detector.generate_report()
        print(f"[*] Total alerts generated: {report['total_alerts']}")
        print(f"[*] ARP table entries: {report['arp_table_size']}")

防止策

レイヤー2の制御
  1. Dynamic ARP Inspection(DAI) - DHCPスヌーピングのバインディングテーブルと照合してARPパケットを検証します
  2. DHCPスヌーピング - 信頼できるIP・MAC・ポートの対応関係を記録したデータベースを構築します
  3. ポートセキュリティ - ポートごとのMACアドレス数を制限します
  4. プライベートVLAN - 同じVLAN内のホスト間の通信を制限します
ネットワーク制御
  1. 静的ARPエントリー - 重要なインフラストラクチャ(ゲートウェイ、DNS、DHCP)に設定します
  2. ネットワークのセグメント化 - VLANを使ってブロードキャストドメインの規模を縮小します
  3. 802.1X認証 - ネットワークへのアクセス前にデバイスを認証します
  4. 暗号化プロトコル - 傍受された場合でもデータを保護できるよう、SSH、HTTPS、TLSを使用します

ベストプラクティス

  • 多層防御 - DAI、ARPWatch、カスタム監視を組み合わせて、包括的に保護します
  • まず DHCP スヌーピングを有効化 - DAI はスヌーピングデータベースに依存するため、DAI を有効にする前に必ず DHCP スヌーピングを有効にします
  • ゲートウェイの静的 ARP - デフォルトゲートウェイの静的 ARP エントリーを重要なサーバーに設定します
  • Gratuitous ARP を監視 - 要求されていない ARP 応答に特に注意します
  • ブロードキャストドメインを小さくする - VLAN を使用して、ARP ベースの攻撃が及ぶ範囲を制限します
  • 定期的な監査 - デバイス間で ARP テーブルを定期的に比較し、異常を特定します

参考資料

コメント