Rastreadores de wifi, baratos y fáciles

· 5min · Juicecat

Introducción

La física detrás del wifi

Los rastreadores de wifi existen desde hace tanto tiempo como el propio wifi. El concepto es simple: imagina que tienes una linterna en la oscuridad. Irradia luz en un radio de 360 ​​grados a su alrededor, y cualquiera que se encuentre cerca podrá ver la luz. Ahora imagina que la luz emitida por la linterna es invisible y puede atravesar 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), al igual que la luz, los rayos X e incluso las lámparas de calor. Su frecuencia relativamente baja le permite penetrar objetos sólidos; de lo contrario, no podrás conectarte a tu wifi si hay una pared entre tú y el punto de acceso.

1

Concepto

Dado que la información se intercambia a través de estas señales wifi, esto plantea un interés para posibles actores maliciosos o simplemente para personas curiosas. Con hardware comúnmente disponible y un conocimiento mínimo del tema, es posible capturar y registrar todas y cada una de las señales wifi que se encuentren en las cercanías. Y eso es lo que vamos a explorar hoy.

Preparación

Materiales

Para hacer nuestro sniffer wifi solo necesitamos un ordenador que pueda acceder a wifi y que tenga un adaptador que sea capaz de entrar en modo promiscuo. En mi last post, presenté el SoC ESP32 de "mini computadora" de Espressif. Son extraordinariamente baratos, pequeños y potentes. Puedes comprar un here por alrededor de $7 USD.

##Software

Para escribir el rastreador, usaremos la cadena de herramientas esp-idf (refer to this post to see why). Utilizo VSCode IDE como mi editor de texto, pero el editor que uses es una preferencia personal y no hace ninguna diferencia.

Escribiendo el código

El código para este proyecto es muy simple y solo una PoC, por lo que es mínimo y no tan útil. En proyectos esp-idf, comenzamos con un punto de entrada a nuestra función app_main(). Antes de hacer cualquier cosa, debemos realizar algunos pasos de configuración previos para inicializar los métodos wifi y de devolución de llamada:

  1. Inicie el bucle de eventos esp32
  2. Inicializar la memoria flash NVS
  3. Inicializar la interfaz de red
  4. 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 de punto de entrada.

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 es inicializar la memoria flash NVS. Aquí hay una verificación condicional que, en caso de 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 arroja un error si el código de retorno no es exitoso.

Pasos 3 y 4

Ahora que estamos listos para usar esp32, necesitamos configurar los componentes de la red. Extraje estos pasos en una función separada, que simplemente se llama desde la función principal para organizar el código un poco mejor:

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 poder utilizar realmente estos paquetes, necesitamos crear una función de devolución de llamada, que se llama cada vez que se captura una trama que coincide con el filtro, lo cual hacemos en las líneas 15-16.

En la línea 23, configuré un filtro para capturar solo tramas de administración, que es solo cierto tipo de mensaje; hago esto sin otra razón que para reducir el ruido creado por este programa, de lo contrario, miles de paquetes por segundo llegarían a la interfaz. Este código se explica por sí mismo, pero puede notar que llamo a una función llamada wifi_mode_to_string. Esto se debe a que el miembro wifi_mode_t es una enumeración y el excelente lenguaje C no es capaz de imprimir la representación de cadena de una enumeración, por lo que tenemos que asignarla 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";
  }
}

Poniéndolo todo junto

Ahora todo está configurado, solo necesitamos crear la función de devolución de llamada wifi y registrar los datos que obtenemos. En nuestra función de devolución de llamada, dado que se trata de una 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, lo actualizamos al ESP32 y finalmente establecemos una conexión UART para que podamos ver el resultado. Podemos hacer esto con un simple comando: idfx build && idfx flash COM11 monitor

Y así es como se ve el resultado: 1

Conclusión

Como se señaló al principio, esto es solo una PoC y no es realmente útil en cualquier forma práctica. Ya existen herramientas fantásticas y establecidas creadas para este propósito, como Wireshark y Air*-ng suite.

El propósito de esta publicación es brindar una explicación simplificada y una descripción general de qué es el rastreo de wifi y qué tan fácil se puede lograr. Como tal, espero haber podido explicar bien las cosas. No soy bueno escribiendo publicaciones de blogs, pero estoy tratando de mejorar, así que tengan paciencia.