Scapy, le couteau suisse Python pour le réseau – Partie 1/2

Découvrez comment utiliser Scapy afin d’écrire vos propres outils réseau en Python.

Tout développeur réseau a déjà rêvé d’avoir à sa disposition une bibliothèque de manipulation de paquets, qui pourrait également être utilisée de façon interactive. C’est exactement ce que propose Scapy, que nous allons présenter dans cet article. Nous verrons tout d’abord son fonctionnement général, puis construirons des outils plus évolués. Il est impossible de détailler toutes les possibilités offertes par Scapy, mais à la fin de cet article, le lecteur devrait avoir toutes les bases nécessaires pour implémenter une application qui répondra à ses besoins.

1. Concepts de base

Voyons dans un premier temps comment prendre en main scapy en utilisant l’interpréteur. Nous utilisons ici la commande scapy3 fournie par le paquet Debian python3-scapy. Notons qu’il faut lancer la commande en tant que root.

Quelques avertissements peuvent apparaître selon la configuration de votre machine. Pensez à les relire si les exemples présentés dans la suite de l’article ne fonctionnent pas correctement.

1.1 Construire des paquets

Construisons et analysons un premier paquet :

En affichant un paquet IP tout simple, on reconnaît les champs de l’entête IP, tels que décrits dans la RFC 791. Nous reproduisons en figure 1 le datagramme présenté en section 3.1 de cette RFC afin de rafraîchir la mémoire de nos lecteurs :
Fig.1 : Structure de notre paquet.

On peut donner une valeur particulière à chacun des champs soit en passant des paramètres au constructeur de la classe IP(), soit en modifiant après coup les attributs :

On peut ajouter des couches à notre paquet grâce à l’opérateur / :

On peut remarquer que les sommes de contrôle ne sont pas calculées. Elles le seront automatiquement lors de la construction du paquet, avant son envoi. Il est possible de les afficher grâce à la méthode show2 :

Nous avons donc construit un paquet similaire à ceux envoyés par l’utilitaire bien connu ping. Ce dernier ajoute par défaut une charge utile de 56 octets (qui peut contenir des informations d’horodatage, notamment sous GNU/Linux). Nous pouvons faire quelque chose de similaire :

1.2 Envoyer des paquets

Maintenant que nous avons construit notre paquet ICMP, il nous faut l’envoyer. Plusieurs fonctions sont disponibles :

  • send() pour envoyer un paquet de la couche 3 ;
  • sendp() pour envoyer un paquet de la couche 2 ;
  • sr1() pour envoyer un paquet de la couche 3 et retourner la première réponse ;
  • srp1() pour envoyer un paquet de la couche 2 et retourner la première réponse ;
  • sr() pour envoyer un paquet de la couche 3 et retourner toutes les réponses ;
  • srp() pour envoyer un paquet de la couche 2 et retourner toutes les réponses.

Nous envoyons ici un paquet ICMP de type echo-request, et nous attendons à recevoir un autre paquet ICMP de type echo-reply. Nous pouvons donc utiliser sr1 :

Nous avons bien reçu la réponse attendue de la part de notre routeur (192.168.0.254). Si nous avions envoyé le paquet à une IP ne correspondant à aucune machine du réseau (ou ne répondant tout simplement pas au ping), la réponse aurait été None :

1.3 Écouter le réseau

Il nous est possible d’écouter le réseau, afin, par exemple, de récupérer les deux prochains paquets ICMP sur l’interface eth0 :

On voit bien ici apparaître notre requête et la réponse du routeur.

Cyril ROELANDT

La seconde partie de cet article sera publiée prochainement sur le blog, restez connectés 😉

Retrouvez cet article (et bien d’autres) dans GNU/Linux Magazine Hors-Série n°90, disponible sur la boutique et sur la plateforme de lecture en ligne Connect !

Laisser un commentaire