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.