4#include <esp_wifi_types.h>
5#include <freertos/FreeRTOS.h>
6#include <freertos/task.h>
7#include <freertos/ringbuf.h>
8#include <freertos/semphr.h>
13#ifndef POLITICIAN_MAX_AP_CACHE
14#define POLITICIAN_MAX_AP_CACHE 48
17#ifndef POLITICIAN_MAX_SESSIONS
18#define POLITICIAN_MAX_SESSIONS 8
21#ifndef POLITICIAN_MAX_CAPTURED
22#define POLITICIAN_MAX_CAPTURED 128
25#ifndef POLITICIAN_MAX_CHANNELS
26#define POLITICIAN_MAX_CHANNELS 50
46#define FC_TYPE_MASK 0x000C
47#define FC_SUBTYPE_MASK 0x00F0
48#define FC_TODS_MASK 0x0100
49#define FC_FROMDS_MASK 0x0200
50#define FC_TYPE_MGMT 0x0000
51#define FC_TYPE_CTRL 0x0004
52#define FC_TYPE_DATA 0x0008
53#define FC_ORDER_MASK 0x8000
55#define MGMT_SUB_ASSOC_REQ 0x00
56#define MGMT_SUB_ASSOC_RESP 0x10
57#define MGMT_SUB_PROBE_REQ 0x40
58#define MGMT_SUB_PROBE_RESP 0x50
59#define MGMT_SUB_BEACON 0x80
60#define MGMT_SUB_AUTH 0xB0
61#define MGMT_SUB_DISASSOC 0xA0
62#define MGMT_SUB_DEAUTH 0xC0
65#define EAPOL_LLC_OFFSET 0
66#define EAPOL_ETHERTYPE_HI 0x88
67#define EAPOL_ETHERTYPE_LO 0x8E
68#define EAPOL_LLC_SIZE 8
69#define EAPOL_MIN_FRAME_LEN (EAPOL_LLC_SIZE + 4)
71#define EAPOL_KEY_DESC_TYPE 0
72#define EAPOL_KEY_INFO 1
73#define EAPOL_REPLAY_COUNTER 5
74#define EAPOL_KEY_NONCE 13
75#define EAPOL_KEY_MIC 77
76#define EAPOL_KEY_DATA_LEN 93
77#define EAPOL_KEY_DATA 95
79#define KEYINFO_TYPE_MASK 0x0007
80#define KEYINFO_PAIRWISE 0x0008
81#define KEYINFO_ACK 0x0080
82#define KEYINFO_MIC 0x0100
83#define KEYINFO_SECURE 0x0200
84#define KEYINFO_INSTALL 0x0040
120 void setIgnoreList(
const uint8_t (*bssids)[6], uint8_t count);
284 bool getClient(
const uint8_t *bssid,
int idx, uint8_t out_sta[6])
const;
286 using _FpHookCb = void (*)(
const uint8_t *mac,
const char *ssid, uint8_t ssid_len, uint8_t ch, int8_t rssi,
const uint8_t *ie, uint16_t ie_len);
289#ifndef POLITICIAN_NO_STD_FUNCTION
294 using PacketCb = std::function<void(
const uint8_t *payload, uint16_t len, int8_t rssi, uint8_t channel, uint32_t ts_usec)>;
299 using ClientFoundCb = std::function<void(
const uint8_t *bssid,
const uint8_t *sta, int8_t rssi)>;
305 using PacketCb = void (*)(
const uint8_t *payload, uint16_t len, int8_t rssi, uint8_t channel, uint32_t ts_usec);
310 using ClientFoundCb = void (*)(
const uint8_t *bssid,
const uint8_t *sta, int8_t rssi);
318 static const char*
getVendor(
const uint8_t *mac);
334 Error injectCustomFrame(
const uint8_t *payload,
size_t len, uint8_t channel, uint32_t lock_ms = 0,
bool wait_for_channel =
false);
392 static void IRAM_ATTR _promiscuousCb(
void *buf, wifi_promiscuous_pkt_type_t type);
393 static void _workerTask(
void *pvParameters);
396 RingbufHandle_t _rb =
nullptr;
397 TaskHandle_t _task =
nullptr;
398 SemaphoreHandle_t _lock =
nullptr;
400 void _handleFrame(
const wifi_promiscuous_pkt_t *pkt, wifi_promiscuous_pkt_type_t type);
401 void _handleMgmt(
const ieee80211_hdr_t *hdr,
const uint8_t *payload, uint16_t len, int8_t rssi);
402 void _handleData(
const ieee80211_hdr_t *hdr,
const uint8_t *payload, uint16_t len, int8_t rssi);
403 bool _parseEapol(
const uint8_t *bssid,
const uint8_t *sta,
404 const uint8_t *eapol, uint16_t len, int8_t rssi);
405 void _parseEapIdentity(
const uint8_t *bssid,
const uint8_t *sta,
406 const uint8_t *eapol, uint16_t len, int8_t rssi);
407 void _parseSsid(
const uint8_t *ie, uint16_t ie_len,
char *out, uint8_t &out_len);
408 uint8_t _classifyEnc(
const uint8_t *ie, uint16_t ie_len);
409 bool _detectWpa3Only(
const uint8_t *ie, uint16_t ie_len);
410 void _detectPmfFlags(
const uint8_t *ie, uint16_t ie_len,
bool &pmf_capable,
bool &pmf_required);
411 bool _detectFt(
const uint8_t *ie, uint16_t ie_len);
413 bool _initialized =
false;
414 volatile bool _active;
418 volatile bool _channelTrafficSeen;
424 uint32_t _csaFallbackMs;
426 static const int MAX_INJECT_QUEUE = 4;
429 uint8_t payload[256];
434 InjectFrame _injectQueue[MAX_INJECT_QUEUE];
436 static const int MAX_ATTACK_OVERRIDES = 8;
437 struct AttackOverride {
bool active; uint8_t bssid[6]; uint8_t mask; };
438 AttackOverride _attackOverrides[MAX_ATTACK_OVERRIDES];
439 uint8_t _getAttackMask(
const uint8_t *bssid)
const;
442 uint8_t _targetBssid[6];
443 uint8_t _targetChannel;
446 uint32_t _m1LockEndMs;
449 uint32_t _probeLockEndMs;
452 uint8_t _customChannelCount;
456 bool _autoTarget =
false;
457 bool _autoTargetActive =
false;
459 uint8_t _lastCapBssid[6] = {};
460 uint8_t _lastCapSta[6] = {};
461 uint32_t _lastCapMs = 0;
464 LogCb _logCb =
nullptr;
477 void _log(
const char *fmt, ...);
479 static const int MAX_IGNORE = 128;
480 uint8_t _ignoreList[MAX_IGNORE][6];
481 uint8_t _ignoreCount;
484 struct ApCacheEntry {
491 uint32_t first_seen_ms;
492 uint32_t last_seen_ms;
493 uint32_t last_probe_ms;
494 uint32_t last_stimulate_ms;
495 uint32_t last_hidden_probe_ms;
496 uint8_t known_stas[4][6];
497 uint8_t known_sta_count;
498 uint16_t beacon_count;
499 uint8_t total_attempts;
501 uint16_t beacon_interval;
502 uint8_t max_rate_mbps;
507 uint8_t network_type;
510 uint8_t has_active_clients : 1;
511 uint8_t is_wpa3_only : 1;
512 uint8_t is_hidden : 1;
513 uint8_t wps_enabled : 1;
514 uint8_t pmf_capable : 1;
515 uint8_t pmf_required : 1;
516 uint8_t ft_capable : 1;
519 ApCacheEntry _apCache[MAX_AP_CACHE];
521 ApCacheEntry* _cacheAp(
const uint8_t *bssid,
const char *ssid, uint8_t ssid_len,
522 uint8_t enc, uint8_t channel, int8_t rssi,
523 bool is_wpa3_only =
false,
bool wps =
false,
524 bool pmf_capable =
false,
bool pmf_required =
false,
525 bool ft_capable =
false, uint16_t sta_count = 0, uint8_t chan_util = 0,
526 uint8_t venue_group = 0, uint8_t venue_type = 0, uint8_t network_type = 0);
527 bool _lookupSsid(
const uint8_t *bssid,
char *out_ssid, uint8_t &out_len);
528 bool _lookupEnc(
const uint8_t *bssid, uint8_t &out_enc);
530 enum FishState : uint8_t { FISH_IDLE = 0, FISH_CONNECTING = 1, FISH_CSA_WAIT = 2 };
531 FishState _fishState;
532 uint32_t _fishStartMs;
533 uint8_t _fishBssid[6];
536 uint8_t _fishSsidLen;
537 uint8_t _fishChannel;
539 uint8_t _ownStaMac[6];
540 bool _fishAuthLogged;
541 bool _fishAssocLogged;
542 bool _csaSecondBurstSent;
544 void _startFishing(
const uint8_t *bssid,
const char *ssid,
545 uint8_t ssid_len, uint8_t channel);
546 void _processFishing();
547 void _randomizeMac();
548 void _sendCsaBurst();
549 void _sendDeauthBurst(uint8_t count,
const uint8_t *sta =
nullptr);
550 void _sendProbeRequest(
const uint8_t *bssid);
551 void _recordClientForAp(
const uint8_t *bssid,
const uint8_t *sta, int8_t rssi = 0);
552 void _markCapturedSsidGroup(
const char *ssid, uint8_t ssid_len);
553 void _markCaptured(
const uint8_t *bssid);
565 uint8_t m1_replay_counter[8];
570 uint8_t eapol_buffer[400];
571 uint16_t m2_off, m2_len;
572 uint16_t m3_off, m3_len;
573 uint16_t m4_off, m4_len;
583 Session _sessions[MAX_SESSIONS];
585 Session* _findSession(
const uint8_t *bssid,
const uint8_t *sta);
586 Session* _createSession(
const uint8_t *bssid,
const uint8_t *sta);
587 void _expireSessions(uint32_t timeoutMs);
590 uint8_t _captured[MAX_CAPTURED][6];
593 bool _isCaptured(
const uint8_t *bssid)
const;
595 static const uint8_t HOP_SEQ[];
596 static const uint8_t HOP_COUNT;
#define POLITICIAN_MAX_CHANNELS
#define POLITICIAN_MAX_AP_CACHE
#define POLITICIAN_MAX_SESSIONS
#define POLITICIAN_MAX_CAPTURED
The core WiFi handshake capturing engine.
void stop()
Full engine teardown.
Error lockChannel(uint8_t ch)
Stops hopping and locks the radio to a specific channel.
void clearCapturedList()
Clears the captured BSSID list.
void markCaptured(const uint8_t *bssid)
Manually adds a BSSID to the "already captured" list to skip it.
static const char * getVendor(const uint8_t *mac)
Looks up the vendor name for a given MAC address (OUI).
void setDisruptCallback(DisruptCb cb)
Sets the callback fired on deauthentication and disassociation frames.
Error injectCustomFrame(const uint8_t *payload, size_t len, uint8_t channel, uint32_t lock_ms=0, bool wait_for_channel=false)
Injects a custom 802.11 frame.
bool getClient(const uint8_t *bssid, int idx, uint8_t out_sta[6]) const
Reads a client MAC from the per-AP client table.
void clearAttackMaskOverrides()
Clears all per-BSSID attack mask overrides.
void setAutoTarget(bool enable)
Continuously locks onto the strongest uncaptured AP in the cache.
int getClientCount(const uint8_t *bssid) const
Returns the number of unique clients seen associated to a given AP.
void resetStats()
Resets all frame and capture statistics to zero.
void tick()
Main worker method.
int8_t getLastRssi() const
void setProbeRequestCallback(ProbeRequestCb cb)
Sets the callback fired on every probe request frame.
void setPacketLogger(PacketCb cb)
Sets the callback for raw promiscuous mode packets.
std::function< void(const ProbeRequestRecord &rec)> ProbeRequestCb
Error setTargetBySsid(const char *ssid)
Searches the AP cache by SSID and locks onto the strongest match.
void setLogger(LogCb cb)
Sets a custom logging callback to intercept library output.
void(*)(const uint8_t *mac, const char *ssid, uint8_t ssid_len, uint8_t ch, int8_t rssi, const uint8_t *ie, uint16_t ie_len) _FpHookCb
void setActive(bool active)
Enables or disables frame processing.
void setChannelBands(bool ghz24, bool ghz5)
Restricts hopping to 2.4GHz, 5GHz, or both bands.
std::function< void(const uint8_t *bssid, const uint8_t *sta, int8_t rssi)> ClientFoundCb
void setRogueApCallback(RogueApCb cb)
Sets the callback fired when a potential evil twin or rogue AP is detected.
void setApFoundCallback(ApFoundCb cb)
Sets the callback for when a new Access Point is discovered.
std::function< void(const HandshakeRecord &rec)> EapolCb
uint8_t getChannel() const
std::function< void(const ApRecord &ap)> ApFoundCb
void setDisconnectionStrategy(DisconnectStrategy strategy)
Configures how the engine handles disconnection when both CSA and Deauth are enabled.
void _setFingerprintHook(_FpHookCb cb)
std::function< void(const DisruptRecord &rec)> DisruptCb
void stopHopping()
Stops autonomous channel hopping and goes idle.
void setClientFoundCallback(ClientFoundCb cb)
Sets the callback fired when a new client (STA) is first seen associated to an AP.
void setAttackMaskForBssid(const uint8_t *bssid, uint8_t mask)
Overrides the attack mask for a specific BSSID.
std::function< void(const EapIdentityRecord &rec)> IdentityCb
void setTargetFilter(TargetFilterCb cb)
Sets an early filter callback.
std::function< int(const ApRecord &ap, const char *vendor)> TargetScoreCb
void setTargetScoreCallback(TargetScoreCb cb)
Sets the callback for calculating a custom priority score during autoTarget.
void setIgnoreList(const uint8_t(*bssids)[6], uint8_t count)
Sets a list of BSSIDs that should always be ignored by the engine.
void setEapolCallback(EapolCb cb)
Sets the callback for when a handshake (EAPOL or PMKID) is captured.
Error setTarget(const uint8_t *bssid, uint8_t channel)
Focuses the engine on a single BSSID.
void setIdentityCallback(IdentityCb cb)
Sets the callback for passive 802.1X Enterprise Identity harvesting.
Error setChannel(uint8_t ch)
Manually sets the WiFi radio to a specific channel.
bool getApByBssid(const uint8_t *bssid, ApRecord &out) const
Looks up an AP in the discovery cache by BSSID.
void clearTarget()
Clears the specific target and resumes autonomous wardriving.
bool getAp(int idx, ApRecord &out) const
Reads an AP from the discovery cache by index.
Error begin(const Config &cfg=Config())
Initializes the WiFi driver in promiscuous mode.
std::function< void(const AttackResultRecord &rec)> AttackResultCb
void startHopping(uint16_t dwellMs=0)
Starts autonomous channel hopping.
void setAttackResultCallback(AttackResultCb cb)
Sets the callback fired when an attack attempt exhausts all options without capturing.
void setAttackMask(uint8_t mask)
Configures which attack techniques are enabled globally.
void setChannelList(const uint8_t *channels, uint8_t count)
Restricts hopping to a specific list of channels.
std::function< void(const uint8_t *payload, uint16_t len, int8_t rssi, uint8_t channel, uint32_t ts_usec)> PacketCb
std::function< bool(const ApRecord &ap)> TargetFilterCb
std::function< void(const char *msg)> LogCb
std::function< void(const RogueApRecord &rec)> RogueApCb
Snapshot of a discovered Access Point from the internal cache.
Identifies the AP and failure reason for a failed attack, delivered to the AttackResultCb callback.
Configuration for the Politician engine.
A deauthentication or disassociation frame observed on the air, delivered to the DisruptCb callback.
A harvested 802.1X Enterprise plaintext identity, delivered to the IdentityCb callback.
A captured handshake or PMKID record delivered to the EapolCb callback.
A probe request frame observed on the air, delivered to the ProbeRequestCb callback.
Cumulative frame and capture counters for the engine session.