Scan de ports avec Scapy
Quelques rappels (Linux)
Un port est l'adresse d'une application (ou service) TCP ou UDP sur un hôte au niveau de la couche 4 (Transport),
selon un service est actif ou en écoute, le port applicatif correspondant (associé) est ouvert,
les numéros de port sont codés sur 16 bits, ce qui permet 65 536 ports distincts par hôte,
les ports sont classés en 3 catégories en fonction de leur numéro:
les numéros de port de 0 à 1 023 correspondent aux ports bien-connus (well-known ports) et sont utilisés pour les services réseaux les plus courants,
les numéros de ports de 1 024 à 49 151 correspondent aux ports enregistrés (registered ports), assignés par l'IANA,
les numéros de ports de 49 152 à 65 535 correspondent aux ports dynamiques, utilisables pour tout type de requêtes TCP ou UDP autres que celle citées précédemment.
Pour en savoir plus voir la page de Wikipdia : https://fr.wikipedia.org/wiki/Port_(logiciel)
Pour visualiser les ports ouverts d'un hôte et donc connaître les services actifs (en écoute) :
root@debian:~# netstat -tanpu
Connexions Internet actives (serveurs et établies)
Proto Recv-Q Send-Q Adresse locale Adresse distante Etat PID/Program name
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 645/mysqld
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 478/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1087/exim4
tcp 0 464 192.168.1.159:22 192.168.1.196:50264 ESTABLISHED 1119/sshd: root@pts
tcp6 0 0 :::80 :::* LISTEN 680/apache2
tcp6 0 0 :::22 :::* LISTEN 478/sshd
tcp6 0 0 ::1:25 :::* LISTEN 1087/exim4
udp 0 0 0.0.0.0:68 0.0.0.0:* 692/dhclient
root@debian:~#
le colonne Etat indique :
LISTEN pour une application qui est en écoute et en attente de requêtes de la part de clients,
ESTABLISHED quand une application a établi la communication suite à une demande de requête.
Quand un client a pu se connecter à un port, l'état de l'application passe de LISTEN à ESTABLISHED.
Quand plusieurs clients se connectent à la même application :
il y a pour chaque client une instance de l'application avec l'état ESTABLISHED,
et une une autre instance en écoute avec l'état LISTEN pour recevoir de nouvelles connexions.
Exemple du service SSH (port 22) :
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 478/sshd
tcp 0 464 192.168.1.159:22 192.168.1.196:50264 ESTABLISHED 1119/sshd: root@pts
la première ligne montre que le port 22 est à l'état LISTEN,
La seconde ligne montre une connexion établie (ESTABLISHED)
Le port 50264 est choisi aléatoirement par le client lors de l'initialisation de la connexion, pour communiquer avec le serveur.
Un port TCP ouvert est un port en état LISTEN,
Un port UDP est simplement en écoute car UDP ne gère pas l'établissement de connexions.
udp 0 0 0.0.0.0:68 0.0.0.0:* 692/dhclient
Lors d'un scan de ports, on ne visualise que les ports en état LISTEN.
Les ports en l'état ESTABLISHED ne peuvent pas être visualisés puisque cela correspond à une communication spécifique entre deux hôtes et un troisième hôte ne peut entrer dans la communication qui a été établie.
Scan de port TCP
Source Wikipedia : https://fr.wikipedia.org/wiki/Transmission_Control_Protocol#.C3.89tablissement_d.27une_connexion
Schéma d'établissement d'une connexion TCP :
Le client envoie un segment avec le flag SYN au serveur,
Le serveur lui répond par un segment avec le flag SYN/ACK,
Le client confirme par un segment avec le flag ACK.
Durant cet échange initial, les numéros de séquence des deux parties sont synchronisés :
Le client utilise son numéro de séquence initial dans le champ “Numéro de séquence” du segment SYN (x par exemple),
Le serveur utilise son numéro de séquence initial dans le champ “Numéro de séquence” du segment SYN/ACK (y par exemple) et ajoute le numéro de séquence du client plus un (x+1) dans le champ “Numéro d'acquittement” du segment,
Le client confirme en envoyant un ACK avec un numéro de séquence augmenté de un (x+1) et un numéro d'acquittement correspondant au numéro de séquence du serveur plus un (y+1).
Si le serveur rejette la connexion, il répond avec un segment dont le flag est RESET.
Attributs disponible et valeur par défaut pour le protocole TCP
>>> 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 = ({})
>>>
)
Envoi d'un paquet TCP sur le service Web d'un serveur (port 80) avec le flag SYN
>>> paquet = IP(dst='192.168.1.1') / TCP(sport=55555, dport=80, flags='S')
>>>
>>> rep, non_rep = sr(paquet)
Begin emission:
..Finished to send 1 packets.
..*
Received 5 packets, got 1 answers, remaining 0 packets
>>> rep.show()
0000 IP / TCP 192.168.1.159:55555 > 192.168.1.1:http S ==> IP / TCP 192.168.1.1:http > 192.168.1.159:55555 SA / Padding
>>>
Le résultat est toujours composé des deux couples paquet émis / paquet reçu.
Le paquet envoyé sur le port 80 (Scapy affiche http à la place du numéro de port 80) avec le flag positionné à SYN (Scapy affiche un S pour l'attribut flags ce qui correspond à la valeur numérique 2)
>>> rep[0][0][TCP].show()
###[ TCP ]###
sport= 55555
dport= http
seq= 0
ack= 0
dataofs= None
reserved= 0
flags= S
window= 8192
chksum= None
urgptr= 0
options= {}
>>>
rep[0][1][TCP].show()
###[ TCP ]###
sport= http
dport= 55555
seq= 3631598158
ack= 1
dataofs= 6
reserved= 0
flags= SA
window= 14600
chksum= 0x5f04
urgptr= 0
options= [('MSS', 1460)]
###[ Padding ]###
load= 'I5'
>>>
Envoi d'un paquet TCP sur le port 22 d'un serveur alors que le service SSH n'est pas actif
>>> paquet = IP(dst='192.168.1.100') / TCP(sport=55555, dport=22, flags='S')
>>> rep, non_rep = sr(paquet)
Begin emission:
..Finished to send 1 packets.
..*
Received 5 packets, got 1 answers, remaining 0 packets
>>> rep.show()
0000 IP / TCP 192.168.1.159:55555 > 192.168.1.100:ssh S ==> IP / TCP 192.168.1.100:ssh > 192.168.1.159:55555 RA / Padding
>>> rep[0][1][TCP].show()
###[ TCP ]###
sport= ssh
dport= 55555
seq= 0
ack= 1
dataofs= 5
reserved= 0
flags= RA
window= 0
chksum= 0x5262
urgptr= 0
options= {}
###[ Padding ]###
load= '\x00\x00\x00\x00\x00\x00'
>>>
Le résultat est toujours composé des deux couples paquet émis / paquet reçu.
Le paquet envoyé sur le port 22 (Scapy affiche ssh à la place du numéro de port 80) avec le flag positionné à SYN (Scapy affiche un S pour l'attribut flags ce qui correspond à la valeur numérique 2)
le paquet reçu correspond a les flags positionnés à RESET et ACK (Scapy affiche RA ce qui correspond à la valeur numérique 20) ⇒ le port 22 n'est pas ouvert.
>>> rep[0][1][TCP].flags
20
>>>
Préciser plusieurs ports
Il est possible d'utiliser une liste de valeurs pour les attributs des protocoles sous la forme :
dport=[80,443] pour indiquer une liste de ports : ici uniquement les ports 80 et 446 ;
dport=(80,443) pour indiquer une plage de valeurs : ici les ports allant de 82 à 443.
Scan de port UDP
Rappel : Un port UDP est simplement en écoute car UDP ne gère pas l'établissement de connexions.
Exemple de scan UDP pour déterminer si le service DNS est actif sur un hôte (en écoute)
Les paramètres à utiliser
Deux réponses sont possibles :
>>> paquet = IP(dst='192.168.1.1') / UDP(dport=53) / DNS()
>>> rep, non_rep = sr(paquet)
Begin emission:
..Finished to send 1 packets.
..*
Received 5 packets, got 1 answers, remaining 0 packets
>>> rep.show()
0000 IP / UDP / DNS Qry ==> IP / UDP / DNS Ans / Padding
>>> paquet = IP(dst='192.168.1.100') / UDP(dport=53) / DNS()
>>> rep, non_rep = sr(paquet)
Begin emission:
..Finished to send 1 packets.
.*
Received 4 packets, got 1 answers, remaining 0 packets
>>> rep.show()
0000 IP / UDP / DNS Qry ==> IP / ICMP 192.168.1.100 > 192.168.1.159 dest-unreach port-unreachable / IPerror / UDPerror
>>>
Retour à Python : la bibliothèque Scapy ...