Blue Flower

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.

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

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

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