En arrivant sur le challenge on tombe sur une interface de connexion :
Etape 1 : Tenter de comprendre le fonctionnement
Quand on soumet le formulaire il n’y a aucun rechargement de la page, le formulaire n’est donc pas soumis en tant que formulaire classique, c’est certainement du javascript qui soumet les informations.
Chose qu’on peut facilement constaté via la console du navigateur dans l’onglet Réseau
En affichant le code source (Ctrl+U) on trouve un fichier /static/script.js
qui contient le code :
//Using our API
function login(){
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
var dat = {'username':username, 'password':password};
$.ajax('/api/v1/login/',{
method: 'POST',
data: JSON.stringify(dat),
dataType: "json",
contentType: "application/json",
}).done(function(res){
if (res['status'] == 'success'){
$("#stat").html('<b>Successful Login. Here is your flag: ');
$("#stat").append(res['flag']);
$("#stat").append('</b>');
}
else{
$("#stat").html('<b>Login Failed</b>');
}
}).fail(function(err){
$("#stat").html(err);
});
}
$(document).ready(function(){
$("#navbar ul li a").on('click', function(event){
event.preventDefault();
var page = $(this).attr("href");
$("#main").load(page);
});
});
La partie qui nous intéresse étant :
var dat = {'username':username, 'password':password}; $.ajax('**/api/v1/login/**',{ method: 'POST', data: **JSON.stringify(dat)**, dataType: "json", contentType: "application/json", })
Maintenant on peut choisir notre façon de faire, soit on utilise directement les champs à notre disposition, soit on effectue les requêtes manuellement, par exemple en curl
ou avec un outil tel que postman
.
Etape 2 : Imaginer le code coté serveur
On peut tout à fait imaginer que coté serveur c’est une requête SQL qui est executé.
Dans le cas d’une injection SQL pour de l’authentification, l’objectif étant de faire dire vrai à la requête.
Mais à quoi peut ressembler la requête de base ?
On peut imaginer que la requête ressemblerait à :
SELECT * FROM users WHERE username='**<username>**' AND password='**<password>**'
<username> correspondrait à $_POST['username']
<password> correspondrait à $_POST['password']
mais potentiellement Hashé (on s’amusera à vérifier ça après).
Etape 3 : Exploiter
Ne connaissant ni le username ni le mot de passe, je dois faire en sorte que la requête donne vrai, sans ces infos.
En effectuant par exemple une injection du type :
' OR 1--
(à mettre dans le champ Nom d’utilisateur)
Cela donnerait
SELECT * FROM users WHERE username='' OR 1--' AND password='**<password>**'
le double tiret permettant de commenter ce qu’il y a derrière. Avec cette injection, on obtient directement le flag.
Cela nous donne plusieurs infos sur le code côté serveur.
- 1re info : il n’y a aucun contrôle sur le fait que les champs soient remplis ou non (dans mon test, le champ password était vide).
- 2ᵉ info : ce n’est pas du MySQL (car MySQL nécessiterait un espace après les 2 tirets pour que cela soit considéré comme un commentaire).
Etape 4 : Par curiosité
On va vérifier si le password était hashé dans la requête SQL, en effectuant la même injection, mais cette fois-ci dans le champ password :
' OR 1--
(à mettre dans le champ Mot de passe)
Cette fois, ça ne fonctionne pas, il y a fort à parier que le password est hashé dans la requête.