Comment créer un scanner WiFi en Python avec Scapy

Par : Géry Menye, le 10 Juillet 2022

Avez-vous déjà voulu créer un outil pour afficher les réseaux sans fil à proximité avec leur adresse MAC et d'autres informations utiles ? Eh bien, dans ce tutoriel, nous allons créer un scanner Wi-Fi en utilisant la bibliothèque Scapy en Python.

Si vous êtes dans ce domaine depuis un certain temps, vous avez peut-être entendu parlé ou même utilisé l'utilitaire airodump-ng qui renifle, capture et décode les trames 802.11 pour afficher les réseaux sans fil à proximité dans un format agréable. Dans ce tutoriel, nous allons faire une chose similaire.

 

Objectif

Créer un scanner Wi-Fi en Python à l'aide de Scapy qui trouve et affiche les réseaux sans fil disponibles à proximité ainsi que leur adresse MAC, le signal en dBm, le canal et le type de chiffrement.

 

Avant de commencer à coder

Pour commencer, vous devez d'abord installer Scapy. Pour ma part, j'ai cloné la version de développement. Vous pouvez aussi l'installer en utilisant pip :

$ pip3 install scapy

Ou vous pouvez cloner la version de développement actuelle sur Github :

$ git clone https://github.com/secdev/scapy.git
$ cd scapy
$ sudo python setup.py install

 

Remarque : Ce tutoriel suppose que vous utilisez un environnement basé sur Unix ; il est également suggéré d'utiliser Kali Linux.

 

Après quoi, nous utiliserons la bibliothèque pandas uniquement pour afficher dans un format agréable (vous pouvez évidemment changer cela) :

pip3 install pandas

À présent, le code de ce tutoriel ne fonctionnera pas si vous n'activez pas le mode monitor dans votre interface réseau, veuillez installer aircrack-ng (pré-installé sur Kali) et exécuter la commande suivante :

Maintenant, vous pouvez vérifier le nom de votre interface en utilisant iwconfig :

Comme vous pouvez le voir, notre interface est maintenant en mode monitor et porte le nom "wlan0mon".

Vous pouvez également utiliser la commande iwconfig pour faire passer votre carte réseau en mode monitor :

$ sudo ifconfig wlan0 down
$ sudo iwconfig wlan0 mode monitor

 

 

Écriture du code

Commençons, ouvrez un nouveau fichier Python et importez les modules nécessaires :

import os
import time
import pandas

from scapy.all import *
from threading import Thread

Ensuite, nous devons initialiser un dataframe vide qui stocke nos réseaux :

# initialize the networks dataframe that will contain all access points nearby
networks = pandas.DataFrame(columns=["BSSID", "SSID", "dBm_Signal", "Channel", "Crypto"])
# set the index BSSID (MAC address of the AP)
networks.set_index("BSSID", inplace=True)

J'ai donc défini le BSSID (adresse MAC du point d'accès) comme l'index de chaque ligne, car il est unique pour chaque appareil.

Si vous êtes familier avec Scapy, alors vous savez à coup sûr que nous allons utiliser la fonction sniff(), qui prend en paramètre la fonction callback qui est exécutée chaque fois qu'un paquet est reniflé ; implémentons cette fonction :

def callback(packet):
    if packet.haslayer(Dot11Beacon):
        # extract the MAC address of the network
        bssid = packet[Dot11].addr2
        # get the name of it
        ssid = packet[Dot11Elt].info.decode()
        try:
            dbm_signal = packet.dBm_AntSignal
        except:
            dbm_signal = "N/A"
        # extract network stats
        stats = packet[Dot11Beacon].network_stats()
        # get the channel of the AP
        channel = stats.get("channel")
        # get the crypto
        crypto = stats.get("crypto")
        networks.loc[bssid] = (ssid, dbm_signal, channel, crypto)

Cette fonction callback s'assure que le paquet reniflé contient une couche de balise, si c'est le cas, il extrait le BSSID, le SSID (nom du point d'accès), le signal et quelques statistiques. La classe Dot11Beacon de Scapy possède l'impressionnante fonction network_stats() qui extrait certaines informations utiles du réseau, comme le canal, les taux et le type de chiffrement. Enfin, nous ajoutons ces informations au dataframe avec le BSSID comme index.

Vous rencontrerez certains réseaux qui n'ont pas de SSID (ssid égal à ""), ce qui indique qu'il s'agit d'un réseau caché. Dans les réseaux cachés, le point d'accès laisse le champ d'information vide pour cacher la découverte du nom du réseau, vous les trouverez toujours en utilisant le script de ce tutoriel, mais sans nom de réseau.

Maintenant, nous avons besoin d'un moyen de visualiser ce dataframe. Puisque nous allons utiliser la fonction sniff() (qui bloque et commence le sniffing dans le thread principal), nous devons utiliser un thread séparé pour afficher le contenu du dataframe du réseau, le code ci-dessous le fait :

def print_all():
    while True:
        os.system("clear")
        print(networks)
        time.sleep(0.5)

Au code principal maintenant :

if __name__ == "__main__":
    # interface name, check using iwconfig
    interface = "wlan0mon"
    # start the thread that prints all the networks
    printer = Thread(target=print_all)
    printer.daemon = True
    printer.start()
    # start sniffing
    sniff(prn=callback, iface=interface)

 

Changer de canal

À présent, si vous exécutez ce code, vous remarquerez que tous les réseaux proches ne sont pas disponibles, c'est parce que nous écoutons sur un seul canal WLAN. Nous pouvons utiliser la commande iwconfig pour changer le canal. Voici la fonction Python pour le faire :

def change_channel():
    ch = 1
    while True:
        os.system(f"iwconfig {interface} channel {ch}")
        # switch channel from 1 to 14 each 0.5s
        ch = ch % 14 + 1
        time.sleep(0.5)

Par exemple, si vous voulez passer au canal 2, la commande iwconfig sera la suivante :

iwconfig wlan0mon channel 2

Bien, donc la fonction change_channel() va changer les canaux de manière incrémentale de 1 à 14 toutes les 0,5 secondes, en lançant le daemon thread qui exécute cette fonction :

# start the channel changer
channel_changer = Thread(target=change_channel)
channel_changer.daemon = True
channel_changer.start()

Remarque : les canaux 12 et 13 sont autorisés en mode faible puissance, tandis que le canal 14 est interdit et uniquement autorisé au Japon.

Notez que nous avons mis l'attribut daemon du thread à True, donc ce thread se terminera dès que le programme se terminera, consultez ce tutoriel pour plus d'informations sur les daemon threads.

Voici le code source complet du fichier wifi_scanner.py.

import os
import time
import pandas

from scapy.all import *
from threading import Thread

# initialize the networks dataframe that will contain all access points nearby
networks = pandas.DataFrame(columns=["BSSID", "SSID", "dBm_Signal", "Channel", "Crypto"])
# set the index BSSID (MAC address of the AP)
networks.set_index("BSSID", inplace=True)

def callback(packet):
    if packet.haslayer(Dot11Beacon):
        # extract the MAC address of the network
        bssid = packet[Dot11].addr2
        # get the name of it
        ssid = packet[Dot11Elt].info.decode()
        try:
            dbm_signal = packet.dBm_AntSignal
        except:
            dbm_signal = "N/A"
        # extract network stats
        stats = packet[Dot11Beacon].network_stats()
        # get the channel of the AP
        channel = stats.get("channel")
        # get the crypto
        crypto = stats.get("crypto")
        networks.loc[bssid] = (ssid, dbm_signal, channel, crypto)


def print_all():
    while True:
        os.system("clear")
        print(networks)
        time.sleep(0.5)


def change_channel():
    ch = 1
    while True:
        os.system(f"iwconfig {interface} channel {ch}")
        # switch channel from 1 to 14 each 0.5s
        ch = ch % 14 + 1
        time.sleep(0.5)


if __name__ == "__main__":
    # interface name, check using iwconfig
    interface = "wlan0mon"
    # start the thread that prints all the networks
    printer = Thread(target=print_all)
    printer.daemon = True
    printer.start()
    # start the channel changer
    channel_changer = Thread(target=change_channel)
    channel_changer.daemon = True
    channel_changer.start()
    # start sniffing
    sniff(prn=callback, iface=interface)

Voici une capture d'écran de mon exécution :

 

 

Conclusion

Très bien, dans ce tutoriel, nous avons écrit un simple scanner Wi-Fi en utilisant la bibliothèque Scapy qui renifle et décode les trames beacon qui sont transmises à chaque fois par les points d'accès, elles servent à signaler la présence d'un réseau sans fil.