#!/usr/bin/env python3 # For authorized penetration testing and lab environments only """Wireless Network Penetration Testing Agent - Tests WiFi security using Scapy and aircrack-ng.""" import json import logging import argparse import subprocess from datetime import datetime from scapy.all import ( Dot11, Dot11Beacon, Dot11Elt, Dot11ProbeReq, Dot11Auth, sniff, RadioTap, sendp, conf, ) logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") logger = logging.getLogger(__name__) def scan_access_points(interface, duration=30): """Scan for WiFi access points by capturing beacon frames.""" access_points = {} def beacon_handler(pkt): if pkt.haslayer(Dot11Beacon): bssid = pkt[Dot11].addr2 if bssid not in access_points: ssid = pkt[Dot11Elt].info.decode("utf-8", errors="ignore") stats = pkt[Dot11Beacon].network_stats() access_points[bssid] = { "ssid": ssid, "bssid": bssid, "channel": stats.get("channel", 0), "crypto": list(stats.get("crypto", set())), "signal": pkt.dBm_AntSignal if hasattr(pkt, "dBm_AntSignal") else None, } logger.info("Scanning for access points on %s for %ds", interface, duration) sniff(iface=interface, prn=beacon_handler, timeout=duration, store=False) logger.info("Discovered %d access points", len(access_points)) return list(access_points.values()) def detect_rogue_aps(discovered_aps, known_ssids, known_bssids): """Detect rogue access points by comparing against known infrastructure.""" rogues = [] for ap in discovered_aps: if ap["ssid"] in known_ssids and ap["bssid"] not in known_bssids: ap["rogue_reason"] = "Known SSID with unknown BSSID (potential evil twin)" rogues.append(ap) logger.warning("ROGUE AP detected: SSID=%s BSSID=%s", ap["ssid"], ap["bssid"]) return rogues def detect_weak_encryption(access_points): """Identify access points using weak or no encryption.""" weak = [] for ap in access_points: crypto = ap.get("crypto", []) if not crypto or "OPN" in crypto: ap["weakness"] = "Open network - no encryption" weak.append(ap) elif "WEP" in str(crypto): ap["weakness"] = "WEP encryption - trivially crackable" weak.append(ap) elif "WPA" in str(crypto) and "WPA2" not in str(crypto): ap["weakness"] = "WPA1 only - vulnerable to TKIP attacks" weak.append(ap) logger.info("Found %d APs with weak encryption", len(weak)) return weak def capture_handshake(interface, target_bssid, channel, output_file, duration=60): """Capture WPA2 4-way handshake using airodump-ng.""" set_channel_cmd = ["iwconfig", interface, "channel", str(channel)] subprocess.run(set_channel_cmd, capture_output=True, timeout=120) cmd = [ "airodump-ng", "--bssid", target_bssid, "--channel", str(channel), "--write", output_file, "--output-format", "pcap", interface, ] logger.info("Capturing handshake for %s on channel %d", target_bssid, channel) try: subprocess.run(cmd, timeout=duration, capture_output=True) except subprocess.TimeoutExpired: pass return f"{output_file}-01.cap" def send_deauth(interface, target_bssid, client_mac="FF:FF:FF:FF:FF:FF", count=5): """Send deauthentication frames to force client reconnection for handshake capture.""" dot11 = Dot11(addr1=client_mac, addr2=target_bssid, addr3=target_bssid) frame = RadioTap() / dot11 / Dot11Auth(algo=0, seqnum=1, status=0) sendp(frame, iface=interface, count=count, inter=0.1, verbose=False) logger.info("Sent %d deauth frames to %s (client: %s)", count, target_bssid, client_mac) def crack_handshake(cap_file, wordlist): """Attempt to crack WPA2 handshake using aircrack-ng.""" cmd = ["aircrack-ng", "-w", wordlist, "-b", "target_bssid", cap_file] result = subprocess.run(cmd, capture_output=True, text=True, timeout=3600) if "KEY FOUND" in result.stdout: key_line = [l for l in result.stdout.split("\n") if "KEY FOUND" in l] if key_line: logger.info("WPA2 key cracked: %s", key_line[0]) return {"cracked": True, "key": key_line[0]} return {"cracked": False} def detect_client_probes(interface, duration=30): """Capture probe requests to identify client-side vulnerabilities.""" probes = [] def probe_handler(pkt): if pkt.haslayer(Dot11ProbeReq): ssid = pkt[Dot11Elt].info.decode("utf-8", errors="ignore") if pkt.haslayer(Dot11Elt) else "" if ssid: probes.append({ "client_mac": pkt[Dot11].addr2, "probed_ssid": ssid, }) sniff(iface=interface, prn=probe_handler, timeout=duration, store=False) unique_clients = len(set(p["client_mac"] for p in probes)) logger.info("Captured %d probe requests from %d clients", len(probes), unique_clients) return probes def check_wps_enabled(interface, target_bssid): """Check if WPS is enabled on target AP using wash.""" cmd = ["wash", "-i", interface, "-C"] try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) if target_bssid.upper() in result.stdout.upper(): logger.warning("WPS enabled on %s - vulnerable to Reaver attack", target_bssid) return True except (subprocess.TimeoutExpired, FileNotFoundError): pass return False def generate_report(access_points, rogues, weak_crypto, client_probes): """Generate wireless penetration test report.""" report = { "timestamp": datetime.utcnow().isoformat(), "access_points": access_points, "rogue_aps": rogues, "weak_encryption": weak_crypto, "client_probes": client_probes[:50], "summary": { "total_aps": len(access_points), "rogue_aps": len(rogues), "weak_crypto_aps": len(weak_crypto), "clients_probing": len(set(p["client_mac"] for p in client_probes)), }, } print(f"WIRELESS PENTEST REPORT: {len(access_points)} APs, {len(rogues)} rogues, {len(weak_crypto)} weak") return report def main(): parser = argparse.ArgumentParser(description="Wireless Network Penetration Testing Agent") parser.add_argument("--interface", required=True, help="Wireless interface in monitor mode") parser.add_argument("--duration", type=int, default=30, help="Scan duration in seconds") parser.add_argument("--known-ssids", nargs="*", default=[], help="Known legitimate SSIDs") parser.add_argument("--known-bssids", nargs="*", default=[], help="Known legitimate BSSIDs") parser.add_argument("--output", default="wireless_pentest_report.json") args = parser.parse_args() aps = scan_access_points(args.interface, args.duration) rogues = detect_rogue_aps(aps, set(args.known_ssids), set(args.known_bssids)) weak = detect_weak_encryption(aps) probes = detect_client_probes(args.interface, args.duration) report = generate_report(aps, rogues, weak, probes) with open(args.output, "w") as f: json.dump(report, f, indent=2) logger.info("Report saved to %s", args.output) if __name__ == "__main__": main()