Injections SQL Classiques – Théorie, pratique et stratégies d’attaques avec DVWA
Rappel : L’article qui va suivre contient des techniques et méthodes qui peuvent s’avérer être illégales et dangereuses si elles sont utilisées à mauvais escient. Ce tutoriel ne doit en aucun cas être utilisé contre un particulier, une entreprise, une organisation à but non lucratif ou une administration publique de quelconque pays. Je me dédouane de toute responsabilité en cas de problèmes ou d’incidents de quelque nature que ce soit après le visionnage de cet article.
Bonjour à tous, dans cet article, nous allons aborder les « fameuses » injections SQL, qui font toujours autant de dégat malgré toutes les contre-mesures qu’il existe de nos jours. (mais qui ne sont pas souvent mises en oeuvre malheuresement surtout sur les applications vieillissantes)
Les serveurs hébergent des SGBD (Système de gestions de base de données) utilisant le language de requête SQL peuvent être vulnérables à plusieurs types d’attaques (Listes non-exhaustive ci-dessous). Ils peuvent être attaqués soit directement, soit à travers un site web dynamique utilisant une ou plusieurs bases de données.
- Abus de privilège excessif
- Abus de privilège légitime
- Elévation de privilège
- Exploitation de failles des bases de données vulnérables ou mal configurées
- Injection SQL
- Déni de service
- Vulnérabilités des protocoles de communication des bases de données
- Copies non autorisées de données sensibles
- Exposition de données de sauvegarde
1. Qu-est-ce qu’une injection SQL ?
Tout d’abord, une injection SQL est un type d’attaque informatique qui a pour but d’attaquer une base de données en envoyant des portions de requêtes SQL malicieuse (injections d’une suite de caractères ayant pour but d’altérer l’intégrité de la requête finale) afin d’essayer de récupérer des informations de la base de données (structure, utilisateurs, mots de passe, version du SGBD etc.). L’objectif final étant d’outrepasser la sécurité de l’application, « Trompant » la BDD afin quel nous renvois le résultat que l’attaquant souhaite.
Analogie :
Vous êtes sur le point de vous rendre à la banque pour effectuer une transaction bacaire au nom de votre patron car il doit se rendre à un rendez-vous d’affaire urgent. Il vous a confié une enveloppe avec des instructions précises à donner au banquier.
Voici l’instruction qui vous a laissé dans l’enveloppe :
Hello John, (initiation de la connexion vers BDD)
Pouvez-vous éffectuer un virement de 1500 euros depuis le compte FR yyyy yyyy yyyy yyyy yyyy yyy vers le compte FR xxxx xxxx xxxx xxxx xxxx xxx (Requête SQL légitime)
Je vous remercie par avance, Bob. (;)
Vous laissez l’enveloppe hors de votre vue pendant quelques minutes le temps de prendre un café avant de partir à la banque.
Une autre personne Monsieur S travaillant comme prestataire pour l’entreprise SQL a repréré cette enveloppe quelques minutes après vous avoir dit bonjour. Il se trouve que cette personne est mal intetionnée.
Il ouvre l’enveloppe confidentiel le temps de votre pause et ajoute au-dessus de la signature : « Aussi (Opérateur UNION SQL) Transférez 10 000 euros du compte FR yyyy yyyy yyyy yyyy yyyy yyy vers le compte FR zzzz zzzz zzzz zzzz zzzz zzz
«
Maintenant, le message complet se lit comme suit :
Hello John, (initiation de la connexion vers BDD)
Pouvez-vous éffectuer un virement de 1500 euros depuis le compte FR yyyy yyyy yyyy yyyy yyyy yyy vers le compte FR xxxx xxxx xxxx xxxx xxxx xxx Aussi (UNION) Transférez 10 000 euros du compte FR yyyy yyyy yyyy yyyy yyyy yyy vers le compte FR zzzz zzzz zzzz zzzz zzzz zzz (Injection SQL)
Je vous remercie par avance, Bob. (;)
Le banquier (La BDD) vérifie que vous êtes une personne autorisée (Utilisateur de la bdd) pour utiliser le compte y (BDD/Table) et suit les instructions à la lettre, car votre chef à prévenu la banque de votre passage.
J’espère que cette analogie vous aura permis de mieux visualiser ce qu’est une injection SQL ^^
Il y a deux grandes familles pour les injections SQL à savoir :
- in-band SQLi : La plus courante et la plus facile à exploiter. L’injection SQL in-band se produit lorsqu’un attaquant est capable d’utiliser le même canal de communication pour lancer son attaque. Il existe deux sous-variants de cette méthode :
- SQLi basé sur les erreurs : La méthode error based est une technique qui s’appuie sur les messages d’erreurs émis par le SGBD pour obtenir des informations sur la structure de la base de données. Bien que les erreurs soient très utiles pendant la phase de développement d’une application Web, elles doivent être désactivées sur un site en production ou enregistrées dans un fichier de log à accès restreint.
- SQLi basé sur l’union : Cette SQLi exploite l’opérateur SQL UNION pour combiner les résultats de deux ou plusieurs instructions SELECT en un seul résultat. Le flux de données corrompu est ensuite renvoyé vers l’utilisateur (dans le cadre de la réponse HTTP).
- blind SQLi : Une blind SQL Injection (ou injection SQL en aveugle en français), c’est un peu un jeu de devinettes. Je m’explique : la BDD que vous interrogez ne peut répondre que par oui ou non. L’injection « blind » est presque identique à l’injection SQL in-band. La seule différence est la façon dont les données sont extraites de la base de données. Lorsque la BDD ne fournit pas de données à afficher à une page web directement, l’attaquant est obligé de voler des données en posant à la BDD une série de questions (Requête malicieuse). Cela rend l’exploitation plus difficile, mais pas impossible. Il existe deux sous-catégories :
- Boolean based : L’attaquant envoie une requête SQL à la BDD invitant l’application à renvoyer un résultat. Le résultat variera selon si la requête est vraie ou fausse. En fonction du retour, les informations contenues dans la réponse HTTP seront modifiées ou resteront inchangées. L’attaquant peut alors déterminer si le message a généré un résultat vrai ou faux, en fonction de sa stratégie d’attaque.
- Time based : L’injection SQL basée sur le temps est une technique d’injection SQL qui repose sur l’envoi d’une requête SQL à la base de données qui oblige la base de données à attendre un laps de temps spécifié (en seconde) avant de répondre. Le temps de réponse indiquera à l’attaquant si le résultat de la requête est VRAI ou FAUX. Selon le résultat, une réponse HTTP sera renvoyée avec un délai, ou renvoyée immédiatement. Cela permet à un attaquant de déduire si l’injection a renvoyé une réponse correcte ou non.
- out of band SQLi(njection) : Dans cette configuration, l’attaquant peut mener cette forme d’attaque que lorsque certaines fonctionnalités sont activées sur la BDD. Cette SQLi est principalement utilisée comme alternative aux techniques SQLi « Classique » vu ci-dessus. Le SQLi hors bande est utile lorsque le hacker ne peut pas utiliser le même canal de communication pour lancer l’attaque, ou bien lorsqu’un serveur est trop lent ou instable pour que ces actions soient effectuées. Ces techniques misent sur la capacité du serveur à créer des requêtes DNS ou HTTP pour le transfert des données. Notez que l’attaquant doit contrôler ledit serveur HTTP ou DNS. C’est ce dernier point qui rend rare et très complexe cette attaque.
D’autres variantes de ces techniques existent à savoir :
- Injection SQL par sous requête et empilement (Stacked Queries SQLi)
- Injection SQL par requête XPATH
- SQL injection + insufficient authentication [7]
- SQL injection + DDoS attacks [8]
- SQL injection + DNS hijacking [9]
- SQL injection + XSS [10]
Mais si vous retenez déjà le schéma ci-dessus c’est déjà très bien. L’avenir nous réserve surement de nouvelles sous-catégories d’SQLi, mais le principe général restera le même.
2. Prérequis :
- Kalilinux avec une version récente (2020.X / 2021.X), disposant d’une connexion internet.
- Une bonne connaissance globale des SGDB ainsi que du langage SQL est requise. (Pas besoin non plus d’être un SQL Master ^^)
- Avoir déjà développé une application web HTML/CSS/PHP/JS, même basique sera un gros plus, car cela vous permettra de comprendre toutes les ficelles du fonctionnement interne de ce type d’attaque.
- Maitriser Docker sera apprécié, et vous fera gagner beaucoup de temps, pour l’installation de notre application web vulnérable DVWA. ^^
Dans cet article, nous n’allons pas utiliser d’outils avancés comme SQLMAP, mais un deuxième article fera l’objet de cet outil prochainement. Nous allons ici réaliser nos requêtes d’injection de manière classique : « à la main ».
3. Préparation de notre lab de tests
Pour réaliser les tests vous avez deux solutions :
- Installez DVWA sur votre machine kalilinux de manière classique. L’installation prend environ 15 bonnes minutes si-vous êtes rapide. https://www.youtube.com/watch?v=PaB17Cc0dUg&ab_channel=edureka
- Une fois que vous avez DVWA d’installé et de fonctionnelle sur votre poste, n’oubliez pas d’activer au démarrage vos services mariadb et apache2. Par défaut kalilinux ne le fait pas… ça évite les mauvaises surprises 😉
systemctl enable mariadb apache2
- Une fois que vous avez DVWA d’installé et de fonctionnelle sur votre poste, n’oubliez pas d’activer au démarrage vos services mariadb et apache2. Par défaut kalilinux ne le fait pas… ça évite les mauvaises surprises 😉
- Ou alors, Installer DVWA avec docker pour setuper votre environnement avec une commande ! « One line setuping is life », DOCKER IS LIFE
Pour ma part, je vais utiliser la deuxième option.
Récupérons dans un premier temps l’image docker de DVWA à l’aide de la commande suivante :
docker run -d -p 8080:80 vulnerables/web-dvwa
Dirigez-vous ensuite vers l’url http://localhost:8080/setup.php, igniorez les messages en rouge qui nous concerne pas dans notre cas, puis cliquez sur « Create / Reset Database.
Patientez une seconde le temps que votre base de données ainsi que ses tables soit créée dans le conteneur dvwa. Vous allez être ensuite redirigé vers une page de connexion. Les identifiants par défaut son admin / password.
La création de la base de données MYSQL s’est faite avec succès ! Votre application web vulnérable est fin prête à être exploité. Connectez-vous dès à présent.
Avant de continuer, vérifier que le niveau de sécurité est bien fixé sur « Low ». Nous verrons dans la section 6 comment exploiter les niveaux suivants. Il est toujours préférable d’y aller « Créchendo » afin de ne pas bruler des étapes dans la compréhension d’une nouvelle notion aussi abstraite que celle-ci.
Dirigez-vous ensuite dans le sous-menu « SQL Injection », qui contient un formulaire de saisie tout simple.
Bien, notre environement de test étant opérationnelle, nous allons pouvoir dès à présent passer à la pratique !
Ci-dessous, je vais dérouler la road map de mon attaque (par le biais des requêtes d’injection présentés plus bas). Je ne vais pas utiliser toutes les sous-catégories d’injection SQL présenté ci-dessus, car le but de finale est de comprendre petit à petit comment la BDD derrière l’application WEB est configurée dans son ensemble.
Note : DVWA vous permet de visualiser le code source PHP, ce qui peut être très utile pour la compréhension si-vous venez de base du monde du développement informatique. Je ne suis pas à la base Dev, mais après avoir réalisé quelques projets je comprends ce code, mais je trouve que vous l’expliquer ligne par ligne ne sera pas intéressant ni pour vous ni pour moi. Rien ne vaut la pratique et les tests.
4. Stratégies d’attaques et réalisation d’injections SQL (Low Level)
0. connaître le nombre de colonne traité par la requête SQL original.
C’est une étape cruciale pour éviter de perdre temps par la suite. Nous devons impérativement trouver le nombre de colonnes qui est traité par la requête SQL original. Grâce à l’opérateur ORDER BY, nous allons « à taton » vérifier combien de colonnes sont traités.
Éxécutez cette première injection SQL.
1' order by 2 #
À première vu quand, on regarde le retour, on observe 3 lignes de retour :
- ID
- First Name
- Surname
Mais ne vous fiez pas aux apparences, ce n’est pas parce que DVWA nous retourne 3 lignes, que la commande traite 3 colonnes obligatoirement. (Surtout si la requête incorpore un opérateur SQL WHERE x=x)
Maintenant, si nous essayons d’augmenter le nombre de colonnes à « Ordonner » nous allons obtenir l’erreur suivante :
1' order by 3 #
Cela signifie que seules 2 colonnes sont renvoyées lors de l’exécution de la requête ci-dessus, qui sont dans ce cas le First_Name et le Last_Name de l’utilisateur. Pour chacune de nos requêtes ci-dessous, nous garderons ce paramètre en tête, afin de construire de manière efficiente nos SQLi.
- Obtenir la version du SGBD mariadb ainsi que le système d’exploitation qui l’héberge :
1' OR 1=1 UNION SELECT null,version()#
Pour cette injection SQL, nous avons besoin de faire croire à MYSQL, que nous souhaitons récupérer des données provenant de deux colonnes (alors qu’au final, seul le numéro de version du SGBD nous intéresse). Afin de ne pas générer une erreur de syntaxe, nous avons besoin de préfixer une colonne « ghost » null, qui ne nous retournera rien, mais qui permettra de ne pas avoir de soucis avec la syntaxe SQL. En effet, suite à nos deux tests effectués plus haut, nous en avons déduit que la requête SQL de base (sans injection) est censé nous afficher 2 données.
- First Name
- Surname
Il faut donc que la deuxième partie de notre requête à partir du mot-clé « UNION » s’aligne sur la même contrainte, et que notre deuxième SELECT contienne lui aussi deux colonnes.
Petit rappel : La commande UNION de SQL permet de mettre bout-à-bout les résultats de plusieurs requêtes utilisant elles-même la commande SELECT. C’est donc une commande qui permet de concaténer les résultats de 2 requêtes ou plus. Pour l’utiliser il est nécessaire que chacune des requêtes à concaténer retournes le même nombre de colonnes, avec les mêmes types de données et dans le même ordre.
https://sql.sh/cours/union
Ce procédé sera répété pour chacune des requêtes ci-dessous, sauf pour l’injection 9, ou j’explique comment il est possible de bypasser cette restriction. On ne va pas aborder cette notion maintenant, car j’ai peur que certains passe l’arme à gauche si j’explique ce concept maintenant ^^
2. Afficher l’utilisateur « courrant » que l’app web utilise pour interragir avec la bdd :
Quand je parle d’utilisateur courant, je pense à l’utilisateur qui est présent dans la fonction de connexion à la base de données. Ce paramètre va nous êtres utile car il nous permettra de vérifier la portée de ses droits sur la BDD.
1' OR 1=1 UNION SELECT null,USER();#
On constate que l’utilisateur se prénomme « app » et qu’il peut seulement se connecter via localhost.
3. Énumérer toutes les databases que l’utilisateur app peut gérer
- Pour aller plus loin : https://dev.mysql.com/doc/refman/8.0/en/information-schema.html
1' OR 1=1 UNION SELECT null,schema_name FROM information_schema.schemata#
L’utilisateur app de notre db peut donc interagir avec seulement deux bases de données à savoir : dvwa, information_schema
4. Tenter de récupérer la liste de tous les utilisateurs de la db.
1' OR 1=1 UNION SELECT null,user FROM mysql.user#
Heureusement l’utilisateur app n’a pas ce privilège, et l’injection nous retourne une erreur assez explicite.
5. Tenter de récupérer la liste des privilèges de tous les users (qui peuvent exécuter les fonctions, SELECT, SHOW etc.)
1' OR 1=1 UNION select GRANTEE,IS_GRANTABLE from information_schema.user_privileges#
Malheureusement, dans ce cas, notre utilisateur n’est pas « Grantable* » et ne dispose pas de tous les droits sur les bases de données, donc en tant qu’utilisateur app, je ne pourrai pas afficher les droits des autres users, ni listers ces derniers, ni interagir avec des db dont je ne suis pas propriétaire.
* Grantable : https://dev.mysql.com/doc/refman/8.0/en/grant.html
5.a. Si-vous souhaitez savoir quel type de fonction SQL, l’utilisateur app peut apeller, exécutez la commande ci-dessous :
1' OR 1=1 UNION select GRANTEE,PRIVILEGE_TYPE from information_schema.user_privileges
#
6. Récupérer toutes les tables de nos deux BDD accessibles par app (dvwa, information_schema)
1' OR 1=1 UNION SELECT null,table_name FROM INFORMATION_SCHEMA.tables#
7. Récupérer le hash du mot de passe de l’utilisateur courant. (app)
1' OR 1=1 UNION SELECT null,PASSWORD('mypass')#
Vous pouvez essayer de trouver la valeur du hash en clair si l’envis vous en prend. Pour cela, ne brûlez pas les étapes.
- Analyser le hash avec l’outil hash identifier
- Tentez de déchiffrer le mot de passe en ligne, en le comparant à d’autres hashs du même algo ayant déjà fuité.
- Si vous n’obtenez pas de résultat convaincant, vous pouvez toujours essayer de brute forcer le hash avec un outil comme john the ripper.
Après cet petite aparté, continuons nos injections :
8. Énumérer toutes les colonnes de la table « users », afin de comprendre un peu mieux le fonctionnement de celle-ci.
1' OR 1=1 UNION SELECT null,column_name FROM INFORMATION_SCHEMA.columns WHERE table_name='users'#
Nous voilà en présence de la liste des colonnes de la table users. Nous voyons qu’une colonne « password » existe, (hum… étonnant ^^). Le but va donc d’être de pouvoir énumérer tous les utilisateurs ainsi que les hashs de leurs mots de passe.
9. Énumération des utilisateurs de la table users, et récupération du hash des mots de passe
1′ OR 1=1 UNION SELECT null,concat(user,0x0a,password) FROM users#
Hum… avez-vous que la syntaxe de notre injection à légèrement changé ? effectivement, nous avons utilisé la fonction concat(), avec à l’intérieur les noms de deux colonnes ainsi que la suite de caractères 0x0a entre celles-ci.
Eh bien, car cela nous permet de pouvoir intégrer plus de deux colonnes dans notre retour. (Ce qui n’est pas possible, si-vous n’utilisez pas cette fonction concat()). Le fait d’utiliser la string 0x0a, nous permet simplement de revenir à la ligne entre chaque résultat, afin de gagner en clarté.
Autre Exemple :
9.a Récupérer les noms d’utilisateurs les hashs des mots de passe, les dates de dernières connexions, les nombres de connexions échoués, ainsi que les identifiants de tout les utilisateurs de la table users, et tout ça en une seule injection SQL. ^^
1' OR 1=1 UNION SELECT null,concat(user,0x0a,password,0x0a,last_login,
,0x0a
failed_login
,0x0a
,user_id) FROM users#
Et voilà le travail, tous les champs souhaités sont énumérés, et ont à bien un retour chariot entre chaque ligne.
5. Injection SQL annexes classé par famille
/SQL Injections/in-band SQLi/Union based
Déjà abordé plus haut. Ce sont toutes les injections qui utilisent la fonction SQL UNION. Ce type d’injection est généralement couplé avec une injection Boolean Based. (Comme vu ci-dessus.)
/SQL Injections/in-band SQLi/Error based
Déjà abordé plus haut. (Exemple 0.)
/SQL Injections/blind SQLi/Boolean based
Déjà abordé plus haut. Ce sont toutes les injections commençant par 1' OR 1=1
# Suite de l’injection
Ce type d’injection est généralement couplé avec une injection Union Based. (Comme vu ci-dessus.)
/SQL Injections/blind SQLi/Time Based
ID: 1'-sleep(2) UNION select null,version();#
/SQL Injections/out of band SQLi
Ce type d’injection est rare contenu de sa complexité de mise en oeuvre et du fait quelle repose sur l’activation de fonctionnalités spécifiques de la BDD qui sont désactivées par défaut.
Pour plus d’informations : https://blog.raw.pm/en/types-of-sql-injection/#Out-of-band-SQLi
6. Low/Medium/High Level – DVWA SQL Injection
A. Low
Jusqu’à présent, nous étions en mode de sécurité « Low », je vous propose d’explorer les deux niveaux suivants à savoir Medium et high avec deux exemples.
B. Medium
Pour ce deuxième niveau de difficulté nous avons besoin d’un logiciel permettant d’intercepter et de modifier le flux de données envoyées dans le formulaire, afin d’y insérer notre injection SQL. Il existe deux logiciels très connus qui permette de réaliser cela :
- Burp suite (Pas totalement open source)
- OWASP Zap (Fully Open source)
Dans mon cas, même si j’aime bien Burp, je vais utiliser le logiciel OWASP Zap. Pour l’installation, je vous laisse télécharger le fichier suivant : Linux Installer
https://www.zaproxy.org/download/
Mettez les droits d’exécution sur ce fichier.
chmod u+x fichier.sh
Puis exécutez le script. Cette action doit être réalisée depuis l’interface graphique de Kali Linux, car le script interagit avec différents composant graphiques de la GUI de kali, qui ne sont bien évidament pas supporté via SSH ^^
./fichier.sh
Lancez OWASP Zap, depuis le menu de recherche de kali, puis cliquez sur le navigateur Firefox « Embarqué »
Dirigez-vous vers l’URL de votre application web vulnérable DVWA, en n’oubliant pas (si cela n’est pas déjà fait) de régler le niveau de sécurité sur Medium.)
Lancé par la suite une simple requête dans le formulaire, puis cliquez sur la dernière requête de type post en bas de votre navigateur en déroulant le sous menu intégré au navigateur embarqué, qui permet d’observer le trafic réseaux qui est échangé lors du chargement d’une page web entre le client et le serveur. (Navigateur Firefox embarqué dans OWASP ZAP <—> service web vulnérable DVWA).
Note : au cas où vous vous poseriez des questions sur ma mise en page, j’utilise OWASP ZAP 2.10 qui comprend un HUD (les boutons gauche / droite et le sous-menu), qui permette de réaliser différentes méthodes d’analyse et surtout de gagner en rapidité.
Grâce à cette sub-console, nous pouvons observer un ensemble de lignes. (description des fluxs de données qui a transité lors de notre choix dans le formulaire. Comme vous pouvez le constater, nous avons le contenu de la requête post qui nous est affiché.
Le but maintenant, va être de modifier l’intégrité du flux de données qui a transité sur le réseau par le biais de la méthode POST (id=2&Submit=Submit), afin de pouvoir injecter notre SQLi. Une fois celle-ci concoctée, nous allons « répéter l’envoi de la requête », en cliquant sur replay in browser, pour obtenir la version Web HTML du rendu.
Voici l’injection SQL. (Déjà exécuté plus haut : Section : 4 ) :
id=2 UNION SELECT null,concat(user,0x0a,password) FROM users &Submit=Submit
Le niveau Medium n’a plus de secret pour vous dès à présent.
C. High
Ce niveau est sur le fond très similaire au niveau low, mais cette fois, les données envoyées dans le formulaire transitent depuis un autre onglet navigateur. Cliquez donc sur le lien hyperlien de l’image, puis entré une injection SQL de votre choix parmi celles présenté dans la section 4.
Je tiens à préciser quand même, que dans ce cas-là, l’injection SQLi manuelle est plus facile qu’avec un logiciel comme SQL Map, où il faudrait gérer la redirection entre :
- https://localhost:8080/vulnerabilities/sqli/ (Onglet principal)
- https://localhost:8080/vulnerabilities/sqli/session-input.php# (Deuxième onglet)
7. Contre-mesures face aux injections SQL – Conclusion
Bon j’avoue que l’article est déjà bien assez long comme ça, et je ne sais pas si quelqu’un d’autre que moi verra un jour ces quelques lignes de conclusion ^^. Je vous laisse en compagnie d’une image qui résume les best practices à mettre en oeuvre pour protéger une application web de ce genre d’attaque, ainsi qu’un guide complet avec des exemples et des bouts de code qui provient du site officiel de OWASP Zap :
https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
++
Geoffrey
Je terminerai cet article par cette phrase :
8. Webographie
https://www.oracle.com/fr/security/injection-sql-attaque.html ;
https://www.imperva.com/learn/application-security/sql-injection-sqli/ ;
https://medium.com/@hninja049/example-of-a-error-based-sql-injection-dce72530271c ;
https://shehackske.medium.com/how-to-setup-dvwa-on-docker-37d1f1bba190 ;
https://analyse-innovation-solution.fr/publication/fr/hacking/injection-sql-sqli-dorks ;
https://www.mysqltutorial.org/mysql-grant.aspx ;
En savoir plus sur Le Guide Du SecOps
Subscribe to get the latest posts sent to your email.