Au préalable, il faut installer tous les packages nécessaires pour faire des graphiques.
sudo apt-get install tcpdump graphviz imagemagick python-gnuplot python-crypto python-pyx
Nous allons faire un graphe du chemin pris pour atteindre www.tf1.fr en envoyant un paquet traceroute.
Avant de faire le graphe du chemin pris par traceroute, on affichera les réponses au format texte.
sudo scapy
[sudo] password for xxxxx:
WARNING: No route found for IPv6 destination :: (no default route?)
Welcome to Scapy (2.2.0)
>>> sn,unans=traceroute(["www.tf1.fr"],maxttl=10)
Begin emission:
*.*.*.*.*.Finished to send 10 packets.
*.*.*.*...
Received 20 packets, got 9 answers, remaining 1 packets
23.200.87.158:tcp80
1 192.168.1.254 11
2 5.50.10.254 11
3 62.35.253.193 11
4 89.89.101.33 11
5 212.194.171.6 11
7 62.34.0.98 11
8 23.200.87.158 SA
9 23.200.87.158 SA
10 23.200.87.158 SA
>>> sn.nsummary()
0000 IP / TCP 192.168.1.2:17239 > 23.200.87.158:http S ==> IP / ICMP 192.168.1.254 > 192.168.1.2 time-exceeded ttl-zero-during-transit / IPerror / TCPerror
0001 IP / TCP 192.168.1.2:10443 > 23.200.87.158:http S ==> IP / ICMP 62.35.253.193 > 192.168.1.2 time-exceeded ttl-zero-during-transit / IPerror / TCPerror
0002 IP / TCP 192.168.1.2:5701 > 23.200.87.158:http S ==> IP / ICMP 5.50.10.254 > 192.168.1.2 time-exceeded ttl-zero-during-transit / IPerror / TCPerror
0003 IP / TCP 192.168.1.2:16247 > 23.200.87.158:http S ==> IP / ICMP 89.89.101.33 > 192.168.1.2 time-exceeded ttl-zero-during-transit / IPerror / TCPerror
0004 IP / TCP 192.168.1.2:16605 > 23.200.87.158:http S ==> IP / ICMP 212.194.171.6 > 192.168.1.2 time-exceeded ttl-zero-during-transit / IPerror / TCPerror / Padding
0005 IP / TCP 192.168.1.2:56709 > 23.200.87.158:http S ==> IP / ICMP 62.34.0.98 > 192.168.1.2 time-exceeded ttl-zero-during-transit / IPerror / TCPerror / Padding
0006 IP / TCP 192.168.1.2:51284 > 23.200.87.158:http S ==> IP / TCP 23.200.87.158:http > 192.168.1.2:51284 SA / Padding
0007 IP / TCP 192.168.1.2:59111 > 23.200.87.158:http S ==> IP / TCP 23.200.87.158:http > 192.168.1.2:59111 SA / Padding
0008 IP / TCP 192.168.1.2:32305 > 23.200.87.158:http S ==> IP / TCP 23.200.87.158:http > 192.168.1.2:32305 SA / Padding
>>> unans.nsummary()
0000 IP / TCP 192.168.1.2:47871 > 23.200.87.158:http S
>>> sn.graph()
ce qui donne sous forme graphique :
Quelques explications :
192.168.1.254 est l'IP privée de ma box Bouygues Telecom
5.50.10.254 est donc le premier routeur de Bouygues Telecom que ma box utilise pour sortir sur Internet.
Pour TTL6, le routeur n'a pas répondu, donc on ne peut pas connaître l'IP de ce routeur (sur le graphe unk0 , et unans.nsummary().
On voit que traceroute envoie en fait des paquets TCP avec flag SYN (S) et port 80 (http) (vers www.tf1.fr (en incrémentant le TTL, et le routeur, qui ne peut transmettre ce paquet, répond par une erreur ICMP, ce qui permet de connaître l'IP du routeur.
- Détails
Pour écouter le réseau, il faut utiliser la commande sniff.
Par exemple, nous allons écouter le réseau en filtrant sur les trames concernant www.google.fr.
Lancer sniff :
resp=sniff(filter="host www.google.fr")
Lancer un ping (un seul) vers www.google.fr : ping www.google.fr -c 1
Afficher ce que sniff a capturé :
resp.show()
ce qui donne :
0000 Ether / IP / UDP / DNS Qry "www.google.fr."
0001 Ether / IP / UDP / DNS Qry "www.google.fr."
0002 Ether / IP / UDP / DNS Qry "www.google.fr."
0003 Ether / IP / ICMP 192.168.1.2 > 173.194.40.216 echo-request 0 / Raw
0004 Ether / IP / ICMP 173.194.40.216 > 192.168.1.2 echo-reply 0 / Raw
- Détails
Installation de scapy sur Ubuntu
sudo apt-get install python-scapy
Obtenir de l'aide sur les commandes scapy
Pour connaitre la liste des commandes scapy
>>> lsc() arpcachepoison : Poison target's cache with (your MAC,victim's IP) couple arping : Send ARP who-has requests to determine which hosts are up bind_layers : Bind 2 layers on some specific fields' values corrupt_bits : Flip a given percentage or number of bits from a string corrupt_bytes : Corrupt a given percentage or number of bytes from a string defrag : defrag(plist) -> ([not fragmented], [defragmented], defragment : defrag(plist) -> plist defragmented as much as possible dyndns_add : Send a DNS add message to a nameserver for "name" to have a new "rdata" dyndns_del : Send a DNS delete message to a nameserver for "name" etherleak : Exploit Etherleak flaw fragment : Fragment a big IP datagram fuzz : Transform a layer into a fuzzy layer by replacing some default values by random objects getmacbyip : Return MAC address corresponding to a given IP address hexdiff : Show differences between 2 binary strings hexdump : -- hexedit : -- is_promisc : Try to guess if target is in Promisc mode. The target is provided by its ip. linehexdump : -- ls : List available layers, or infos on a given layer promiscping : Send ARP who-has requests to determine which hosts are in promiscuous mode rdpcap : Read a pcap file and return a packet list send : Send packets at layer 3 sendp : Send packets at layer 2 sendpfast : Send packets at layer 2 using tcpreplay for performance sniff : Sniff packets split_layers : Split 2 layers previously bound sr : Send and receive packets at layer 3 sr1 : Send packets at layer 3 and return only the first answer srbt : send and receive using a bluetooth socket srbt1 : send and receive 1 packet using a bluetooth socket srflood : Flood and receive packets at layer 3 srloop : Send a packet at layer 3 in loop and print the answer each time srp : Send and receive packets at layer 2 srp1 : Send and receive packets at layer 2 and return only the first answer srpflood : Flood and receive packets at layer 2 srploop : Send a packet at layer 2 in loop and print the answer each time traceroute : Instant TCP traceroute tshark : Sniff packets and print them calling pkt.show(), a bit like text wireshark wireshark : Run wireshark on a list of packets wrpcap : Write a list of packets to a pcap file >>>
Voir tous les objets que Scapy peut manipuler
ls() ARP : ARP ... IP : IP ...
=> Il existe donc un objet IP dont le contenu peut être connu via ls(IP)
>>> ls(IP) version : BitField = (4) ihl : BitField = (None) tos : XByteField = (0) len : ShortField = (None) id : ShortField = (1) flags : FlagsField = (0) frag : BitField = (0) ttl : ByteField = (64) proto : ByteEnumField = (0) chksum : XShortField = (None) src : Emph = (None) dst : Emph = ('127.0.0.1') options : PacketListField = ([]) >>>
Aide sur la commande send qui permet d'envoyer un paquet forgé à partir du niveau 3 (couche IP). Ceci veut dire que le niveau 2 (la couche ethernet) a été renseigné par scapy automatiquement :
thierry@PC1:~/Bureau/data/testScapy$ scapy
Welcome to Scapy (2.2.0)
>>> help(send)
Help on function send in module scapy.sendrecv:
send(x, inter=0, loop=0, count=None, verbose=None, realtime=None, *args, **kargs)
Send packets at layer 3
send(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None
Aide sur la commande sendp qui permet d'envoyer un paquet forgé à partir du niveau 2 (couche Ethernet):
thierry@PC1:~$ scapy INFO: Can't import python gnuplot wrapper . Won't be able to plot. INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). WARNING: No route found for IPv6 destination :: (no default route?) Welcome to Scapy (2.2.0) >>> help(sendp) Help on function sendp in module scapy.sendrecv: sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, realtime=None, *args, **kargs) Send packets at layer 2 sendp(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None
Création d'un paquet
On voit ci-dessous les valeutrs par défaut.
De plus, certaines valeurs sont recalculées, comme l'IP source suite à la définition de l'IP destination :
>>> a=IP() >>> a.ttl 64 >>> a.src '127.0.0.1' >>> a.dst='192.168.1.2' >>> a.src '192.168.1.10' >>>
Création d'un paquet ping vers www.google.fr
Trois méthodes :
- soit on crée le paquet à partir du niveau Ethernet en précisant que celui-ci contient un paquet IP à destination www.google.fr, lui même contenant un paquet ICMP echo-request, et envoie via srp
- soit on crée le paquet à partir du niveau IP en précisant que la destination est www.google.fr, puis en précisant que ce paquet IP contient une charge un paquet ICMP echo-request, et envoi via sr,
- soit on crée le paquet avec la méthode ICMP().
Cas de la construction de la trame à partir du niveau Ethernet. La fonction pour envoyer un tel paquet est alors srp.
#!/usr/bin/python
from scapy.all import *
# creation du paquet IP a destination de www.google.fr, ce paquet IP contenant un ICMP
pck=Ether()/IP(dst='www.google.fr')/ICMP()
# Affichage de ce paquet : affiche son contenu Ethernet, IP et ICMP
print "--- pck.show():"
pck.show()
L'exécution de ce script donne :
WARNING: No route found for IPv6 destination :: (no default route?) --- pck.show(): ###[ Ethernet ]### dst = 94:fe:f4:ac:d0:51 src = 00:24:21:b5:e6:46 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.2 dst = Net('www.google.fr') \options \ ###[ ICMP ]### type = echo-request code = 0 chksum = None id = 0x0 seq = 0x0
Explications :
Scapy signale qu'il ne peut joindre www.google.fr via IPv6 étant donné que ma box ne supporte pas IPv6.
Scapy a complété automatiquement la couche 2 pour ce qui concerne l'adresse MAC de l'émetteur et l'adresse MAC du destinataire :
adresse MAC de l'émetteur : l'adresse MAC de mon PC
adresse MAC du destinataire : l'adresse MAC de ma box côté LAN.
A présent on envoie ce paquet en utilisant rep,norep=srp(pck). rep contient les paquet émis avec la réponse. norep contient les paquets qui n'auraient pas obtenu de réponse.
Compléter donc le script ainsi :
#envoi du paquet construit a partir du niveau IP, send permet l'envoi du paquet mais ne permet pas de recuperer la reponse a ce paquet
print "--- envoi du paquet sans lire la reponse"
sendp(pck)
print "--- envoi du paquet avec lecture de la reponse via la commande srp"
rep,norep=srp(pck)
print "--- Affichage de la reponse, uniquement la partie ICMP pour la destination " + str(pck.dst)
rep[0][1][ICMP].show()
Ce qui donne:
--- envoi du paquet sans lire la reponse . Sent 1 packets. --- envoi du paquet avec lecture de la reponse via la commande srp Begin emission: Finished to send 1 packets. * Received 1 packets, got 1 answers, remaining 0 packets --- Affichage de la reponse, uniquement la partie ICMP pour la destination 94:fe:f4:ac:d0:51 ###[ ICMP ]### type = echo-reply code = 0 chksum = 0xffff id = 0x0 seq = 0x0 ###[ Padding ]### load = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
Cas de la construction de la trame à partir du niveau IP comme montré ci-dessous. La fonction pour envoyer un tel paquet est alors sr.
#!/usr/bin/python
from scapy.all import *
# creation du paquet IP a destination de www.google.fr, ce paquet IP contenant un ICMP
pck=IP(dst='www.google.fr')/ICMP()/"Hello"
# Affichage de ce paquet : affiche son contenu IP et son contenu ICMP
print "--- pck.show():"
pck.show()
# Affichage de ce paquet mais uniquement sa partie ICMP
print "--- pck[ICMP].show():"
pck[ICMP].show()
#envoi du paquet construit a partir du niveau IP, send permet l'envoi du paquet mais ne permet pas de recuperer la reponse a ce paquet
print "--- envoi du paquet sans lire la reponse"
send(pck)
print "--- envoi du paquet avec lecture de la reponse via la commande sr"
rep,norep=sr(pck)
print "--- Affichage de la reponse, uniquement la partie ICMP pour la destination " + str(pck.dst)
rep[0][1][ICMP].show()
Ce qui donne:
WARNING: No route found for IPv6 destination :: (no default route?) --- pck.show(): ###[ IP ]### version = 4 ihl = None tos = 0x0 len = None id = 1 flags = frag = 0 ttl = 64 proto = icmp chksum = None src = 192.168.1.2 dst = Net('www.google.fr') \options \ ###[ ICMP ]### type = echo-request code = 0 chksum = None id = 0x0 seq = 0x0 ###[ Raw ]### load = 'Hello' --- pck[ICMP].show(): ###[ ICMP ]### type = echo-request code = 0 chksum = None id = 0x0 seq = 0x0 ###[ Raw ]### load = 'Hello' --- envoi du paquet sans lire la reponse . Sent 1 packets. --- envoi du paquet avec lecture de la reponse via la commande sr Begin emission: .Finished to send 1 packets. * Received 2 packets, got 1 answers, remaining 0 packets --- Affichage de la reponse, uniquement la partie ICMP pour la destination Net('www.google.fr') ###[ ICMP ]### type = echo-reply code = 0 chksum = 0xdc2d id = 0x0 seq = 0x0 ###[ Raw ]### load = 'Hello' ###[ Padding ]### load = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
Bibliographie
http://fr.openclassrooms.com/informatique/cours/manipulez-les-paquets-reseau-avec-scapy
- Détails
Voici comment via Scapy envoyer un ping sur une plage d'adresse IPv4. Dans l'exemple, on scanne la plage de 192.168.1.1 à 192.168.1.45.
#! /usr/bin/python
from scapy.all import *
# scan sur la plage 192.168.1.1 a 192.168.1.45
rang = '192.168.1.1-45'
rep,non_rep = sr( IP(dst=rang) / ICMP() , timeout=0.5 )
print "-------- Fin Envoi des ping ---------------"
for elem in rep :
if elem[1].type == 0 : # 0 <=> echo-reply
print elem[1].src + ' a renvoye un echo-reply au ping vers ' + str(elem[0].dst)
Ce qui donnera :
Begin emission:
......WARNING: Mac address to reach destination not found. Using broadcast.
....
WARNING: Mac address to reach destination not found. Using broadcast.
.Finished to send 45 packets.
... diverses lignes similaires
Received 76 packets, got 1 answers, remaining 44 packets
-------- Fin Envoi des ping ---------------
192.168.1.40 a renvoye un echo-reply au ping vers 192.168.1.40
- Détails