kuji@portfolio:~/projets$ cat portscan-v2.md
Scanneur de port, v2 : 5 threads
Le scan est réparti sur 5 threads qui se partagent les ports grâce à un mutex, et l'attente par port passe de 2 secondes à 200 ms.
Source
Lien vers le repo GitHubCe qui change
La technique de connexion (connect_with_timeout) reste celle de la version 1. Ce qui change, c'est la façon de répartir le travail : la v1 était trop lente pour un usage réel en CTF, ce qui m'a poussé à paralléliser le scan.
- Cinq threads.
thread_scanest exécutée par 5 threads POSIX (pthread_create), et lemainles attend tous avecpthread_join. - Un compteur partagé. Chaque thread prend le port suivant dans la variable globale
first_port, de 1 à 10 000. Un thread bloqué sur un port lent ne retarde pas les autres, qui continuent d'avancer : la charge se répartit d'elle-même, sans découper les ports à l'avance. - Un mutex autour du compteur. Lire puis incrémenter
first_portn'est pas atomique : sans verrou, deux threads pourraient lire la même valeur et scanner le même port. Le mutex est relâché avant le scan, pour ne pas bloquer les autres pendant l'attente réseau. Le reste (socket,sockaddr_in) est local à chaque thread, donc sans risque. - Timeout de 200 ms.
selectreçoit désormaistv_sec = 0ettv_usec = 200000, au lieu de 2 secondes. - Adresse IP en argument. L'IP se passe en ligne de commande (
argv) plutôt qu'avecscanf, ce qui permet d'automatiser le scan. - Ports fermés affichés. Chaque port produit une ligne
OPENEDouCLOSED. Les threads écrivant en parallèle, l'ordre des lignes varie d'une exécution à l'autre.
Utilisation
$ gcc mapping_network_5threads.c -o mapping_network_5threads -pthread
$ ./mapping_network_5threads 127.0.0.1 | grep OPENED
[+] PORT 8080 : OPENED
[+] PORT 9000 : OPENED
Sans grep, le programme affiche une ligne par port, soit 10 000 lignes. L'ordre des résultats peut varier d'une exécution à l'autre.
Limites
- Le nombre de threads est fixé à 5 dans le code :
thread1àthread5etargs1àargs5sont copiés-collés. En changer impose de modifier le code et de recompiler. C'est ce que corrige la version 3. - 200 ms suffisent en local ou sur un réseau rapide, mais un port ouvert sur une machine lointaine, à forte latence, peut être classé « CLOSED » à tort. Le programme ne distingue pas non plus un port fermé d'un port filtré.
Code source
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#include <pthread.h>
struct ScanArgs_Mutex{ char* ip_to_scan; pthread_mutex_t* mut;};
int last_port = 10000;
int first_port = 1;
int connect_with_timeout(int s, struct sockaddr_in* addr, int timeout_sec){
int flags = fcntl(s, F_GETFL, 0);
fcntl(s, F_SETFL, flags | O_NONBLOCK);
int res = connect(s, (struct sockaddr*)addr, sizeof(*addr));
if (res < 0 && errno != EINPROGRESS){
return -1;
}
if (res == 0){
fcntl(s, F_SETFL, flags);
return 0;
}
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(s, &fdset);
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = timeout_sec;
res = select(s + 1, NULL, &fdset, NULL, &tv);
fcntl(s, F_SETFL, flags);
if (res <= 0){
return -1;
}
int so_error;
socklen_t len = sizeof(so_error);
getsockopt(s, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error != 0){
return -1;
}
return 0;
}
void* thread_scan(void* arg_g){
struct ScanArgs_Mutex* args = (struct ScanArgs_Mutex*) arg_g;
struct sockaddr_in configuration_serveur;
while(1){
pthread_mutex_lock(args->mut);
if (first_port > last_port){
pthread_mutex_unlock(args->mut);
break;
}
int port_to_scan = first_port;
first_port++;
pthread_mutex_unlock(args->mut);
int s = socket(AF_INET, SOCK_STREAM, 0);
if(s < 0){
printf("Error while creating socket. Port %d\n", port_to_scan);
continue;
}
memset(&configuration_serveur, 0, sizeof(configuration_serveur));
configuration_serveur.sin_addr.s_addr = inet_addr(args->ip_to_scan);
configuration_serveur.sin_family = AF_INET;
configuration_serveur.sin_port = htons(port_to_scan);
if (connect_with_timeout(s, &configuration_serveur, 200000) == 0){
printf("[+] PORT %d : OPENED\n", port_to_scan);
}
else{
printf("[+] PORT %d CLOSED\n", port_to_scan);
}
close(s);
}
return NULL;
}
int main(int argc, char* argv[]){
if (argc != 2) {
printf("Usage: %s <IP_Address>\n", argv[0]);
return 1;
}
char* ip_cible = argv[1];
printf("BEGINNING IP SCAN : %s\n", ip_cible);
printf("--------------------------\n");
pthread_mutex_t mut;
pthread_mutex_init(&mut, NULL);
struct ScanArgs_Mutex args1 = {ip_cible, &mut};
struct ScanArgs_Mutex args2 = {ip_cible, &mut};
struct ScanArgs_Mutex args3 = {ip_cible, &mut};
struct ScanArgs_Mutex args4 = {ip_cible, &mut};
struct ScanArgs_Mutex args5 = {ip_cible, &mut};
pthread_t thread1;
pthread_t thread2;
pthread_t thread3;
pthread_t thread4;
pthread_t thread5;
pthread_create(&thread1, NULL, thread_scan, &args1);
pthread_create(&thread2, NULL, thread_scan, &args2);
pthread_create(&thread3, NULL, thread_scan, &args3);
pthread_create(&thread4, NULL, thread_scan, &args4);
pthread_create(&thread5, NULL, thread_scan, &args5);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_join(thread3, NULL);
pthread_join(thread4, NULL);
pthread_join(thread5, NULL);
printf("--------------------------\n");
printf("SCAN FINISHED.\n");
pthread_mutex_destroy(&mut);
return 0;
}