Manipulez les paquets réseau avec Scapy
Formation
En Ligne
Avez-vous besoin d'un coach de formation?
Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.
Description
-
Typologie
Formation
-
Méthodologie
En ligne
Grâce à cette formation vous pourrez acquérir les connaissances nécessaires qui vous permettrons d’ajouter des compétences à votre profil et obtenir de solides aptitude qui vous offriront de nombreuses opportunités professionnelles.
Les Avis
Les matières
- Réseau
- Mise en réseau
Le programme
Scapy est un module pour Python permettant de forger, envoyer, réceptionner et manipuler des paquets réseau.
Si le réseau vous intéresse et que vous aimeriez mieux comprendre le fonctionnement des outils traditionnels (wireshark, dig, ping, traceroute, nmap...), alors ce tutoriel est fait pour vous ! :)
La compréhension de ce tutoriel nécessite quelques prérequis :
Des connaissances en Python (grosso modo les parties 1 et 2 du tutoriel officiel)
Des connaissances de base en réseau (je vous conseille la lecture du tutoriel de elalitte)
A l'issue de ce tutoriel, vous devriez être en mesure de programmer par vous même des utilitaires simples, et surtout de comprendre leur fonctionnement.
Allez, à l'assaut !
Installation et utilisationProfitant de la portabilité du langage Python, Scapy est multi-plateforme. Cela dit, je ne détaillerai ici son installation et son utilisation que sous Linux, ne possédant que cet OS lors de l'écriture de ce tutoriel. La procédure d'installation est décrite ici pour Mac OS X et ici pour Windows. Par facilité, vous pouvez également préférer installer GNU/Linux en machine virtuelle.
InstallationDepuis les dépôts de votre distributionPour les pressés, l'installation minimale se fait avec la commande suivante :
$ sudo apt-get install python-scapyScapy ne fonctionne qu'avec la branche 2.x de Python (>= 2.5), la dernière version de cette branche étant Python 2.7. De plus, vous pouvez étendre les possibilités de Scapy (rapports pdf, traceroute 3D...) grâce à d'autres paquets.
Pour une distribution à base de Debian, l'installation complète se fait donc par cette ligne de commande :
Si vous souhaitez disposer de la toute dernière version de Scapy, vous pouvez l'installer depuis les sources en les récupérant depuis le dépôt mercurial.
Si ce n'est pas déjà fait, installez mercurial :
Récupérez les sources et installez Scapy :
$ hg clone http://hg.secdev.org/scapy $ cd scapy $ sudo python setup.py installScapy est maintenant installé, c'est bien beau, mais passons à son utilisation. :)
UtilisationDepuis l'interpréteurRien de plus simple, lancez la commande suivante :
$ sudo scapyScapy manipule des paquets réseaux, ce qui nécessite d'être en root pour une majorité de tâches
Si tout se passe bien, vous vous retrouvez devant l'interpréteur python :
WARNING: No route found for IPv6 destination :: (no default route?) Welcome to Scapy (2.1.0) >>>Dans un script Python (.py)Pour disposer des fonctionnalités de Scapy dans vos programmes Python, ajoutez simplement cette ligne à vos fichiers :
from scapy.all import *Nous voilà maintenant prêts à utiliser Scapy !
Manipulation de paquetsForgeons !Euh, quel rapport entre la métallurgie et les paquets ? o_O
Aucun. ^^
Forger un paquet désigne le fait de le construire en mettant les "mains dans le cambouis".
Je m'explique. D'ordinaire, quand vous utilisez un logiciel orienté réseau tel qu'un navigateur web, un logiciel de messagerie, etc, celui-ci échange des paquets. Par exemple, votre navigateur, quand vous vous rendez sur http://www.siteduzero.com, échange des paquets avec le serveur web du Site du Zéro. Pour simplifier cet échange, on peut dire que votre navigateur envoie un paquet "envoie moi cette page web s'il te plaît", et que le serveur du Site du Zéro lui renvoie le paquet "tiens, la voici : <html>Coucou</html>" (ne m'en veuillez pas pour cet exemple :D ).
Si vous décidez de programmer un tel logiciel, en pratique, vous n'aurez pas à vous soucier du détail de cette conversation par paquets. Par exemple, en C++, à l'aide de la bibliothèque Qt, afficher une page web se fait ainsi :
Comme vous le voyez, aucune connaissance en réseau n'est nécessaire pour réaliser une telle chose, car on n'a pas réellement mis les mains dans le cambouis.
Or, nous, ce qui nous intéresse, avec Scapy, c'est de comprendre le détail de ces mystérieuses conversations...
Les mots "trame", "paquet", mais aussi "datagramme", "segment", ont des sens bien précis, mais par abus de langage j'utiliserai surtout le mot "paquet" pour désigner les informations échangées sur le réseau.
L'échange de paquets avec un serveur web est loin d'être simple, elle fait intervenir le protocole HTTP, le handshake TCP, l'entête IP, bref, nous allons rester plus basique.
Commençons donc par créer et afficher une trame Éthernet dans l'interpréteur Scapy :
Comme on le voit, la création d'une trame éthernet se fait en instanciant la classe Ether(). Bien qu'on ne lui ai fournit aucun paramètre, on constate a l'appel de la méthode show() que les attributs dst, src et type ont des valeurs par défaut.
Que représentent ces différents attributs ?
Pour ceux qui ne connaitraient pas le protocole éthernet, voici la structure qu'une trame éthernet doit présenter :
Nous venons de créer une trame éthernet "pure", c'est à dire qu'on a rien dans data. Le CRC permet le contrôle d'intégrité de notre trame : si on le modifiait, notre trame deviendrait invalide et inutile. Il ne nous reste donc que 3 champs modifiables :
dst : représente l'adresse mac du destinataire
src : représente l'adresse mac de l'émetteur
type : représente le type de protocole (dépend du contenu de la partie "data" pour l'instant vide)
Pour les modifier, c'est très simple :
>>> ma_trame.dst = '00:19:4b:10:38:79' >>> ma_trame.show() ###[ Ethernet ]### dst= 00:19:4b:10:38:79 src= 00:00:00:00:00:00 type= 0x0 >>>On aurait pu préciser l'adresse mac du destinataire lors de la création de la trame :
>>> ma_trame = Ether(dst='00:19:4b:10:38:79')Les attributs dst, src et type sont modifiables à votre guise. Cela veut donc dire que vous pouvez facilement envoyer des trames en faisant croire que l'émetteur est quelqu'un d'autre !
Les envoyer ? Non, je ne sais pas faire ... :'(
Envoi de la tramePour envoyer une trame Éthernet, il existe la fonction sendp() :
>>> sendp(ma_trame) . Sent 1 packets. >>>Dans le jargon Scapy, un point "." représente un envoi.
Voilà, mon paquet a bien été envoyé à la machine dont j'avais précisé l'adresse mac.
Génial ! .. C'est tout ? :-°
Je vous vois bien déçu. :p Ce que nous venons de faire ne présentait guère d'intérêt, je vous l'accorde. En effet, une trame Éthernet pure ne sert pratiquement à rien ; pour pouvoir faire quelque chose d'intéressant, il faudrait donc mettre quelque chose dans le "data" vu plus haut...
Nous allons donc faire de l'encapsulation.
Citation : Wikipédia
L'encapsulation, en informatique et spécifiquement pour les réseaux informatiques, est un procédé consistant à inclure les données d'un protocole dans un autre protocole.
Encapsuler les protocoles : l'exemple du pingLa commande ping permet de savoir si un hôte, désigné par son adresse IP, existe. En version cambouis, la commande ping consiste à envoyer un paquet ICMP "echo-request" à l'hôte et à dire si un paquet ICMP "echo-reply" a été renvoyé.
Forgeons donc un paquet ICMP echo-request !
On voit que par défaut, l'instanciation de la classe ICMP() met le type du ping à echo-request. On pourrait tout à fait le modifier, tout comme les autres champs. Pour savoir ce qu'ils représentent, je vous renvoie à l'article ICMP sur Wikipédia. Dans cet article, on peut notamment lire quelque chose d'intéressant : un paquet ICMP est encapsulé dans un datagramme IP. En effet, c'est dans le datagramme IP qu'on va pouvoir renseigner l'adresse IP du destinataire.
L'encapsulation entre protocoles, dans Scapy, est réalisée par l'opérateur / (slash). Rien à voir avec une division, donc ;) .
>>> mon_ping = Ether() / IP(dst='192.168.1.1') / ICMP() >>> mon_ping.show() ###[ Ethernet ]### dst= 00:19:4b:10:38:79 src= 00:26:5e:17:00:6e type= 0x800 ###[ IP ]### version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= icmp chksum= None src= 192.168.1.14 dst= 192.168.1.1 \options\ ###[ ICMP ]### type= echo-request code= 0 chksum= None id= 0x0 seq= 0x0 >>>On constate que, en précisant simplement l'adresse IP du destinataire, Scapy a compris tout seul qu'il devait modifier les attributs dst, src et type de Ether() ainsi que l'adresse IP de l'émetteur (src dans IP()) ! C'est très pratique, mais évidemment nous aurions pu forcer Scapy à mettre les valeurs que l'on voulait.
Voyons maintenant si 192.168.1.1 (ma Livebox) va répondre à cela par un paquet ICMP echo-reply.
Envoi du paquetL'envoi s'effectue comme auparavant :
>>> sendp(mon_ping) . Sent 1 packets. >>>Hé, on a toujours rien ! Tu nous aurais menti ?
Oui et non !
Oui, car la fonction sendp() ne fait qu'envoyer, c'est vrai. Pour envoyer et recevoir, il faut utiliser les fonctions srp() et srp1().
Non, car dans le cas d'une trame Éthernet pure, srp() et srp1() n'auraient de toute façon rien reçu !
srp() renvoie deux objets : le premier contient les paquets émis et leurs réponses associées, l'autre contient les paquets sans réponse.
Dans le jargon scapy, une étoile représente une réponse.
On voit qu'on a eu une réponse, zéro échecs, et que notre réponse est un paquet ICMP ! Examinons-le :
>>> rep.show() 0000 Ether / IP / ICMP 192.168.1.14 > 192.168.1.1 echo-request 0 ==> Ether / IP / ICMP 192.168.1.1 > 192.168.1.14 echo-reply 0 >>>Bingo, on a bien reçu un ICMP echo-reply ! :)
rep contient en réalité une liste de couples de paquets. En l'occurence, la liste ne contient qu'un seul couple de paquets, qu'on peut afficher ainsi comme on afficherai n'importe quel élément d'une liste en Python :
Le résultat est un couple (tuple à deux valeurs). Pour afficher le paquet émis (notre ICMP echo-request), on fera donc rep[0][0].show(), et pour le paquet reçu en réponse, rep[0][1].show() :
>>> rep[0][0].show() ###[ Ethernet ]### dst= 00:19:4b:10:38:79 src= 00:26:5e:17:00:6e type= 0x800 ###[ IP ]### version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= icmp chksum= None src= 192.168.1.14 dst= 192.168.1.1 \options\ ###[ ICMP ]### type= echo-request code= 0 chksum= None id= 0x0 seq= 0x0 >>> rep[0][1].show() ###[ Ethernet ]### dst= 00:26:5e:17:00:6e src= 00:19:4b:10:38:79 type= 0x800 ###[ IP ]### version= 4L ihl= 5L tos= 0x0 len= 28 id= 58681 flags= frag= 0L ttl= 64 proto= icmp chksum= 0x1248 src= 192.168.1.1 dst= 192.168.1.14 \options\ ###[ ICMP ]### type= echo-reply code= 0 chksum= 0xffff id= 0x0 seq= 0x0 >>>Pour simplifier tout cela, on peut préférer ici la fonction srp1(). Cette fonction renvoie renvoie un seul objet : la première réponse.
>>> rep = srp1(mon_ping) Begin emission: Finished to send 1 packets. * Received 1 packets, got 1 answers, remaining 0 packets >>> rep.show() ###[ Ethernet ]### dst= 00:26:5e:17:00:6e src= 00:19:4b:10:38:79 type= 0x800 ###[ IP ]### version= 4L ihl= 5L tos= 0x0 len= 28 id= 3386 flags= frag= 0L ttl= 64 proto= icmp chksum= 0xea47 src= 192.168.1.1 dst= 192.168.1.14 \options\ ###[ ICMP ]### type= echo-reply code=...Avez-vous besoin d'un coach de formation?
Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.
Manipulez les paquets réseau avec Scapy