Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Conduct wireless network security assessments using Kismet to detect rogue access points, hidden SSIDs, weak encryption, and unauthorized clients through passive RF monitoring.
.claude/skills/performing-wireless-security-assessment-with-kismet/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | — | — |
| case-01 | ✗→✓ | ▲ Improved | — | — |
| case-07 | ✗→✓ | ▲ Improved | — | — |
| case-20 | ✗→✓ | ▲ Improved | — | — |
| case-06 | ✗→✓ | ▲ Improved | — | — |
Kismet is an open-source wireless network detector, packet sniffer, and wireless intrusion detection system (WIDS) supporting 802.11a/b/g/n/ac/ax. Unlike active scanners, Kismet operates in passive monitor mode, making it undetectable to the networks being assessed. It captures raw 802.11 frames, identifies access points, clients, probe requests, and encryption types without transmitting any packets. This skill covers deploying Kismet for comprehensive wireless security assessments, identifying rogue access points, detecting weak encryption, mapping hidden networks, and analyzing client behavior.
Kismet uses a client-server architecture:
| Frame Type | Purpose | Security Relevance | |------------|---------|-------------------| | Beacon | AP announces its presence | SSID, encryption, vendor | | Probe Request | Client searches for networks | Reveals preferred networks | | Probe Response | AP responds to client probe | Hidden SSID disclosure | | Authentication | Client authenticates to AP | Auth type identification | | Deauthentication | Disconnects client from AP | Potential attack indicator | | Association | Client joins network | Client-AP relationship |
| Encryption | Status | Risk | |------------|--------|------| | Open (No encryption) | Insecure | Critical - all traffic visible | | WEP | Broken | Critical - crackable in minutes | | WPA-TKIP | Deprecated | High - known vulnerabilities | | WPA2-PSK (CCMP) | Acceptable | Medium - depends on passphrase strength | | WPA2-Enterprise (802.1X) | Recommended | Low - certificate-based | | WPA3-SAE | Best practice | Low - resistant to offline attacks |
bash# Identify wireless interfaces iwconfig # Check adapter capabilities iw list | grep -A 10 "Supported interface modes" # Kill processes that may interfere sudo airmon-ng check kill # Enable monitor mode sudo ip link set wlan0 down sudo iw dev wlan0 set type monitor sudo ip link set wlan0 up # Verify monitor mode iw dev wlan0 info | grep type
Edit /etc/kismet/kismet.conf:
ini# Data sources source=wlan0:name=WiFi-Monitor,channel_hop=true,channel_hoprate=5/sec # Logging configuration log_types=kismet,pcapng log_prefix=/opt/kismet/logs/assessment # Enable all 802.11 channels (2.4GHz and 5GHz) channel_hop_speed=5 channel_list=IEEE80211:1,2,3,4,5,6,7,8,9,10,11,36,40,44,48,52,56,60,64,100,104,108,112,116,120,124,128,132,136,140,149,153,157,161,165 # GPS configuration (if available) gps=gpsd:host=localhost,port=2947 # Alert configuration alert=ADVCRYPTCHANGE,5/min,1/sec alert=BSSTIMESTAMP,5/min,1/sec alert=CRYPTODROP,5/min,1/sec alert=DISASSOCTRAFFIC,10/min,1/sec alert=DEAUTHFLOOD,10/min,2/sec alert=PROBENOMFP,5/min,1/sec
Launch Kismet:
bash# Start Kismet server sudo kismet -c wlan0 # Access web interface # Open browser to http://localhost:2501 # Default credentials: kismet / kismet (change immediately)
Rogue Access Point Detection:
bash# Export device list via Kismet REST API curl -u kismet:kismet http://localhost:2501/devices/summary/devices.json | \ python3 -m json.tool > all_devices.json # Filter for access points curl -u kismet:kismet \ 'http://localhost:2501/devices/summary/devices.json' \ -d 'json={"fields":["kismet.device.base.macaddr","kismet.device.base.name","kismet.device.base.type","kismet.device.base.crypt","kismet.device.base.channel","kismet.device.base.manuf","dot11.device/dot11.device.advertised_ssid_map/dot11.advertisedssid.ssid"]}' \ > access_points.json
Client Probe Analysis:
Probe requests reveal networks that clients have previously connected to, which can indicate:
Python analysis script for Kismet database:
python#!/usr/bin/env python3 """Analyze Kismet capture database for wireless security findings.""" import sqlite3 import json import sys from collections import defaultdict def analyze_kismet_db(db_path: str): """Analyze Kismet SQLite database for security issues.""" conn = sqlite3.connect(db_path) cursor = conn.cursor() findings = [] # Query all devices cursor.execute(""" SELECT devmac, type, device FROM devices """) devices = cursor.fetchall() ap_count = 0 client_count = 0 open_networks = [] wep_networks = [] wpa_tkip_networks = [] hidden_networks = [] all_aps = [] for mac, dev_type, device_json in devices: try: device = json.loads(device_json) except json.JSONDecodeError: continue base = device.get('kismet.device.base.type', '') if 'Wi-Fi AP' in base or 'Wi-Fi Device' in base: ap_count += 1 ssid_map = device.get('dot11.device', {}).get( 'dot11.device.advertised_ssid_map', [] ) crypt = device.get('kismet.device.base.crypt', '') name = device.get('kismet.device.base.name', 'Unknown') channel = device.get('kismet.device.base.channel', '') manuf = device.get('kismet.device.base.manuf', 'Unknown') ap_info = { 'mac': mac, 'ssid': name, 'encryption': crypt, 'channel': channel, 'manufacturer': manuf, } all_aps.append(ap_info) if 'None' in crypt or crypt == '': open_networks.append(ap_info) elif 'WEP' in crypt: wep_networks.append(ap_info) elif 'WPA+TKIP' in crypt and 'AES' not in crypt: wpa_tkip_networks.append(ap_info) for ssid_entry in ssid_map: if isinstance(ssid_entry, dict): ssid = ssid_entry.get('dot11.advertisedssid.ssid', '') if ssid == '' or ssid is None: hidden_networks.append(ap_info) elif 'Wi-Fi Client' in base: client_count += 1 # Generate findings print(f"\n{'='*70}") print("WIRELESS SECURITY ASSESSMENT REPORT") print(f"{'='*70}") print(f"\nTotal Access Points Detected: {ap_count}") print(f"Total Clients Detected: {client_count}") if open_networks: print(f"\n[CRITICAL] Open Networks (No Encryption): {len(open_networks)}") for net in open_networks: print(f" - SSID: {net['ssid']}, MAC: {net['mac']}, " f"Channel: {net['channel']}, Vendor: {net['manufacturer']}") if wep_networks: print(f"\n[CRITICAL] WEP-Encrypted Networks: {len(wep_networks)}") for net in wep_networks: print(f" - SSID: {net['ssid']}, MAC: {net['mac']}, " f"Channel: {net['channel']}") if wpa_tkip_networks: print(f"\n[HIGH] WPA-TKIP Networks (Deprecated): {len(wpa_tkip_networks)}") for net in wpa_tkip_networks: print(f" - SSID: {net['ssid']}, MAC: {net['mac']}, " f"Channel: {net['channel']}") if hidden_networks: print(f"\n[MEDIUM] Hidden SSIDs Detected: {len(hidden_networks)}") for net in hidden_networks: print(f" - MAC: {net['mac']}, Channel: {net['channel']}, " f"Vendor: {net['manufacturer']}") # Channel utilization analysis channel_usage = defaultdict(int) for ap in all_aps: ch = ap.get('channel', 'Unknown') channel_usage[ch] += 1 print(f"\n[INFO] Channel Utilization:") for ch, count in sorted(channel_usage.items()): print(f" Channel {ch}: {count} APs") conn.close() if __name__ == '__main__': db_path = sys.argv[1] if len(sys.argv) > 1 else 'Kismet-*.kismet' analyze_kismet_db(db_path)
Compare discovered APs against authorized inventory:
python#!/usr/bin/env python3 """Detect rogue access points by comparing against authorized AP list.""" import json import sys def load_authorized_aps(filepath: str) -> set: """Load authorized AP MAC addresses from file.""" authorized = set() with open(filepath, 'r') as f: for line in f: mac = line.strip().lower() if mac and not mac.startswith('#'): authorized.add(mac) return authorized def detect_rogues(kismet_json: str, authorized_file: str): """Compare discovered APs against authorized list.""" authorized = load_authorized_aps(authorized_file) with open(kismet_json, 'r') as f: devices = json.load(f) rogues = [] for device in devices: mac = device.get('kismet.device.base.macaddr', '').lower() dev_type = device.get('kismet.device.base.type', '') if 'AP' in dev_type and mac not in authorized: rogues.append({ 'mac': mac, 'ssid': device.get('kismet.device.base.name', 'Unknown'), 'encryption': device.get('kismet.device.base.crypt', ''), 'channel': device.get('kismet.device.base.channel', ''), 'manufacturer': device.get('kismet.device.base.manuf', ''), 'signal': device.get('kismet.device.base.signal', {}).get( 'kismet.common.signal.last_signal', 0), }) if rogues: print(f"\n[ALERT] {len(rogues)} ROGUE ACCESS POINTS DETECTED\n") for rogue in rogues: print(f" MAC: {rogue['mac']}") print(f" SSID: {rogue['ssid']}") print(f" Encryption: {rogue['encryption']}") print(f" Channel: {rogue['channel']}") print(f" Vendor: {rogue['manufacturer']}") print(f" Signal: {rogue['signal']} dBm") print() else: print("No rogue access points detected.") if __name__ == '__main__': if len(sys.argv) < 3: print("Usage: python detect_rogues.py <kismet_devices.json> <authorized_aps.txt>") sys.exit(1) detect_rogues(sys.argv[1], sys.argv[2])
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-10 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.