Fetch the Flag CTF 2022 : solution de Juggalo Central
Alexander Kraev
10 novembre 2022
0 minutes de lectureMerci d’avoir joué à Fetch avec nous ! Félicitations aux milliers de joueurs qui nous ont rejoints pourFetch the Flag CTF. Et un immense merci aux Snykers qui ont créé, testé et documenté les défis !
Si vous avez participé à l’édition 2022 de Fetch the Flag de Snyk et n’avez pas réussi à terminer le défi Juggalo Central, vous êtes au bon endroit. Découvrons ensemble la solution !
Guide pas à pas
Pour commencer, un lien mène à un site web contenant le formulaire de connexion habituel, avec deux champs : nom d’utilisateur et mot de passe. Il y a aussi un fichier avec une extension .php. Commençons par examiner le site web.
Le site web
Comme indiqué précédemment, la page comporte deux champs de saisie (et une image de clown). J’ai d’abord vérifié le code source de la page, le fichier .css et même l’image. Rien d’exploitable. J’ai remarqué qu’il y avait deux messages d’erreur différents : l’un lorsque j’utilisais un nom d’utilisateur courant comme « admin », et l’autre lorsque j’entrais une chaîne aléatoire comme « qweasdzxc ».
Le code
Dans index.php, j’ai trouvé trois fonctions : getFlag, searchUserByName et validateCredentails.
La première fonction, getFlag, interroge une table de base de données appelée flag et renvoie la toute première ligne trouvée. Après quelques calculs, j’en ai conclu que la clé recherchée était stockée dans cette table.
La deuxième fonction, searchUserByName, cherche un utilisateur à partir de la variable d’entrée appelée $username. Si des résultats sont trouvés, la fonction les renvoie. J’ai remarqué cette ligne :
Cela signifie que cette fonction ne renverrait pas forcément une seule ligne (je m’attends à ce que les utilisateurs aient des noms d’utilisateur uniques) : s’il existe plusieurs utilisateurs portant le même nom, elle les renverra tous. J’ai découvert par la suite que cela ne nous serait pas utile, à cause de ce qui se passe dans la fonction suivante. Ça arrive parfois dans un CTF.
La troisième fonction, validateCredentails, lit le nom d’utilisateur et le mot de passe transmis dans le corps de la requête POST, puis appelle searchUserByName pour vérifier s’il existe un utilisateur portant ce nom. J’ai également compris pourquoi le message d’erreur était différent pour un nom d’utilisateur composé de caractères aléatoires. Si le nom d’utilisateur fourni n’existe pas, la fonction renvoie : « Juggalo not found! ». En revanche, pour tout nom d’utilisateur existant associé à un mot de passe incorrect, le message d’erreur est : « Invalid credalo ».
Trouver un nom d’utilisateur valide
Nous allons maintenant chercher des identifiants valides. Lorsque j’ai essayé un nom courant comme « admin », j’ai obtenu le message d’erreur « Invalid credalo ». Grâce à l’analyse du code effectuée à l’étape précédente, je peux maintenant affirmer qu’un utilisateur possède la valeur « admin » dans son champ de nom d’utilisateur.
Première hypothèse
Maintenant que nous avons un nom d’utilisateur valide, voyons comment pirater le mot de passe. Le message d’erreur indiquant que nous n’avons pas accès à cet utilisateur est dû à la ligne de code suivante :
À gauche de la condition, le mot de passe de l’utilisateur est récupéré dans la base de données. À droite, on trouve les 20 premiers caractères du hachage md5 calculé à partir de deux variables fournies par l’utilisateur : username et password.
Ma première idée était que cette condition impliquait que tout mot de passe valide devait être une chaîne de 20 caractères. En bref, je me trompais.
Deuxième hypothèse
Après avoir réfléchi un moment au défi, une autre idée m’est venue. Cette ligne utilise l’opérateur de comparaison !=. Je suis donc allé consulter la documentation officielle de PHP pour vérifier le fonctionnement des opérateurs de comparaison dans PHP. Une phrase de la documentation, juste à côté de l’opérateur !=, m’a laissé perplexe :
Attendez. Qu’est-ce que la « coercition de type » ? Après avoir lu cela, j’ai rouvert la page web du défi. Le clown était toujours là, à me regarder.
Le piratage
Il existe en fait en PHP un mécanisme appelé « coercition de type ». Après avoir lu un article sur le sujet et exécuté quelques tests PHP en ligne, j’ai trouvé la condition suivante :
Étonnamment, cela a renvoyé « true ». Cela s’explique par le fait que PHP convertit les deux opérandes en valeurs numériques. Pour trouver le bon mot de passe de l’utilisateur « admin », il nous suffit maintenant de générer un hachage md5 qui répond à deux conditions : commencer par « 0e » et ne contenir que des chiffres ensuite (ou au moins 18 caractères après, en raison de l’utilisation de substr).
Le script
Voici un script Python qui permet de trouver le bon hachage :
Au bout d’une minute, j’ai pu me connecter à la page web Juggalo Central avec les identifiants générés par ce script.
Autres solutions de Fetch the Flag
Vous voulez savoir comment nous avons trouvé tous les autres flags ? Consultez notre page Solutions de Fetch the Flag pour découvrir notre démarche.



