espilon-source/espilon_bot/components/mod_redteam/rt_promisc.c
Eun0us 2315979db0
Some checks failed
Discord Push Notification / notify (push) Has been cancelled
ε - Add WiFi offensive capabilities to mod_redteam
Phase 1 of v0.4.0 offensive modules:

- Promiscuous dispatcher (rt_promisc): shared IRAM callback multiplexer
  for stealth scan, karma, capture — solves single-callback ESP-IDF limit
- Attack manager (rt_attack): mutual exclusion ensuring only one
  offensive operation runs at a time
- Deauth refactored to use shared promisc dispatcher + attack lock
- Stealth passive scan migrated to promisc dispatcher
- Karma attack (rt_karma): probe request listener + probe response
  injection + rogue SoftAP with most-requested SSID + DNS responder
- WPA handshake capture (rt_capture): EAPOL frame capture via
  promiscuous DATA filter, 4-way handshake identification, optional
  deauth burst to trigger reconnection
- Kconfig: RT_BEACON, RT_KARMA, RT_CAPTURE toggle options
- 5 new C2 commands: rt_karma, rt_karma_stop, rt_karma_clients,
  rt_capture, rt_capture_stop (14 total in mod_redteam)
2026-03-01 02:08:28 +01:00

202 lines
5.8 KiB
C

/*
* rt_promisc.c
* Shared promiscuous mode dispatcher.
*
* Multiplexes up to RT_PROMISC_MAX_HANDLERS consumers behind a single
* IRAM callback registered with esp_wifi_set_promiscuous_rx_cb().
*/
#include "sdkconfig.h"
#ifdef CONFIG_MODULE_REDTEAM
#include <string.h>
#include <stdatomic.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "rt_promisc.h"
#define TAG "RT_PROMISC"
/* ============================================================
* Internal state
* ============================================================ */
static rt_promisc_handler_t s_handlers[RT_PROMISC_MAX_HANDLERS];
static int s_handler_count = 0;
static SemaphoreHandle_t s_mutex = NULL;
static atomic_bool s_enabled = ATOMIC_VAR_INIT(false);
static bool s_inited = false;
/* ============================================================
* IRAM dispatcher — called from WiFi driver context
* ============================================================ */
static void IRAM_ATTR promisc_dispatcher(void *buf, wifi_promiscuous_pkt_type_t type)
{
/*
* We iterate without taking the mutex because:
* - This runs in IRAM from the WiFi RX ISR context
* - s_handler_count is only modified while promiscuous is disabled
* - Handlers are only added/removed via register/unregister which
* require the caller to disable promiscuous first (or accept races
* on the count — benign: worst case a handler is skipped once).
*/
int n = s_handler_count;
for (int i = 0; i < n; i++) {
if (s_handlers[i].cb) {
s_handlers[i].cb(buf, type);
}
}
}
/* ============================================================
* Public API
* ============================================================ */
void rt_promisc_init(void)
{
if (s_inited) return;
s_mutex = xSemaphoreCreateMutex();
configASSERT(s_mutex);
memset(s_handlers, 0, sizeof(s_handlers));
s_handler_count = 0;
s_inited = true;
ESP_LOGI(TAG, "Promiscuous dispatcher initialised (max %d handlers)",
RT_PROMISC_MAX_HANDLERS);
}
esp_err_t rt_promisc_register(const rt_promisc_handler_t *h)
{
if (!h || !h->cb) return ESP_ERR_INVALID_ARG;
if (!s_inited) rt_promisc_init();
xSemaphoreTake(s_mutex, portMAX_DELAY);
/* Check for duplicates */
for (int i = 0; i < s_handler_count; i++) {
if (s_handlers[i].cb == h->cb) {
xSemaphoreGive(s_mutex);
ESP_LOGW(TAG, "Handler '%s' already registered", h->tag ? h->tag : "?");
return ESP_OK;
}
}
if (s_handler_count >= RT_PROMISC_MAX_HANDLERS) {
xSemaphoreGive(s_mutex);
ESP_LOGE(TAG, "Handler table full (%d/%d)", s_handler_count,
RT_PROMISC_MAX_HANDLERS);
return ESP_ERR_NO_MEM;
}
s_handlers[s_handler_count] = *h;
s_handler_count++;
ESP_LOGI(TAG, "Registered handler '%s' (filter=0x%04"PRIx32") [%d/%d]",
h->tag ? h->tag : "?", h->filter_mask,
s_handler_count, RT_PROMISC_MAX_HANDLERS);
xSemaphoreGive(s_mutex);
return ESP_OK;
}
esp_err_t rt_promisc_unregister(const rt_promisc_handler_t *h)
{
if (!h || !h->cb) return ESP_ERR_INVALID_ARG;
if (!s_inited) return ESP_ERR_INVALID_STATE;
xSemaphoreTake(s_mutex, portMAX_DELAY);
for (int i = 0; i < s_handler_count; i++) {
if (s_handlers[i].cb == h->cb) {
/* Shift remaining entries down */
for (int j = i; j < s_handler_count - 1; j++) {
s_handlers[j] = s_handlers[j + 1];
}
s_handler_count--;
memset(&s_handlers[s_handler_count], 0, sizeof(rt_promisc_handler_t));
ESP_LOGI(TAG, "Unregistered handler '%s' [%d/%d]",
h->tag ? h->tag : "?",
s_handler_count, RT_PROMISC_MAX_HANDLERS);
xSemaphoreGive(s_mutex);
return ESP_OK;
}
}
xSemaphoreGive(s_mutex);
ESP_LOGW(TAG, "Handler '%s' not found", h->tag ? h->tag : "?");
return ESP_ERR_NOT_FOUND;
}
esp_err_t rt_promisc_enable(void)
{
if (!s_inited) rt_promisc_init();
xSemaphoreTake(s_mutex, portMAX_DELAY);
/* Combine filters from all registered handlers */
uint32_t combined = 0;
for (int i = 0; i < s_handler_count; i++) {
combined |= s_handlers[i].filter_mask;
}
/* If no handlers registered but caller still wants promisc (e.g. for TX),
* default to management frames */
if (combined == 0) {
combined = WIFI_PROMIS_FILTER_MASK_MGMT;
}
xSemaphoreGive(s_mutex);
/* Set the single dispatcher callback */
esp_err_t ret = esp_wifi_set_promiscuous_rx_cb(promisc_dispatcher);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "set_promiscuous_rx_cb failed: %s", esp_err_to_name(ret));
return ret;
}
wifi_promiscuous_filter_t filter = { .filter_mask = combined };
esp_wifi_set_promiscuous_filter(&filter);
ret = esp_wifi_set_promiscuous(true);
if (ret == ESP_OK) {
atomic_store(&s_enabled, true);
ESP_LOGI(TAG, "Promiscuous enabled (filter=0x%04"PRIx32")", combined);
} else {
ESP_LOGE(TAG, "set_promiscuous(true) failed: %s", esp_err_to_name(ret));
}
return ret;
}
esp_err_t rt_promisc_disable(void)
{
esp_err_t ret = esp_wifi_set_promiscuous(false);
if (ret == ESP_OK) {
atomic_store(&s_enabled, false);
ESP_LOGI(TAG, "Promiscuous disabled");
}
return ret;
}
esp_err_t rt_promisc_set_channel(uint8_t channel)
{
if (channel < 1 || channel > 14) return ESP_ERR_INVALID_ARG;
return esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
}
bool rt_promisc_is_enabled(void)
{
return atomic_load(&s_enabled);
}
#endif /* CONFIG_MODULE_REDTEAM */