Sniffers Wifi, barats i fàcils

· 5min · Juicecat

Introducció

La física darrere del wifi

Els sniffers de Wifi han existit tant de temps com el mateix wifi. El concepte és senzill: imagina que tens una llanterna a les fosques. Brilla llum en un radi de 360 ​​graus al seu voltant, i qualsevol persona que estigui al seu voltant podrà veure la llum. Ara, imagineu-vos que la llum emesa per la llanterna és invisible i pot travessar objectes sòlids: això és exactament el que són els senyals wifi, encara que immensament simplificats.

De fet, els senyals wifi són una forma de radiació electromagnètica (EMR), igual que la llum, els raigs X i fins i tot les làmpades de calor. La seva freqüència relativament baixa li permet impregnar objectes sòlids, en cas contrari no podríeu connectar-vos al vostre wifi si hi ha una paret entre vosaltres i el punt d'accés.

1

Concepte

Com que la informació s'intercanvia a través d'aquests senyals wifi, això suposa un interès per a possibles actors maliciosos o persones simplement curiosos. Amb el maquinari disponible habitualment i un coneixement mínim del tema, és possible capturar i gravar tots els senyals wifi que hi ha als voltants, i això és el que explorarem avui.

Preparació

Materials

Per fer que el nostre wifi snifer, només necessitem un ordinador que pugui accedir a wifi i que tingui un adaptador capaç d'entrar en mode promiscu. Al meu last post, vaig presentar el SoC ESP32 "mini ordinador" d'Espressif. Són extraordinàriament barats, petits i potents. Podeu comprar un here per uns 7 USD.

Programari

Per escriure l'sniffer, farem servir la cadena d'eines esp-idf (refer to this post to see why). Utilitzo l'IDE VSCode com a editor de text, però l'editor que utilitzeu és una preferència personal i no fa cap diferència.

Escrivint el codi

El codi d'aquest projecte és molt senzill i només un PoC, de manera que és mínim i no és tan útil. En els projectes esp-idf, comencem amb un punt d'entrada a la nostra funció app_main(). Abans de fer res, hem de fer uns quants passos de configuració de requisits previs per inicialitzar els mètodes wifi i de devolució de trucada:

  1. Inicieu el bucle d'esdeveniments esp32
  2. Inicialitzeu el flash NVS
  3. Inicialitzar la interfície de xarxa
  4. Configura la interfície de xarxa

Passos 1 i 2

Aquests passos són molt senzills i bastant estàndard per a projectes d'aquest tipus: aquí teniu el codi, que explicaré a continuació. Recordeu que tot això està inclòs a la nostra funció de punt d'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 pas 1 està totalment contingut en una única línia de codi a la línia 1: la resta és inicialitzar el flash NVS. Aquí hi ha una comprovació condicional que, en cas d'error, esborra la memòria flash i intenta inicialitzar-la de nou. La funció ESP_ERROR_CHECK() que veieu al llarg d'aquest fragment és una funció integrada que accepta un codi de retorn i genera un error si el codi de retorn és qualsevol cosa excepte l'èxit.

Passos 3 i 4

Ara que estem preparats per utilitzar l'esp32, hem de configurar els components de xarxa. Vaig extreure aquests passos en una funció separada, que només s'anomena des de la funció principal per organitzar una mica millor el codi:

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);

}

Per poder utilitzar aquests paquets, hem de crear una funció de devolució de trucada, que es crida sempre que es captura un fotograma que coincideix amb el filtre, cosa que fem a la línia 15-16.

A la línia 23, vaig establir un filtre per capturar només fotogrames de gestió, que és només un cert tipus de missatge: ho faig sense cap altre motiu que reduir el soroll creat per aquest programa, en cas contrari, hi hauria milers de paquets per segon colpejant la interfície. Aquest codi s'explica per si mateix, però és possible que observeu que anomeno una funció anomenada wifi_mode_to_string. Això es deu al fet que el membre wifi_mode_t és una enumeració i el gran llenguatge C no és capaç d'imprimir la representació de cadena d'una enumeració, de manera que l'hem de mapejar manualment:

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";
  }
}

Ajuntant-ho tot

Ara tot està configurat, només hem de crear la funció de devolució de trucada wifi i registrar les dades que obtenim. A la nostra funció de devolució de trucada, com que es tracta d'un PoC, no necessitem fer res més que imprimir els fotogrames.

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");
    }
  }
}

Executant

Ara simplement naveguem al directori de treball del projecte i construïm el projecte, el compilem, el flashem a l'ESP32 i, finalment, establim una connexió UART perquè puguem veure la sortida. Ho podem fer amb una ordre senzilla: idfx build && idfx flash COM11 monitor

I aquí és com es veu la sortida: 1

Conclusió

Com es va assenyalar al principi, això és només un PoC, i és no realment útil de qualsevol manera pràctica. Hi ha eines establertes i fantàstiques ja construïdes per a aquest propòsit, com ara Wireshark i Air*-ng suite.

L'objectiu d'aquesta publicació és proporcionar una explicació simplificada i una visió general del que és l'sniffing de wifi i de com de fàcil es pot aconseguir. Com a tal, espero haver pogut explicar bé les coses: no sóc genial per escriure publicacions de bloc, però estic intentant millorar, així que si us plau, pagueu amb mi.