====== 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**) * sur le **port 22** du **serveur** 192.168.1.159 * avec le client 192.168.1.196 sur le **port 50264**. 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 : {{ :dev:python:scapy:tcp_connect.svg.png |}} - 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 ==== * valeurs du segment TCP à préciser : * * **port de destination 80** pour le service Web * * **port source client 55 555** (choisi au dessus de 49 151) * * **flag** positionné à **SYN** (valeur S) * création du segment encapsulé dans un paquet IP destiné à l'hôte 192.168.1.1 (Box Internet) : >>> paquet = IP(dst='192.168.1.1') / TCP(sport=55555, dport=80, flags='S') >>> * envoi du paquet >>> 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= {} >>> * le paquet reçu correspond a les flags positionnés à SYN et ACK (Scapy affiche SA ce qui correspond à la valeur numérique 18) => le port 80 est ouvert. 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 ==== * valeurs du segment TCP à préciser : * * **port de destination 22** pour le service SSH * * **port source client 55 555** (choisi au dessus de 49 151) * * **flag** positionné à **SYN** (valeur S) * création du segment encapsulé dans un paquet IP destiné à l'hôte 192.168.1.100 (Imprimante réseau) : >>> 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 * le nom/adresse IP du serveur DNS dans le **paquet IP**, * Le **port UDP** a utilisé et qui est **53** dans le **segment UD**P. * il faut **encapsuler** les **informations DNS** sans préciser la requête de résolution de nom car il s'agit d'un simple scan. Deux réponses sont possibles : * Si le port est **ouvert** et donc le service DNS est **actif**, l'application répond en **UDP**, * Si le port est **fermé**, l'application répond avec un message d**'erreur ICMP port-unreachable**. >>> 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 ... ==== * [[dev:python:scapy:accueil|Python : la bibliothèque Scapy pour manipuler les paquets réseau]]