espilon-source/espilon_bot/components/mod_redteam/rt_stealth.c
Eun0us 6d45770d98 epsilon: merge command system into core + add 5 new modules
Move command registry from components/command/ into components/core/.
New modules: mod_canbus, mod_honeypot, mod_fallback, mod_redteam, mod_ota.
Replace mod_proxy with tun_core (multiplexed SOCKS5 tunnel).
Kconfig extended with per-module settings and async worker config.
2026-02-28 20:07:59 +01:00

273 lines
8.2 KiB
C

/*
* rt_stealth.c
* OPSEC: MAC randomization, TX power control, passive scan.
*/
#include "sdkconfig.h"
#include "rt_stealth.h"
#ifdef CONFIG_MODULE_REDTEAM
#include <string.h>
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_random.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char *TAG = "RT_STEALTH";
/* ============================================================
* MAC randomization
* ============================================================ */
static uint8_t s_orig_mac[6] = {0};
static bool s_mac_saved = false;
void rt_stealth_save_original_mac(void)
{
if (esp_wifi_get_mac(WIFI_IF_STA, s_orig_mac) == ESP_OK) {
s_mac_saved = true;
ESP_LOGI(TAG, "Original MAC: %02X:%02X:%02X:%02X:%02X:%02X",
s_orig_mac[0], s_orig_mac[1], s_orig_mac[2],
s_orig_mac[3], s_orig_mac[4], s_orig_mac[5]);
}
}
void rt_stealth_randomize_mac(void)
{
uint8_t mac[6];
esp_fill_random(mac, 6);
mac[0] &= 0xFE; /* unicast */
mac[0] |= 0x02; /* locally administered */
/* Must disconnect before changing MAC */
esp_wifi_disconnect();
vTaskDelay(pdMS_TO_TICKS(50));
esp_err_t err = esp_wifi_set_mac(WIFI_IF_STA, mac);
if (err == ESP_OK) {
ESP_LOGI(TAG, "MAC randomized: %02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
} else {
ESP_LOGW(TAG, "MAC set failed: %s", esp_err_to_name(err));
}
}
void rt_stealth_restore_mac(void)
{
if (s_mac_saved) {
esp_wifi_disconnect();
vTaskDelay(pdMS_TO_TICKS(50));
esp_wifi_set_mac(WIFI_IF_STA, s_orig_mac);
ESP_LOGI(TAG, "MAC restored: %02X:%02X:%02X:%02X:%02X:%02X",
s_orig_mac[0], s_orig_mac[1], s_orig_mac[2],
s_orig_mac[3], s_orig_mac[4], s_orig_mac[5]);
}
}
void rt_stealth_get_current_mac(uint8_t mac[6])
{
esp_wifi_get_mac(WIFI_IF_STA, mac);
}
/* ============================================================
* TX power control
* ============================================================ */
void rt_stealth_low_tx_power(void)
{
/* 8 dBm (arg * 0.25 dBm, so 32 = 8 dBm) */
esp_err_t err = esp_wifi_set_max_tx_power(32);
if (err == ESP_OK) {
ESP_LOGI(TAG, "TX power reduced to 8 dBm");
} else {
ESP_LOGW(TAG, "TX power set failed: %s", esp_err_to_name(err));
}
}
void rt_stealth_restore_tx_power(void)
{
esp_wifi_set_max_tx_power(80); /* 20 dBm */
ESP_LOGI(TAG, "TX power restored to 20 dBm");
}
/* ============================================================
* Passive scan — promiscuous mode beacon capture
* ============================================================ */
/* WiFi management frame header */
typedef struct {
unsigned frame_ctrl:16;
unsigned duration_id:16;
uint8_t addr1[6]; /* Destination */
uint8_t addr2[6]; /* Source */
uint8_t addr3[6]; /* BSSID */
unsigned seq_ctrl:16;
} __attribute__((packed)) wifi_mgmt_hdr_t;
/* Beacon frame body (partial — just what we need) */
/* Fixed fields: timestamp(8) + beacon_interval(2) + capability(2) = 12 bytes */
#define BEACON_FIXED_LEN 12
/* Tag: SSID = tag_number 0, followed by length, then SSID string */
static rt_scan_ap_t s_scan_results[RT_MAX_SCAN_APS];
static volatile int s_scan_count = 0;
/* Check if we already have this BSSID */
static int find_bssid(const uint8_t bssid[6])
{
for (int i = 0; i < s_scan_count; i++) {
if (memcmp(s_scan_results[i].bssid, bssid, 6) == 0)
return i;
}
return -1;
}
static void IRAM_ATTR passive_scan_cb(void *buf, wifi_promiscuous_pkt_type_t type)
{
if (type != WIFI_PKT_MGMT) return;
wifi_promiscuous_pkt_t *pkt = (wifi_promiscuous_pkt_t *)buf;
wifi_mgmt_hdr_t *hdr = (wifi_mgmt_hdr_t *)pkt->payload;
/* Check frame type: beacon = 0x80, probe response = 0x50 */
uint16_t fc = hdr->frame_ctrl;
uint8_t subtype = (fc >> 4) & 0x0F;
if (subtype != 8 && subtype != 5) return; /* 8=beacon, 5=probe_resp */
/* BSSID is addr3 for beacons */
const uint8_t *bssid = hdr->addr3;
/* Skip if already seen */
if (find_bssid(bssid) >= 0) {
/* Update RSSI if stronger */
int idx = find_bssid(bssid);
if (pkt->rx_ctrl.rssi > s_scan_results[idx].rssi) {
s_scan_results[idx].rssi = pkt->rx_ctrl.rssi;
}
return;
}
if (s_scan_count >= RT_MAX_SCAN_APS) return;
/* Parse beacon body for SSID */
size_t hdr_len = sizeof(wifi_mgmt_hdr_t);
size_t body_offset = hdr_len + BEACON_FIXED_LEN;
if ((int)pkt->rx_ctrl.sig_len < (int)(body_offset + 2))
return;
/* Parse tagged parameters for SSID (tag 0) and RSN/WPA (security) */
const uint8_t *body = pkt->payload + body_offset;
size_t body_len = pkt->rx_ctrl.sig_len - body_offset;
/* Remove FCS (4 bytes) if present */
if (body_len > 4) body_len -= 4;
rt_scan_ap_t *ap = &s_scan_results[s_scan_count];
memset(ap, 0, sizeof(*ap));
memcpy(ap->bssid, bssid, 6);
ap->rssi = pkt->rx_ctrl.rssi;
ap->channel = pkt->rx_ctrl.channel;
ap->auth_mode = 0; /* Assume open until we find RSN/WPA tag */
/* Parse IEs (Information Elements) */
size_t pos = 0;
while (pos + 2 <= body_len) {
uint8_t tag_id = body[pos];
uint8_t tag_len = body[pos + 1];
if (pos + 2 + tag_len > body_len) break;
if (tag_id == 0) { /* SSID */
size_t ssid_len = tag_len;
if (ssid_len > 32) ssid_len = 32;
memcpy(ap->ssid, body + pos + 2, ssid_len);
ap->ssid[ssid_len] = '\0';
} else if (tag_id == 48) { /* RSN (WPA2) */
ap->auth_mode = 3; /* WPA2 */
} else if (tag_id == 221) { /* Vendor specific — check for WPA OUI */
if (tag_len >= 4 &&
body[pos + 2] == 0x00 && body[pos + 3] == 0x50 &&
body[pos + 4] == 0xF2 && body[pos + 5] == 0x01) {
if (ap->auth_mode == 0) ap->auth_mode = 2; /* WPA */
}
}
pos += 2 + tag_len;
}
s_scan_count++;
}
int rt_stealth_passive_scan(int duration_ms)
{
s_scan_count = 0;
memset(s_scan_results, 0, sizeof(s_scan_results));
/* Enable promiscuous mode */
esp_wifi_disconnect();
vTaskDelay(pdMS_TO_TICKS(100));
esp_err_t ret = esp_wifi_set_promiscuous_rx_cb(passive_scan_cb);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Promiscuous CB failed: %s", esp_err_to_name(ret));
return 0;
}
/* Filter management frames only (beacons, probe responses) */
wifi_promiscuous_filter_t filter = {
.filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT
};
esp_wifi_set_promiscuous_filter(&filter);
ret = esp_wifi_set_promiscuous(true);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Promiscuous enable failed: %s", esp_err_to_name(ret));
return 0;
}
ESP_LOGI(TAG, "Passive scan started (%d ms)", duration_ms);
/* Channel hop: ~200ms per channel, 13 channels per cycle */
int channels = 13;
int hop_ms = 200;
int elapsed = 0;
while (elapsed < duration_ms) {
for (int ch = 1; ch <= channels && elapsed < duration_ms; ch++) {
esp_wifi_set_channel(ch, WIFI_SECOND_CHAN_NONE);
vTaskDelay(pdMS_TO_TICKS(hop_ms));
elapsed += hop_ms;
}
}
/* Disable promiscuous mode */
esp_wifi_set_promiscuous(false);
ESP_LOGI(TAG, "Passive scan done: %d unique APs", s_scan_count);
return s_scan_count;
}
int rt_stealth_get_scan_results(rt_scan_ap_t *out, int max_count)
{
int count = s_scan_count;
if (count > max_count) count = max_count;
memcpy(out, s_scan_results, count * sizeof(rt_scan_ap_t));
return count;
}
#else /* !CONFIG_MODULE_REDTEAM — empty stubs */
#include <string.h>
void rt_stealth_save_original_mac(void) {}
void rt_stealth_randomize_mac(void) {}
void rt_stealth_restore_mac(void) {}
void rt_stealth_get_current_mac(uint8_t mac[6]) { memset(mac, 0, 6); }
void rt_stealth_low_tx_power(void) {}
void rt_stealth_restore_tx_power(void) {}
int rt_stealth_passive_scan(int duration_ms) { (void)duration_ms; return 0; }
int rt_stealth_get_scan_results(rt_scan_ap_t *out, int max_count) { (void)out; (void)max_count; return 0; }
#endif /* CONFIG_MODULE_REDTEAM */