0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MAC重複チェック

Posted at

 2. パッシブ監視 Pythonコード(ARPフラッピング検出)

既存のサーバや PC 上で、「ARP を眺めて IP/MAC の変化を検出したい」なら、
scapy で数十行のスクリプトを書けば実現できます。

前提

OS:Linux or macOS(Windows でも WSL や権限周りを調整すれば可)

pip install scapy

root 権限で実行(パケットキャプチャのため)

コード例:ARP の IP→MAC 変化を監視

from scapy.all import sniff, ARP
from collections import defaultdict
import time

# 現在認識している IP -> MAC
ip_to_mac = {}

# IPごとの変更履歴(タイムスタンプとMAC)
change_history = defaultdict(list)

# フラッピング判定のしきい値
WINDOW_SEC = 60      # 何秒間を見るか
FLAP_COUNT = 3       # この回数以上変わったら「バタバタ」とみなす

def handle_arp(pkt):
    if ARP not in pkt:
        return

    arp = pkt[ARP]

    # ARP Reply (is-at) のみ見る
    if arp.op != 2:
        return

    ip = arp.psrc
    mac = arp.hwsrc.lower()
    now = time.time()

    if ip not in ip_to_mac:
        ip_to_mac[ip] = mac
        print(f"[{time.strftime('%H:%M:%S')}] 初回観測: {ip} -> {mac}")
        return

    if ip_to_mac[ip] != mac:
        old_mac = ip_to_mac[ip]
        ip_to_mac[ip] = mac
        change_history[ip].append((now, mac))

        print(f"[{time.strftime('%H:%M:%S')}] IP {ip} のMACが変化: {old_mac} -> {mac}")

        # 直近 WINDOW_SEC 秒以内の変更回数を確認
        recent_changes = [
            t for (t, _) in change_history[ip]
            if now - t <= WINDOW_SEC
        ]
        if len(recent_changes) >= FLAP_COUNT:
            print(f"  !!! IP {ip} でMACフラッピング検知: "
                  f"{WINDOW_SEC}秒以内に {len(recent_changes)} 回変更")

if __name__ == "__main__":
    # sniff(filter="arp", iface="eth0", prn=handle_arp, store=False)
    # iface を指定しない場合、全インターフェースが対象(環境依存)
    sniff(filter="arp", prn=handle_arp, store=False)

使い方イメージ

監視したい VLAN/セグメントにぶら下がっている Linux マシンで実行

しばらく放置しておく

IP 1つに対して、MAC が何度も入れ替わると MACフラッピング検知 のメッセージが出る

「arpテーブルのMACアドレスがバタバタしている」が本当に発生しているなら、
このスクリプトを流しっぱなしにすると、どの IP でどの MAC が入れ替わっているかが見えます。

 . アクティブスキャン Pythonコード(同一IPに複数MACがいないか)

重複している IP を一括チェックしたい場合は、サブネットに対して ARP を投げて
同じ IP に対して複数の MAC アドレスから応答があるか を確認します。

コード例:サブネット全体を ARP スキャンして重複チェック

from scapy.all import ARP, Ether, srp

# スキャン対象のサブネット(自分の環境に合わせて変更)
TARGET_NET = "192.168.1.0/24"

def scan_duplicates(network):
    # ブロードキャストで ARP リクエスト送信
    arp = ARP(pdst=network)
    ether = Ether(dst="ff:ff:ff:ff:ff:ff")
    packet = ether / arp

    print(f"[*] Scanning {network} ...")
    answered, _ = srp(packet, timeout=3, verbose=False)

    ip_to_macs = {}

    for snd, rcv in answered:
        ip = rcv.psrc
        mac = rcv.hwsrc.lower()
        ip_to_macs.setdefault(ip, set()).add(mac)

    duplicates = {ip: macs for ip, macs in ip_to_macs.items() if len(macs) > 1}

    if not duplicates:
        print("重複IPは検出されませんでした。")
    else:
        print("重複している可能性のあるIP:")
        for ip, macs in duplicates.items():
            print(f"  {ip}: {', '.join(macs)}")

if __name__ == "__main__":
    scan_duplicates(TARGET_NET)

ポイント

1回のスキャンでは「たまたま片方だけ応答する」ケースもあるので、
数回繰り返して結果を比較すると精度が上がります。

セグメントをまたいだ IP 重複にはもちろん無力なので、
L3 境界ごとに実行するイメージです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?