Specter is a passive WiFi monitor and lightweight wireless IDS written in Python on top of Scapy. It hops channels, fingerprints every nearby access point, and flags rogue and evil-twin APs in real time. It is meant for network administrators and security people watching the integrity of their own wireless.
The detection logic lives in a pure DetectionEngine that consumes plain Observation records, so every detector is unit-tested without a radio (see tests/).
| Detector | Signal |
|---|---|
EVIL_TWIN |
A known SSID advertised by a BSSID that is not in your allowlist |
SECURITY_DOWNGRADE |
A listed AP suddenly advertising Open or WEP where it should be WPA2/WPA3 |
PMF_DOWNGRADE |
A listed AP recorded as PMF-required seen without PMF (spoof / deauth-DoS exposure) |
SSID_CONFLICT |
The same SSID offered both secured and open by different BSSIDs |
VENDOR_MISMATCH |
A listed BSSID whose OUI vendor no longer matches |
MULTICHANNEL_BSSID |
One BSSID claiming two channels (impossible for a real AP) |
KARMA |
An AP that probe-answers many distinct SSIDs (KARMA / MANA responder) |
CSA_SPOOF |
A Channel Switch Announcement from a foreign BSSID for a known SSID (client eviction) |
CSA_OBSERVED |
A Channel Switch Announcement from any other AP (informational, LOW) |
SIGNAL_ANOMALY |
An AP whose signal deviates from its own baseline (robust MAD z-score) |
TSF_ANOMALY |
A beacon timestamp running backwards (AP reset or freshly spun clone) |
PMKID_EXPOSED |
An EAPOL M1 carrying an RSN PMKID (clientless-crackable handshake) |
DEAUTH_FLOOD |
A burst of deauth / disassoc frames (evil-twin precursor) |
BEACON_FLOOD |
A burst of new BSSIDs (mdk3-style beacon flood) |
AUTH_FLOOD |
A burst of 802.11 authentication frames at one AP (auth DoS) |
ASSOC_FLOOD |
A burst of association requests at one AP (association DoS) |
WPS_BRUTEFORCE |
Excessive EAP frames from one station to one AP (Reaver-style WPS brute) |
WEP_REPLAY |
A flood of replayed WEP data frames (aireplay-style ARP replay / IV harvesting) |
Channel from a frame is trusted when it comes from the DS Parameter Set (2.4 GHz), the HT Operation element (5 GHz), or the HE Operation element (6 GHz), so the channel hopper never makes a single AP look multi-channel. Evil twins of cloaked (hidden) SSIDs are caught once the attacker probe-responds with the real name, and the alert is tagged [cloaked].
- Real-time scanning across 2.4, 5, and 6 GHz with adaptive channel hopping.
- MAC randomization of the monitoring interface (disable with
--no-mac-spoof). - Async vendor (OUI) lookup off the capture path, with an on-disk cache, so a slow or rate-limited API never drops frames.
- Dual-interface failover.
- Alerts logged to
specter_alerts.log; rogue APs saved torogue_aps.jsonon exit, with cipher, AKM, PMF, and WPS recorded per AP. - Webhook and command alerting for HIGH/CRITICAL events (
--webhook,--alert-cmd). - Tunable detection thresholds via a JSON config (
--config). - Optional active modules (off by default): KARMA bait probing and deauth-based defense.
git clone https://github.com/UncleJ4ck/specter.git
cd specter
pip3 install -r requirements.txtspecter.py # thin launcher (sudo python3 specter.py ...)
specter/ # package
_radio.py # single guarded scapy import boundary
models.py # Observation, Alert, constants, ranking helpers (pure)
engine.py # DetectionEngine, all detectors (pure)
parsing.py # 802.11 frame -> Observation
vendor.py # async OUI lookup
interfaces.py # monitor mode, MAC spoof, channel hopping
active.py # KARMA bait, deauth defense (gated)
notify.py # webhook + command alerting
render.py # terminal UI
capture.py # packet handler, logging, persistence
cli.py # argparse + capture loop
tests/test_specter.py # hard-assertion detector tests (no radio)
The engine and models have no scapy or I/O dependency, which is what makes the detectors testable on any machine.
sudo python3 specter.py <interface> [known_networks.json] [secondary_interface] [options]
# or, equivalently:
sudo python3 -m specter <interface> [known_networks.json] [secondary_interface] [options]<interface>: primary wireless interface (required).[known_networks.json]: allowlist of your legitimate APs (optional). Without it, Specter still does evil-twin, conflict, karma, flood, and anomaly detection; with it, it also does allowlist, downgrade, and vendor checks.[secondary_interface]: failover interface (optional).
Options:
| Flag | Effect |
|---|---|
--band {2.4,5,6,all} |
Band(s) to hop. Default 2.4. Use all for dual/tri-band cards. |
--config FILE |
JSON file overriding detection thresholds (see below). |
--webhook URL |
POST each HIGH/CRITICAL alert as JSON to a URL. |
--alert-cmd CMD |
Run a command on each HIGH/CRITICAL alert. Fields are exported as SPECTER_* env vars. |
--no-mac-spoof |
Keep the real MAC on the monitor interface. |
--active-karma |
Inject bait probe requests for a random SSID to flush KARMA/MANA APs. |
--active-defense |
Transmit deauth frames at confirmed rogue APs. Asks for confirmation. |
Example:
sudo python3 specter.py wlan0 config.json wlan1 --band all \
--webhook https://hooks.example.com/specterHIGH and CRITICAL alerts are written to specter_alerts.log and, if configured, sent out of band:
--webhook URLPOSTs a JSON body{severity, type, bssid, ssid, detail, count}.--alert-cmd CMDrunsCMDwith the same fields asSPECTER_SEVERITY,SPECTER_TYPE,SPECTER_BSSID,SPECTER_SSID,SPECTER_DETAIL,SPECTER_COUNT. For email, point it at your MTA, e.g.--alert-cmd 'sh -c "printf \"%s\" \"$SPECTER_DETAIL\" | mail -s \"$SPECTER_TYPE\" you@example.com"'.
Both run on a background thread, so a slow endpoint never drops frames.
--config thresholds.json overrides any of the detection thresholds. Unspecified keys keep their defaults.
{ "deauth_flood": 15, "beacon_flood": 25, "signal_swing": 20, "karma_ssids": 4 }Keys: power_history, signal_min_samples, signal_swing, tsf_regress, karma_ssids, flood_window, deauth_flood, beacon_flood, auth_flood, assoc_flood, wps_window, wps_eap.
A JSON list of your legitimate APs. Multiple BSSIDs per SSID are fine (mesh, band steering). encryption and pmf are optional baselines that enable downgrade detection.
[
{ "ssid": "YourNetwork", "bssid": "e4:9e:12:00:00:01", "vendor": "Freebox SAS", "encryption": "wpa2", "pmf": "required" },
{ "ssid": "YourNetwork", "bssid": "e4:9e:12:00:00:02", "vendor": "Freebox SAS", "encryption": "wpa2" }
]encryption accepts a family: open, wep, wpa, wpa2, wpa3-transition, wpa3. pmf set to required raises PMF_DOWNGRADE if that AP is later seen without protected management frames.
--active-defense sends 802.11 deauthentication frames at APs Specter has confirmed as rogue. Transmitting deauth can be illegal in many jurisdictions (the FCC has issued large fines for it) and can knock legitimate nearby clients offline. Only enable it on wireless you own or are explicitly authorized to defend. It is off by default and prompts for a typed confirmation.
Two suites, both run without a wireless card:
python3 tests/test_specter.py # detector logic, hard assertions + negative controls
python3 tests/test_integration.py # real scapy frames through the capture and notify pathsThe unit suite covers every detector with a positive case and a negative control. The integration suite dissects genuine 802.11 frames (beacon, RSN, EAPOL with a PMKID KDE, CSA) through parse_packet and make_handler, and drives the webhook and --alert-cmd notifier against a real loopback HTTP server and a real subprocess. What remains hardware-only: monitor-mode setup, iw channel hopping, and over-the-air transmission (sendp).
Creating an experimental evil twin with airgeddon
Specter detecting the evil twin
Tested on:
- Ubuntu 22.04
- PopOS
- Raspberry Pi 3 Model B+
- Switching between two interfaces
- Multithreading
- MAC spoofing
- KARMA / MANA detection (passive distinct-SSID count + active bait)
- Logging
- Evil twin for unlisted SSIDs (conflict, downgrade, CSA)
- Deauth and beacon flood detection
- Auth flood, assoc flood, and WPS/EAP brute-force detection
- PMKID exposure alerting (EAPOL M1 RSN PMKID KDE)
- PMF downgrade detection (PMF-required baseline)
- Webhook and command alerting (email via
--alert-cmd+ MTA) - Tunable thresholds via
--config - 5 GHz channel via HT Operation, 6 GHz via HE Operation
- Per-AP signal classifier (robust MAD modified z-score)
- WEP ARP-replay detection (scoped to WEP data frames, not broadcast-MAC heuristics)
- Hidden-SSID (cloaked) evil-twin correlation via probe responses
- Deauthentication as a defensive mechanism (gated)
- On-air verification of monitor mode, channel hop, and deauth transmit (needs a card)

