Renifleurs Wifi, pas chers et faciles

· 5min · Juicecat

#Présentation

La physique derrière le wifi

Les renifleurs de Wi-Fi existent depuis aussi longtemps que le Wi-Fi lui-même. Le concept est simple : imaginez que vous avez une lanterne dans le noir. Il projette de la lumière dans un rayon de 360 ​​degrés autour de lui et toute personne se trouvant à proximité pourra voir la lumière. Imaginez maintenant que la lumière émise par la lanterne soit invisible et puisse traverser des objets solides : c'est exactement ce que sont les signaux wifi, bien qu'extrêmement simplistes.

En effet, les signaux Wi-Fi sont une forme de rayonnement électromagnétique (EMR), au même titre que la lumière, les rayons X et même les lampes chauffantes. Sa fréquence relativement basse lui permet de pénétrer dans les objets solides, sinon vous ne pourrez pas vous connecter à votre wifi s'il y a un mur entre vous et le point d'accès.

1

##Concept

Les informations étant échangées via ces signaux wifi, cela présente un intérêt pour de potentiels acteurs malveillants ou simplement des individus curieux. Avec du matériel couramment disponible et une connaissance minimale du sujet, il est possible de capturer et d'enregistrer tous les signaux wifi se trouvant à proximité. Et c'est ce que nous allons explorer aujourd'hui.

Préparation

Matériaux

Pour que notre wifi renifleur, nous n'avons besoin que d'un ordinateur pouvant accéder au wifi et disposant d'un adaptateur capable d'entrer en mode promiscuité. Dans mon last post, j'ai présenté le "mini ordinateur" SoC ESP32 d'Espressif. Ce sont des appareils extrêmement bon marché, petits et puissants. Vous pouvez en acheter un here pour environ 7 USD.

Logiciel

Afin d'écrire le renifleur, nous utiliserons la chaîne d'outils esp-idf (refer to this post to see why). J'utilise l'IDE VSCode comme éditeur de texte, mais l'éditeur que vous utilisez dépend de vos préférences personnelles et ne fait aucune différence.

Ecrire le code

Le code de ce projet est très simple et n'est qu'un PoC, il est donc minimal et pas très utile. Dans les projets esp-idf, nous commençons par un point d'entrée dans notre fonction app_main(). Avant de faire quoi que ce soit, nous devons effectuer quelques étapes de configuration préalables afin d'initialiser les méthodes wifi et de rappel :

  1. Démarrez la boucle d'événements esp32
  2. Initialisez le flash NVS
  3. Initialisez l'interface réseau
  4. Configurer l'interface réseau

Étapes 1 et 2

Ces étapes sont très simples et assez standards pour les projets de ce type. Voici le code que je vais expliquer ci-dessous. N'oubliez pas que tout cela est contenu dans notre fonction de point d'entrée.

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

L'étape 1 est entièrement contenue dans une seule ligne de code sur la ligne 1. Le reste consiste à initialiser le flash NVS. Il y a ici une vérification conditionnelle qui, en cas d'erreur, efface la mémoire flash et tente de la réinitialiser à nouveau. La fonction ESP_ERROR_CHECK() que vous voyez dans cet extrait est une fonction intégrée qui accepte un code de retour et renvoie une erreur si le code de retour est autre chose qu'un succès.

Étapes 3 et 4

Maintenant que nous sommes prêts à utiliser l'esp32, nous devons configurer les composants réseau. J'ai extrait ces étapes dans une fonction distincte, qui est simplement appelée depuis la fonction principale afin d'organiser un peu mieux le code :

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

}

Afin d'utiliser réellement ces paquets, nous devons créer une fonction de rappel, qui est appelée chaque fois qu'une trame correspondant au filtre est capturée, ce que nous faisons aux lignes 15-16.

Sur la ligne 23, j'ai défini un filtre pour capturer uniquement les trames de gestion, qui sont juste un certain type de message. Je le fais sans autre raison que de réduire le bruit créé par ce programme, sinon il y aurait des milliers de paquets par seconde frappant l'interface. Ce code est assez explicite, mais vous remarquerez peut-être que j'appelle une fonction appelée wifi_mode_to_string. En effet, le membre wifi_mode_t est une énumération et le grand langage C n'est pas capable d'imprimer la représentation sous forme de chaîne d'une énumération, nous devons donc la mapper manuellement :

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

Rassembler tout cela

Maintenant que tout est configuré, il ne nous reste plus qu'à créer la fonction de rappel wifi et à enregistrer les données que nous obtenons. Dans notre fonction de rappel, puisqu'il s'agit d'un PoC, nous n'avons rien d'autre à faire que d'imprimer les images.

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

Exécution

Maintenant, nous naviguons simplement vers le répertoire de travail du projet et construisons le projet, le compilons, le flashons sur l'ESP32 et enfin établissons une connexion UART afin que nous puissions voir le résultat. Nous pouvons le faire avec une simple commande : idfx build && idfx flash COM11 monitor

Et voici à quoi ressemble le résultat : 1

#Conclusion

Comme indiqué au début, il ne s'agit que d'un PoC et pas est vraiment utile de manière pratique. Il existe des outils établis et fantastiques déjà conçus à cet effet, tels que Wireshark et Air*-ng suite.

Le but de cet article est de fournir une explication simplifiée et un aperçu de ce qu'est la détection Wi-Fi et de la facilité avec laquelle elle peut être réalisée. En tant que tel, j'espère avoir pu bien expliquer les choses. Je ne suis pas doué pour écrire des articles de blog, mais j'essaie de m'améliorer, alors soyez indulgents avec moi.