|
Nom de l'article : Scapy
Auteur(s) : Sébastien DAMAYE
Statut : publié
Version : 1.0
Date dernière mise à jour : 18/07/2009
Mots clés : scapy forgeur paquets
Pièces jointes :
Description : Cet article présente un forgeur de paquet très complet : Scapy.
Scapy est un logiciel libre de manipulation de paquets, écrit en langage python. Il est capable, entre autres, d'intercepter le trafic sur un segment réseau, de générer des paquets dans un nombre important de protocoles, de réaliser une prise d'empreinte de la pile TCP/IP, de faire un traceroute et d'analyser le réseau.
L'installation de Scapy sous Windows est bien moins triviale que sous Linux.
La procédure d'installation est décrite ici.
Il existe un package Scapy. Néanmoins, celui-ci correspond à une ancienne version (1.1.1-3) et il est préférable d’effectuer une installation manuelle. La commande suivante installe scapy ainsi que les dépendances nécessaires :
# apt-get install python-gnuplot python-crypto python-pyx ebtables python-visual sox xpdf gv python-scapy
|
# cd /usr/local/src/ # wget http://downloads.sourceforge.net/sourceforge/gnuplot/gnuplot-4.2.5.tar.gz?use_mirror=freefr # tar xzvf gnuplot-4.2.5.tar.gz # cd gnuplot-4.2.5/ # ./configure # make # make install
# apt-get install python-dev
# cd /usr/local/src/ # wget http://downloads.sourceforge.net/sourceforge/numpy/numpy-1.3.0.tar.gz?use_mirror=freefr # tar xzvf numpy-1.3.0.tar.gz # cd numpy-1.3.0/ # python setup.py install
# cd /usr/local/src/ # wget http://downloads.sourceforge.net/sourceforge/gnuplot-py/gnuplot-py-1.8.tar.gz?use_mirror=freefr # tar xzvf gnuplot-py-1.8.tar.gz # cd gnuplot-py-1.8/ # python setup.py install
# cd /usr/local/src/ # wget http://ctan.math.utah.edu/ctan/tex-archive/systems/texlive/tlnet/2008/install-tl-unx.tar.gz # tar xzvf install-tl-unx.tar.gz # cd install-tl/ # ./install-tl
Suivre les étapes de l'installation
|
************ http://www.secdev.org/projects/scapy/doc/installation.html ************
>>> a="Hello" >>> str(a) 'Hello' >>> hexdump(_) 0000 48 65 6C 6C 6F Hello
>>> help(sniff)
Help on function sniff in module __main__:
sniff(count=0, store=1, offline=None, prn=None, lfilter=None, L2socket=None, timeout=None, *arg, **karg)
Sniff packets
sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets
count: number of packets to capture. 0 means infinity
store: wether to store sniffed packets or discard them
prn: function to apply to each packet. If something is returned, it is displayed. Ex:
ex: prn = lambda x: x.summary()
lfilter: python function applied to each packet to determine if further action may be done
ex: lfilter = lambda x: x.haslayer(Padding)
offline: pcap file to read packets from, instead of sniffing them
timeout: stop sniffing after a given time (default: None)
L2socket: use the provided L2socket
>>> 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 : IPoptionsField = ()
>>> lsc() sr : Send and receive packets at layer 3 sr1 : Send packets at layer 3 and return only the first answer srp : Send and receive packets at layer 2 srp1 : Send and receive packets at layer 2 and return only the first answer srloop : Send a packet at layer 3 in loop and print the answer each time srploop : Send a packet at layer 2 in loop and print the answer each time sniff : Sniff packets p0f : Passive OS fingerprinting: which OS emitted this TCP SYN ? arpcachepoison : Poison target's cache with (your MAC,victim's IP) couple send : Send packets at layer 3 sendp : Send packets at layer 2 traceroute : Instant TCP traceroute arping : Send ARP who-has requests to determine which hosts are up ls : List available layers, or infos on a given layer lsc : List user commands queso : Queso OS fingerprinting nmap_fp : nmap fingerprinting report_ports : portscan a target and output a LaTeX table 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" is_promisc : Try to guess if target is in Promisc mode. The target is provided by its ip. promiscping : Send ARP who-has requests to determine which hosts are in promiscuous mode
Pour initialiser une nouvelle session, utiliser la commande suivante :
# ./scapy.py -s mysession INFO: Using session [mysession] Welcome to Scapy (1.2.0.2) >>>
Scapy permet de forger n’importe quel type de paquet (couche TCP, UDP, IP, etc.). Pour chaque couche, les valeurs qui ne sont pas renseignées manuellement le seront automatiquement. La commande ls(IP) permet de consulter l’attribution automatique de ces valeurs.
>>> 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 : IPoptionsField = ()
|
Initialisons tout d’abord un nouvel objet :
>>> a=IP()
Celui-ci contient dorénavant des valeurs par défaut :
>>> a.ttl 64 >>> a.src '127.0.0.1'
Il est possible de forcer ces valeurs par défaut :
>>> a.src='192.168.1.13' >>> a.src '192.168.1.13'
Pour consulter toutes les valeurs forcées, il suffit de rappeler l’objet :
>>> a <IP src=192.168.1.13 |>
Certaines valeurs sont dépendantes et donc recalculées. L’exemple ci-dessous illustre ce phénomène (la valeur de a.src par défaut est mise à jour par dépendance de mise à jour de la valeur de a.dst)
>>> a=IP() >>> a.src '127.0.0.1' >>> a.dst='192.168.1.1' >>> a.src '192.168.182.132'
La fonction del( ) permet de détruire tout ou partie d’un objet. L’exemple qui suit montre que la suppression d’une valeur forcée entraîne la réinitialisation de la valeur par défaut :
>>> del(a.dst)
>>> a.dst, a.src
('127.0.0.1', '127.0.0.1')
>>> a
<IP |>
>>> del(a)
>>> a
Traceback (most recent call last):
File "<console>", line 1, in ?
NameError: name 'a' is not defined
Pour compléter notre paquet, nous pouvons également spécifier les drapeaux TCP comme suit :
>>> a=IP(dst="192.168.1.1") >>> a <IP dst=192.168.1.1 |> >>> b=a/TCP(flags="SF") >>> b <IP frag=0 proto=tcp dst=192.168.1.1 | <TCP flags=FS |>>
Ou, pour une vue plus détaillée :
>>> b.show()
###[ IP ]###
version= 4
ihl= 0
tos= 0x0
len= 0
id= 1
flags=
frag= 0
ttl= 64
proto= tcp
chksum= 0x0
src= 192.168.182.132
dst= 192.168.1.1
options=
###[ TCP ]###
sport= ftp_data
dport= www
seq= 0
ack= 0
dataofs= 0
reserved= 0
flags= FS
window= 8192
chksum= 0x0
urgptr= 0
options= {}
Il est également possible de spécifier plusieurs éléments par paramètre. L'exemple suivant montre comment spécifier deux ports de destination :
>>> b.ttl=(10,14) >>> b.dport=[80,443] >>> [k for k in b] [<IP frag=0 ttl=10 proto=tcp dst=192.168.1.1 |<TCP dport=www flags=FS |>>, <IP frag=0 ttl=10 proto=tcp dst=192.168.1.1 |<TCP dport=https flags=FS |>>, <IP frag=0 ttl=11 proto=tcp dst=192.168.1.1 |<TCP dport=www flags=FS |>>, <IP frag=0 ttl=11 proto=tcp dst=192.168.1.1 |<TCP dport=https flags=FS |>>, <IP frag=0 ttl=12 proto=tcp dst=192.168.1.1 |<TCP dport=www flags=FS |>>, <IP frag=0 ttl=12 proto=tcp dst=192.168.1.1 |<TCP dport=https flags=FS |>>, <IP frag=0 ttl=13 proto=tcp dst=192.168.1.1 |<TCP dport=www flags=FS |>>, <IP frag=0 ttl=13 proto=tcp dst=192.168.1.1 |<TCP dport=https flags=FS |>>, <IP frag=0 ttl=14 proto=tcp dst=192.168.1.1 |<TCP dport=www flags=FS |>>, <IP frag=0 ttl=14 proto=tcp dst=192.168.1.1 |<TCP dport=https flags=FS |>>]
|
La commande send( ) permet d'envoyer des paquets précédemment forgés :
>>> a=IP(dst="192.168.225.1") >>> a <IP dst=192.168.225.1 |> >>> send(a) . Sent 1 packets.
|
Il existe plusieurs fonctions pour envoyer et recevoir des paquets :
>>> sr1(IP(dst="192.168.225.1")/ICMP()) Begin emission: .*Finished to send 1 packets.
Received 2 packets, got 1 answers, remaining 0 packets <IP version=4L ihl=5L tos=0x0 len=28 id=1758 flags= frag=0L ttl=128 proto=icmp chksum=0x1b2c src=192.168.225.1 dst=192.168.182.132 options= |<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' >>>
En cas de non réponse :
>>> sr1(IP(dst="192.168.225.2")/ICMP()) Begin emission: .Finished to send 1 packets. ..............................................................^C Received 63 packets, got 0 answers, remaining 1 packets
Il est possible de scanner les hôtes actifs sur un réseau, en exploitant le tableau de résultats comme dans l'exemple ci-dessous :
>>> sr(IP(dst="192.168.0.0/24")/ICMP()) Begin emission: ....*.*.................*.Finished to send 256 packets. ...........^C Received 37 packets, got 3 answers, remaining 253 packets (<Results: TCP:0 UDP:0 ICMP:3 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:253 Other: 0>) >>> res,unans=_ >>> res.summary() IP / ICMP 192.168.182.132 > 192.168.0.10 echo-request 0 ==> IP / ICMP 192.168.0. 10 > 192.168.182.132 echo-reply 0 / Padding IP / ICMP 192.168.182.132 > 192.168.0.13 echo-request 0 ==> IP / ICMP 192.168.0. 13 > 192.168.182.132 echo-reply 0 / Padding IP / ICMP 192.168.182.132 > 192.168.0.254 echo-request 0 ==> IP / ICMP 192.168.0 .254 > 192.168.182.132 echo-reply 0 / Padding
Le mode d'écoute est assuré par la fonction sniff( ). Elle accepte comme arguments principaux count pour limiter l'écoute à un nombre de paquets, et filter afin de filtrer le type de paquet (TCP, UDP, ICMP, Other).
Sur le serveur, entrez la commande suivante :
>>> sniff(count=5)
Sur le client, effectuez un ping vers le serveur afin de générer du trafic. Scapy passe alors en mode d'écoute. Lorsque le nombre de paquets spécifiés (ici 5) est atteint, Scapy rend la main et affiche un message :
<Sniffed: TCP:1 UDP:0 ICMP:4 Other:0>
Il est alors possible de déchiffrer le résultat intercepté :
>>> a=_ >>> a.summary() Ether / IP / TCP 192.168.182.1:1200 > 192.168.182.132:https A / Padding Ether / IP / ICMP 192.168.182.1 > 192.168.182.132 echo-request 0 / Raw Ether / IP / ICMP 192.168.182.132 > 192.168.182.1 echo-reply 0 / Raw Ether / IP / ICMP 192.168.182.1 > 192.168.182.132 echo-request 0 / Raw Ether / IP / ICMP 192.168.182.132 > 192.168.182.1 echo-reply 0 / Raw
Les fonctions wrpcap( ) et rdpcap( ) permettent respectivement d'écrire et de lire des fichiers de capture.
Scapy est un outil complet qui permet de fabriquer presque n'importe quel type de paquet, ce qui implique également l'émission de paquets malformés, ping de la mort ou autres. C'est pourquoi, même si cet outil connaît certaines limites, il est très utile dans le cadre de campagnes de tests de sécurité d'un réseau.
Par ailleurs, Scapy est doté de fonctionnalités graphiques – non présentées dans le présent document – et qui permettent de représenter les résultats de traceroute en 3D (voir figure suivante : rendu 3D d'un traceroute avec Scapy).