In this tutorial, we will be looking at the Promiscuous Mode (WiFi sniffer) for the ESP32 based boards and as an example, we are going to see the implementation of WiFi sniffer using Zerynth Studio.

What is a WiFi Sniffer?

Sniffing a network is a technique for intercepting and analyzing packets of data that are being sent in a network. The packages payloads of data are often encrypted and cannot be read, but they also include some metadata that may be useful in different scenarios.

We are going to sniff a WiFi network using a tiny board such as the ESP32. The board won’t be connected to any network and will catch every packet around.

WiFi sniffers have wide uses in the business environment. For example, this technology is used in Smart Retail applications for acquiring point of sale information, identification of consistent customers, people counters, etc.

However, these analytics raise a lot of concerns regarding the privacy of users since every device could be uniquely identified by its MAC address. For this reason, new generation smartphones use a technique known as MAC address randomization: this replaces the MAC address with randomly generated values when the device is not connected to any network, making it difficult to really track a person.

Sniffing WiFi packets using ESP32 and Zerynth

The heart of this tutorial is using the promiscuous mode for WiFi packet sniffing using a board based on a microcontroller. For more information, check the documentation of the promiscuous mode for ESP32.

We can use any of the Zerynth supported boards that are based on ESP32. In this tutorial, we are going to use the DOIT ESP32 Devkit V1.

If this is your first time using Zerynth, follow this documentation on getting started with Zerynth Studio. And here you can find a specific tutorial to get started with Zerynth and ESP32.

As you can find in the tutorials just mentioned, first you need to connect and virtualize the board.

Then clone the example “Wifi Sniffer” and uplink the script into the DOIT ESP32 Devkit V1. To uplink the script, press the “up arrow” icon found below:

Once the code is uplinked, you will receive the message “uplink done” in the terminal log of the Zerynth Studio.

Then open the console to debug the status of the request.

The console prints the sniffer output in the current channel. Before hopping to the next channel, the console print the stats of each channel as a tuple with sniffer statistics:

  • number of sniffed packets since last start
  • number of management packets that did not match a management subtype filter
  • number of control packets that did not match a control subtype filter
  • number of data packets that did not match a data subtype filter
  • number of packets that did not match the direction filter
  • number of packets missed due to buffer full
  • number of packets in the buffer
  • number of bytes used up in the payload memory pool
  • current sniffer channel

Code Explanation

The code starts with importing the libraries the serial peripheral and the wifi driver:

Then we initialize the serial peripheral and the wifi driver:

The sniffer can only sniff one channel at a time. To work around this limitation, we specify a list of channels in the range [1..13] and a “hop_time” in milliseconds. The sniffer will listen on each channel in channels for “hop_time” before jumping to the next.

Then we start sniffing, by calling the function “start_sniffer”, and passing the following parameters: packet type, direction, channels, the packet buffer and the max payload.

Finally we will sniff each channel for 10 seconds, print the results and the stats for each channel then hop to the next one.

For more information, check the documentation of each of these functions here.

We hope that this tutorial was easy to follow,

Happy coding!