Sniffers wifi, barato y fácil
Introducción
La física detrás de wifi
Los sniffers de Wifi han existido tan tiempo como WiFi mismo.El concepto es simple: imagine que tienes una linterna en la oscuridad.Brilla la luz en un radio de 360 grados a su alrededor, y cualquiera que esté en sus alrededores podrá ver la luz.Ahora, imagine que la luz emitida por la linterna es invisible y puede pasar a través de objetos sólidos: eso es exactamente lo que son las señales WiFi, aunque inmensamente simplificadas.
De hecho, las señales WiFi son una forma de radiación electromagnética (EMR), lo mismo que la luz, los rayos X e incluso las lámparas de calor.Su frecuencia relativamente baja le permite impregnar objetos sólidos, de lo contrario, no podría conectarse a su wifi si hay una pared entre usted y el punto de acceso.
Concepto
Debido a que se está intercambiando información a través de estas señales WiFi, esto plantea un interés para posibles actores maliciosos o simplemente personas curiosas.Con el hardware comúnmente disponible y el conocimiento mínimo del tema, es posible capturar y registrar todas y cada una de las señales WiFi que están cerca, y eso es lo que vamos a explorar hoy.
Preparación
Materiales
Para hacer que nuestro WiFi sea rastreador, solo necesitamos una computadora que pueda acceder a WiFi y que tenga un adaptador que sea capaz de ingresar al modo promiscuo.En mi last post, introduje la "Mini Computer" SOC ESP32 de Espressif.Estos son extraordinariamente baratos, pequeños y potentes.Puede comprar uno here por aproximadamente $ 7 USD.
Software
Para escribir el sniffer, utilizaremos esp-idf herramienta de herramientas (refer to this post to see why).Utilizo el IDE VSCode como mi editor de texto, pero lo que usa es preferencia personal y no hace ninguna diferencia.
Escribir el código
El código para este proyecto es muy simple y solo un POC, por lo que es mínimo y no tan útil.En los proyectos ESP-IDF, comenzamos con un punto de entrada en nuestra función app_main()
.Antes de hacer algo, necesitamos hacer algunos pasos de configuración de requisitos previos para inicializar los métodos WiFi y la devolución de llamada:
- Comience el bucle de eventos ESP32
- Inicializar NVS Flash
- Inicializar la interfaz de red
- Configurar la interfaz de red
Pasos 1 y 2
Estos pasos son muy simples y bastante estándar para proyectos de este tipo, aquí está el código, que explicaré a continuación.Recuerde, todo esto está contenido en nuestra función EntryPoint.
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
El paso 1 está completamente contenido en una sola línea de código en la línea 1: el resto está inicializando el flash NVS.Aquí hay una verificación condicional que, en caso de un error, borra la memoria flash e intenta inicializarla nuevamente.La función ESP_ERROR_CHECK()
que ve a lo largo de este fragmento es una función incorporada que acepta un código de retorno y lanza un error si el código de retorno es algo excepto el éxito.
Pasos 3 y 4
Ahora que estamos listos para usar el ESP32, necesitamos configurar los componentes de la red.Extraje estos pasos en una función separada, que se llama desde la función principal para organizar un poco mejor el código:
static void setup_wifi(void) {
// Initializ
ESP_LOGI(TAG, "Beginning wifi setup");
ESP_ERROR_CHECK(esp_netif_init());
// Interface defaults
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
assert(sta_netif);
// Default config
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_wifi_set_ps(WIFI_PS_MAX_MODEM);
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
// Setup callback
wifi_promiscuous_cb_t callback = &wifi_callback;
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(callback));
// Set station mode and start
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
// Config filters and stuff
wifi_promiscuous_filter_t filter = {.filter_mask =
WIFI_PROMIS_FILTER_MASK_MGMT};
esp_wifi_set_promiscuous_filter(&filter);
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true));
ESP_LOGI(TAG, "Wifi started successfully");
wifi_mode_t ptr;
ESP_ERROR_CHECK(esp_wifi_get_mode(&ptr));
const char *c = wifi_mode_to_string(ptr);
ESP_LOGI(TAG, "Mode: %s", c);
}
Para usar estos paquetes, necesitamos crear una función de devolución de llamada, que se llama cada vez que se captura un marco que coincide con el filtro, lo que hacemos en la línea 15-16.
En la línea 23, establecí un filtro para capturar solo los marcos de administración, que es solo cierto tipo de mensaje: hago esto sin otra razón que reducir el ruido creado por este programa, de lo contrario, habría miles de paquetes por segundo que llega a la interfaz.Este código se explica bastante, pero puede notar que llamo una función llamada wifi_mode_to_string
.Esto se debe a que el miembro wifi_mode_t
es un enum, y el gran lenguaje C no es capaz de imprimir la representación de una cadena de un enum, por lo que tenemos que mapearlo manualmente:
const char *wifi_mode_to_string(wifi_mode_t mode) {
switch (mode) {
case WIFI_MODE_NULL:
return "WIFI_MODE_NULL";
case WIFI_MODE_STA:
return "WIFI_MODE_STA";
case WIFI_MODE_AP:
return "WIFI_MODE_AP";
case WIFI_MODE_APSTA:
return "WIFI_MODE_APSTA";
case WIFI_MODE_NAN:
return "WIFI_MODE_NAN";
case WIFI_MODE_MAX:
return "WIFI_MODE_MAX";
default:
return "UNKNOWN_MODE";
}
}
Ponlo todo junto
Ahora todo está configurado, solo necesitamos crear la función de devolución de llamada WiFi y los datos de registro que obtenemos.En nuestra función de devolución de llamada, dado que este es un POC, no necesitamos hacer nada más que imprimir los marcos-
void wifi_callback(void *buf, wifi_promiscuous_pkt_type_t type) {
const uint16_t data_length = 256;
ESP_LOGI(TAG, "Printing first %d bytes of received packet with type %s\n",
data_length, wifi_pkt_type_to_string(type));
unsigned char *char_buf = (unsigned char *)buf;
for (int i = 0; i < data_length; i++) {
printf("%02X ", char_buf[i]); // 1 byte = 0x00, two hex chars
if (i % 16 == 15) { // Organize it by groups of 16 bytes because im spoiled by wireshark
printf("\n");
}
}
}
Ejecutando
Ahora simplemente navegamos al directorio de trabajo del proyecto y construimos el proyecto, lo compilamos, flashulamos al ESP32 y finalmente establecemos una conexión UART para que podamos ver la salida.Podemos hacer esto con un comando simple:
idfx build && idfx flash COM11 monitor
Y así es como se ve la salida
Conclusión
Como se señaló al principio, esto es solo un POC, y es no útil de cualquier manera práctica.Hay herramientas establecidas y fantásticas ya construidas para este propósito, como Wireshark y Air*-ng suite.
El propósito de esta publicación es proporcionar una explicación simplificada y una visión general de qué es el olfato WiFi y lo fácil que se puede lograr.Como tal, espero haber podido explicar bien las cosas. No soy genial al escribir publicaciones de blog, pero estoy tratando de mejorar, así que por favor tengan paciencia conmigo.