Introduction
Dans ce projet, nous allons développer un scanner de ports serveur à l'aide de Python pour détecter les ports ouverts sur une cible donnée. Cet outil est indispensable aux administrateurs système pour valider les configurations de sécurité, mais il est également utilisé pour identifier les services réseau opérationnels sur une machine. Nous explorerons les concepts essentiels du scan de ports, sa méthodologie et ses enjeux. Nous nous concentrerons sur la création d'un outil performant grâce à une architecture multi-thread, permettant d'accélérer considérablement les opérations de scan.
Le scan de ports consiste à envoyer des requêtes à une plage de ports serveur pour déterminer lesquels sont accessibles. Cette étape est cruciale pour maintenir la sécurité ou pour identifier des services potentiellement vulnérables. Nous commencerons par explorer les bases du scan de ports et ses implications techniques.
Concepts clés :
- Les scanners de ports permettent de détecter les ports ouverts sur un serveur ou un hôte.
- Ils sont utilisés pour les audits de sécurité et l'identification des services réseau.
- La forme la plus simple de scan de ports consiste à tenter des connexions TCP sur une plage de ports définie.
👀 Aperçu
Voici un aperçu de l'outil que nous allons construire :
python port_scanner.py 127.0.0.1 5000-9000
Sortie attendue :
Opened Port: 8081
Scanning completed.
🎯 Tâches
Dans ce projet, vous apprendrez à :
- Utiliser les capacités de programmation socket de Python pour interagir avec les ports réseau.
- Implémenter une approche multi-thread pour améliorer l'efficacité des tâches de scan réseau.
- Développer un outil en ligne de commande acceptant des paramètres utilisateur pour un scan flexible.
🏆 Objectifs
Après avoir terminé ce projet, vous serez capable de :
- Manipuler la bibliothèque
socketde Python pour créer des connexions, tester la disponibilité des ports et gérer les exceptions réseau. - Comprendre et appliquer le multi-threading pour exécuter des tâches concurrentes, améliorant ainsi radicalement les performances des opérations réseau intensives.
- Construire un scanner de ports pratique, renforçant vos compétences en scripting Python et en analyse d'arguments en ligne de commande.
Établissement des tests de connexion TCP
Notre première étape consiste à écrire une fonction pour tester si un port TCP est ouvert sur l'adresse IP cible. Cette fonction tentera de se connecter à un port spécifique et déterminera son état.
Ouvrez le fichier nommé port_scanner.py dans le répertoire /home/labex/project et ajoutez la fonction Python suivante :
import argparse
from queue import Queue
from socket import AF_INET, gethostbyname, socket, SOCK_STREAM
import threading
def tcp_test(port: int, target_ip: str) -> None:
with socket(AF_INET, SOCK_STREAM) as sock:
sock.settimeout(1)
result = sock.connect_ex((target_ip, port))
if result == 0:
print(f"Opened Port: {port}")
Ce code définit la fonction tcp_test, qui tente d'établir une connexion TCP vers un port donné sur l'IP cible. Elle définit un délai d'attente (timeout) pour éviter de bloquer trop longtemps sur chaque port et utilise connect_ex pour effectuer la tentative.
Si connect_ex retourne 0, cela signifie que la connexion a réussi, indiquant que le port est ouvert. La fonction affiche alors un message confirmant l'ouverture du port.
Configuration des vérifications de ports concurrentes
Ensuite, nous allons créer une fonction worker qui sera utilisée par chaque thread pour traiter les ports provenant d'une file d'attente (queue) et utiliser la fonction tcp_test pour vérifier l'état de chaque port.
Ajoutez le code suivant à votre fichier port_scanner.py :
def worker(target_ip: str, queue: Queue) -> None:
while not queue.empty():
port = queue.get()
tcp_test(port, target_ip)
queue.task_done()
La fonction worker est conçue pour être exécutée par plusieurs threads simultanément. Elle récupère continuellement des numéros de ports dans une file d'attente et utilise la fonction tcp_test précédemment définie pour vérifier si ces ports sont ouverts sur l'IP cible.
Après avoir testé un port, elle marque la tâche comme terminée dans la file d'attente. Cette fonction permet un scan concurrent, ce qui accélère considérablement le processus global.
Orchestration du processus de scan
La fonction main orchestre le processus de scan en configurant la file d'attente, en initialisant les threads de travail et en gérant l'opération de scan sur la plage de ports spécifiée.
Poursuivez en ajoutant la fonction main à votre fichier port_scanner.py :
def main(host: str, start_port: int, end_port: int) -> None:
target_ip = gethostbyname(host)
queue = Queue()
for port in range(start_port, end_port + 1):
queue.put(port)
for _ in range(100): ## Adjust the number of threads if necessary
t = threading.Thread(target=worker, args=(target_ip, queue,))
t.daemon = True
t.start()
queue.join()
print("Scanning completed.")
La fonction main pilote l'intégralité du scan.
Elle commence par résoudre le nom d'hôte cible en adresse IP, puis crée une file d'attente pour les ports à scanner. Elle remplit cette file avec tous les ports de la plage spécifiée et lance plusieurs threads (100 dans cet exemple) qui traitent la file de manière concurrente. La fonction attend que tous les ports aient été traités (queue.join()) avant d'afficher un message indiquant la fin du scan.
Exécution du scanner de ports
Enfin, il est temps de lancer notre scanner. Nous allons scanner une plage de ports sur l'hôte local (localhost) pour tester notre outil.
Ajoutez les lignes suivantes à la fin de votre fichier port_scanner.py pour rendre le script exécutable :
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='TCP Port Scanner')
parser.add_argument('host', help='Host to scan')
parser.add_argument('ports', help='Port range to scan, formatted as start-end')
args = parser.parse_args()
start_port, end_port = map(int, args.ports.split('-'))
main(args.host, start_port, end_port)
Ce code utilise l'analyse d'arguments en ligne de commande, rendant le script plus polyvalent et convivial.
Grâce à la bibliothèque argparse, deux arguments obligatoires sont définis : host et ports.
- L'argument
hostpermet de spécifier l'adresse ou le nom d'hôte de la cible. - L'argument
portsattend une chaîne définissant la plage de ports, formatée comme "début-fin" (par exemple, "5000-8000"). Le script sépare ces valeurs via le délimiteur-et les convertit en entiers. Cela permet aux utilisateurs de spécifier facilement la cible et la plage de ports lors de l'exécution depuis le terminal.
Maintenant, exécutez le scanner en tapant la commande suivante dans le terminal :
python port_scanner.py 127.0.0.1 5000-9000
Sortie :
Opened Port: 8081
Scanning completed.
Maintenant que vous avez identifié un port ouvert (8081), ce qui indique qu'un serveur web y est actif, vous pouvez explorer ce qui y est hébergé.
Pour ce faire, cliquez d'abord sur le bouton d'ajout et sélectionnez l'option "Web Service".

Ensuite, saisissez le port 8081 et cliquez sur le bouton "Access".

Vous découvrirez le serveur web en cours d'exécution sur votre système, écoutant sur le port 8081.

Résumé
Dans ce projet, nous avons construit un scanner de ports de base avec Python, en partant de la fonction fondamentale tcp_test jusqu'à une architecture de scan multi-thread. Nous avons décomposé le processus en étapes structurées, aboutissant à un outil fonctionnel capable d'identifier les ports ouverts sur un serveur cible. Grâce à cette expérience pratique, nous avons non seulement approfondi notre compréhension de la sécurité réseau, mais aussi perfectionné nos compétences en programmation Python et en gestion de la concurrence.



