Sqlmap, l’outil pour identifier et exploiter des injections SQL (SQLi)

Introduction

Sûrement l’une des vulnérabilités les plus connues, et ce depuis bien longtemps, l’injection SQL fait toujours des ravages en 2024. Elle se retrouve chaque année dans bons nombres de nos rapports de pentest.

Par ailleurs, comparativement à 2022, en 2023, les failles d’injection SQL ont été identifiées en tant que CVE 2159 fois. Et dans le dernier Top 10 de l’OWASP qui liste les vulnérabilités les plus critiques et courantes dans les applications web, elles scrutent la troisième position. 

En fonction des cas, une injection SQL peut être fastidieuse à exploiter afin d’évaluer son impact. Heureusement, des outils extrêmement qualitatifs et développés par la communauté open source tel que sqlmap nous facilitent grandement la tâche.

Dans cet article, nous rappellerons le principe des injections SQL et présenterons de façon exhaustive, cas concrets à l’appui, le fonctionnement de sqlmap.

Guide complet sur sqlmap

Petit rappel sur les injections SQL (SQLi)

Tout d’abord, que signifie SQL ? Le « Structured Query Language » est un langage informatique permettant de communiquer avec la base de données et d’y manipuler les données stockées.

La grande majorité des applications web utilisent le SQL afin d’interroger leur base de données. Cependant, en fonction de l’implémentation, des vulnérabilités de type injection SQL, communément appelées « SQLi », peuvent émerger.

Une injection SQL est une attaque permettant de manipuler les bases de données relationnelles d’une application web, comme MySQL, Oracle Database ou Microsoft SQL Server. À ne pas confondre avec les injections NoSQL qui sont des attaques permettant de manipuler des bases de données non relationnelles telles que MongoDB ou CouchDB.

Par exemple, considérons le bout de code PHP suivant :

Requête SQL

On peut voir sur la première ligne que la variable $id prend en valeur le paramètre id de l’URL. Cependant, cette variable $id est directement concaténée dans la requête SQL de la ligne suivante.

Ainsi, le paramètre id étant contrôlable par n’importe quel utilisateur, un attaquant peut injecter du code SQL dans le paramètre id de l’URL et effectuer une multitude d’actions sur la base de données de l’application web.

Par exemple, pour s’assurer de l’injection, l’attaquant peut injecter un délai de 5 secondes dans le paramètre id de l’URL :

Injection SQL dans le paramètre id de l’URL

Côté serveur, la requête SQL sera la suivante :

Requête SQL injectée

Le serveur mettra donc 5 secondes à répondre, ce qui confirme l’injection SQL.

L’impact d’une injection SQL est dans la plupart des cas, critique. En effet, si l’utilisateur de la base de données a assez de droits, comme c’est souvent le cas, une SQLi permet à un attaquant d’exfiltrer l’entièreté de la base de données de l’application web et parfois même de gagner le contrôle du serveur.

Il existe plusieurs types d’injections SQL. Cela dépend du point d’injection et de la technique utilisée pour exploiter l’injection. Ci-dessous, une liste non exhaustive des types de SQLi :

  • UNION-based
  • Boolean-based blind
  • Error-based
  • Stacked queries
  • Time-based blind

Le meilleur moyen de se protéger des injections SQL est d’utiliser des « requêtes paramétrées » aussi appelées « requêtes préparées » qui permettent de définir la structure de la requête SQL avant d’y incorporer les variables.

Ainsi si une variable est contrôlée par un attaquant, celui-ci ne pourra pas s’injecter dans la requête SQL, car sa structure aura déjà été définie auparavant.

Si vous souhaitez plus d’informations et des cas concrets sur les injections SQL en général, nous vous renvoyons vers notre article complet : Injections SQL (SQLi) : principes, impacts, exploitations et bonnes pratiques de sécurité.

Sqlmap : principes et fonctionnement

Bien qu’il soit possible d’exploiter une injection SQL et d’exfiltrer le contenu d’une base de données à la main, il est beaucoup plus simple et rapide d’écrire et d’utiliser un script.

Encore mieux, si vous ne voulez pas réinventer la roue et être encore plus efficace, sqlmap sera là pour vous aider.

Sqlmap est un outil open source qui permet d’automatiser la détection et l’exploitation d’injections SQL. C’est un outil très complet qui propose une multitude de fonctionnalités et d’options permettant d’aller jusqu’à la compromission du serveur SQL si les conditions le permettent.

Avant d’aller plus loin, il est important de noter que sqlmap génère potentiellement beaucoup de trafic et que son utilisation est illégale sans une autorisation de la part du propriétaire du système testé.

Lors d’un pentest, sqlmap est généralement utilisé après qu’une SQLi ait été identifiée. Cela permet notamment de ne pas générer du trafic pour rien. Ensuite, plusieurs étapes sont à suivre pour exploiter l’injection SQL de manière classique.

Voyons cela étape par étape.

Trouver une payload adaptée au type de SQLi

La première étape consiste à trouver un payload adapté au type de SQLi et qui permettra d’exfiltrer la base de données dans les étapes suivantes.

Commande :

L’option « -r » indique à sqlmap le fichier contenant la requête HTTP qui contient le point d’injection. Si l’endroit du point d’injection est connu par l’attaquant, comme c’est le cas pour cet exemple, il est possible de le préciser en ajoutant un astérisque à l’endroit précis où doit être faite l’injection SQL :

Contenu du fichier req.txt

Ici, l’attaquant sait qu’une injection SQL est possible dans la valeur du cookie « TrackingId ».

À noter qu’il est également possible d’utiliser l’option « -u » à la place pour préciser une URL dans le cas où une injection SQL est possible dans un paramètre d’URL.

Résultat :

Après avoir testé plusieurs payloads, sqlmap a enfin trouvé le type d’injection SQL :

Identification du payload et du DBMS

Ici, le type de l’injection SQL est une « stacked query time based-blind », c’est-à-dire qu’il est possible d’exfiltrer des données en jouant avec des délais, car il est impossible d’avoir directement le résultat de l’injection SQL. Également, l’injection est faite avec une nouvelle requête après l’originale d’où le « stacked query ».

Dans le même temps, sqlmap a également identifié le système de base de données utilisé qui est PostgreSQL. Grâce à ces informations, sqlmap peut maintenant utiliser des payloads qui respectent le type de l’injection et le système de base de données afin d’exfiltrer des données.

Exfiltrer le nom des bases de données

La deuxième étape consiste à exfiltrer le nom des bases de données. Cette étape peut d’ailleurs être faite en même temps que la première pour gagner du temps.

Commande :

Commande sqlmap pour énumérer les bases de données

Pour énumérer les bases de données, on rajoute l’option « –dbs ».

Résultat :

Liste des bases de données

Ici, une seule base de données a été trouvée, la base de données « public ».

Énumérer le nom des tables

À la troisième étape, maintenant que l’on connaît le nom de la base de données, on peut énumérer le nom des tables.

Commande :

Commande sqlmap pour énumérer les tables

L’option « -D » précise le nom de la base de données, ici « public », et l’option « –tables » est utilisée pour énumérer le nom des tables présentes dans la base de données « public ».

Résultat :

Deux tables ont été trouvées, « tracking » et « users ». La deuxième table semble intéressante et peut contenir des données liées aux utilisateurs de la plateforme.

Choisir une table intéressante

La quatrième étape consiste donc à choisir une table intéressante à creuser. Pour cela, nous énumérons ses colonnes.

Commande :

Commande sqlmap pour énumérer les colonnes

L’option « -T » est utilisée pour spécifier la table « users » et l’option « –columns » pour énumérer le nom des colonnes.

Résultat :

Liste des colonnes

Énumérer les données de la table

Pour la cinquième et dernière étape, maintenant que nous avons une vue d’ensemble sur la table « users », on peut choisir d’énumérer l’entièreté des données de la table ou seulement les données de certaines colonnes. Dans notre cas, nous énumérons les données des colonnes « username » et « password ».

Commande :

Commande sqlmap pour exfiltrer les données d’une table

Avec l’option « -C », on choisit nos colonnes séparées par une virgule. L’option « –dump » est là pour exfiltrer les données de la table.

Résultat :

Liste des données exfiltrées

On a donc réussi à exfiltrer le nom et le mot de passe de chaque utilisateur de la plateforme grâce à sqlmap.

Pour aller plus loin et établir une meilleure compréhension de l’environnement attaqué, il est toujours intéressant de récupérer les informations liées au système de bases de données. Avec sqlmap, on peut donc récupérer :

  • L’utilisateur système qui utilise actuellement la base de données
  • Si l’utilisateur système actuel a le rôle de DBA (Data Base Administrator) ou non
  • Le nom d’hôte
  • Tous les utilisateurs système de la base de données
  • Les privilèges de tous les utilisateurs systèmes

Ci-dessous, les commandes permettant de récupérer ces informations et leurs résultats :

Utilisateur système qui utilise actuellement la base de données

Commande :

Résultat :

Est-ce que l’utilisateur système actuel est DBA ?

Commande :

Résultat :

Nom d’hôte

Commande :

Résultat :

Liste des utilisateurs système de la base de données

Commande :

Résultat :

Privilèges des utilisateurs systèmes de la base de données

Commande :

Résultat :

Références des labs sur Portswigger

Souvent, le résultat de l’injection SQL n’est pas retourné en réponse, c’est le cas des blinds SQLi. Mais alors, comment sqlmap se débrouille pour exfiltrer les données ?

Si une injection SQL est de type blind, il faut trouver une condition qui renvoie une réponse serveur différente en fonction de si elle est vraie ou fausse. Par exemple dans le cas d’une SQLi blind booléenne, le serveur répondra d’une manière différente si une condition booléenne dans la requête injectée est fausse :

Requête SQL avec condition booléenne

Si le serveur répond bien de manière différente, il est possible d’exploiter cela et de récupérer caractère par caractère d’abord le nom des bases de données, ensuite des tables, puis des colonnes et enfin les données.

Ainsi, avec l’injection suivante par exemple, si nous gardons notre exemple d’injection dans l’ID du produit, on pourra savoir si le premier caractère du mot de passe de l’utilisateur « administrator » de la colonne « password » de la table « users » est égal à « 5 » ou non :

Boolean-based blind SQLi

La requête complète côté serveur sera donc :

Le mot de passe commençant bien par « 5 », le serveur nous répond différemment contrairement à si nous avions mis un autre chiffre ou une lettre à la place.

On peut passer aux caractères suivants en incrémentant le deuxième paramètre de la fonction SQL SUBSTRING(). À noter que cette méthode fonctionne, car nous savons que le système de base de données est PostgreSQL et que le type de SQLi est une boolean-based blind.

Ce processus peut être automatisé en créant un script, ce qui est une très bonne idée pour apprendre à exploiter des injections SQL, sinon il est possible d’utiliser sqlmap.

La puissance de sqlmap réside donc dans l’automatisation et la prise en compte de la majorité des cas en termes d’injection SQL. L’outil sqlmap est très complet et est utile pour exploiter bien d’autres types d’injection SQL et pas seulement des blinds. Dans la partie suivante, nous présenterons quelques-unes des nombreuses options « avancées » de sqlmap et nous verrons également la notion de « tampers ».

Ce n’est pas exagéré de dire que sqlmap est un outil complet en termes de SQLi. Essayez d’afficher l’aide complète avec l’option « -hh » et vous aurez déjà une idée de la puissance de cet outil.

Dans cette partie, nous listons certaines options qui peuvent être pratiques lors d’un pentest, mais ne vous y limitez pas, je vous conseille fortement d’explorer par vous-mêmes les nombreuses nuances de cet outil.

–proxy=

Pour comprendre ce qu’envoie exactement sqlmap, il est possible de visualiser chaque requête envoyée et leur réponse en spécifiant un proxy. On peut par exemple faire passer tout le trafic généré par sqlmap à travers l’outil Burp Suite.

Cela peut notamment permettre de comprendre et de résoudre les cas où sqlmap ne fonctionne pas comme prévu en voyant directement comment le serveur répond par rapport aux requêtes envoyées par sqlmap.

–delay=

Il arrive parfois qu’un rate limiting soit implémenté sur certaines applications web.

Ainsi, le fort trafic généré par sqlmap peut activer des alertes sur le serveur qui bloquera toutes nouvelles requêtes de notre part. Pour remédier à ce problème, on peut utiliser l’option « –delay » qui précisera un délai en seconde entre chaque requête envoyée par sqlmap.

En jouant avec ce délai on peut potentiellement contourner le rate limiting, mais l’exfiltration des données sera donc plus longue.

–force-ssl

Lorsque le serveur n’accepte pas l’usage du protocole HTTP et que vous spécifiez un fichier de requête avec l’option « -r », l’option « –force-ssl » permet de forcer l’usage du HTTPS sur les requêtes envoyées.

C’est une option toute simple, mais importante à ajouter dans votre commande pour une exfiltration des données de manière sécurisée.

–threads=

Si vous savez que le serveur est capable d’assumer un fort trafic, il est intéressant d’augmenter le nombre de threads dans votre commande sqlmap pour récupérer les données plus rapidement. Attention donc à ne pas en abuser au risque de faire crasher le serveur SQL.

–dbms=

Pour des raisons d’optimisation, si vous connaissez le système de base de données utilisé par le serveur SQL, il est possible de le préciser à sqlmap. Cela diminue le temps de détection du type de SQLi, car sqlmap ne testera que les payloads en lien avec le DBMS précisé.

–level=

L’option « –level » permet de préciser à sqlmap l’exhaustivité des tests à effectuer. La valeur est de 1 à 5 et par défaut elle est à 1. Plus la valeur augmente, plus le nombre de payloads testés sera grand.

C’est une option pratique pour des cas complexes d’injection SQL. Si sqlmap ne trouve pas de payload d’injection la première fois, il est intéressant d’incrémenter la valeur de cette option.

–risk=

Un peu à la manière de l’option précédente, l’option « –risk » permet de préciser un niveau de risque accepté de 1 à 3. Plus la valeur de cette option est grande, plus les payloads injectées par sqlmap sont nombreux, mais également risqués.

En fonction de l’endroit de l’injection dans la requête SQL originale, augmenter le risque peut amener à des modifications de données non voulues. Par défaut, cette valeur est à 1.

–string=

Dans les cas des injections SQL boolean-based, il est possible de préciser à sqlmap une chaîne de caractère à chercher dans la réponse serveur si la condition injectée est vraie.

Comme nous l’avons pu voir précédemment, dans le cas d’une boolean-based blind, la réponse serveur est différente si la condition injectée est vraie ou fausse. Si elle est vraie, la différence peut se jouer à une simple chaine de caractère dans la réponse serveur, d’où l’intérêt de la préciser à sqlmap.

–technique=

Il est possible de préciser à sqlmap le type des payloads à tester et leur ordre. Une injection SQL peut être de plusieurs types différents et peut par exemple en même temps être une boolean-based blind et une time-based blind. Cependant, un payload boolean-based blind sera plus rapide à exploiter qu’un payload time-based blind. Avec l’option « –technique » on peut imposer à sqlmap le type de SQLi à chercher et trouver. Ci-dessous, la valeur que peut prendre l’option et sa signification :

  • B : Boolean-based blind
  • E : Error-based
  • U : Union query-based
  • S : Stacked queries
  • T : Time-based blind
  • Q : Inline queries

On peut choisir de ne mettre qu’une valeur ou de toute les mettre dans un ordre précis que suivra sqlmap. Par défaut, l’ordre est le suivant : BEUSTQ.

–file-read=

Sur certains systèmes de base de données, comme MySQL, PostgreSQL ou Microsoft SQL Server, il arrive parfois que l’utilisateur de la base de données ait les droits suffisant pour lire des fichiers sur le système. Ainsi, avec l’option « –file-read » on peut spécifier le chemin du fichier à récupérer.

–os-cmd=

Comme pour l’option précédente, l’option « –os-cmd » permet d’exécuter une commande sur le serveur SQL. Cette option n’est fonctionnelle que sur les DBMS MySQL, PostgreSQL ou Microsoft SQL Server et l’utilisateur de la base de données doit avoir les droits suffisants.

–flush-session

Par défaut, lorsque vous utilisez sqlmap sur une cible, celui-ci crée une session relative à la cible qu’il gardera en mémoire sur votre machine.

Ainsi, si vous fournissez à sqlmap la même requête, il pourra tout de suite continuer l’exploitation de la SQLi, car il aura toutes les informations nécessaires déjà en mémoire (payload, structure de la base de données déjà trouvées, etc.) et il n’aura pas besoin de recommencer depuis le début.

Si vous voulez au contraire que sqlmap recommence à zéro sur votre cible, utilisez une fois cette option pour supprimer la session sauvegardée.

–tamper=

Cette option permet d’étendre la flexibilité de sqlmap. Grâce à cette option, il est possible de préciser un ou plusieurs scripts, qui modifient le payload avant d’être envoyé à la cible.

Dans la partie suivante, nous développerons la notion des scripts tampers et verrons quelques cas concrets.

Pour plus d’informations sur toutes les options de sqlmap, je vous redirige vers le wiki.

Les scripts tampers sont des scripts permettant de modifier les payloads envoyés par sqlmap afin de contourner certains filtres applicatifs ou WAF mis en place par l’application testée. Par exemple, une application web pourrait très bien interdire l’utilisation d’espaces sur une entrée utilisateur.

L’auditeur ayant identifié une injection SQL dans cette même entrée, il lui faudra trouver un moyen de contourner ce filtre afin d’exploiter l’injection SQL. C’est pour ce genre de cas que les scripts tampers peuvent être utilisés. Il existe des scripts déjà implémentés par sqlmap que l’utilisateur peut utiliser, mais il est tout à fait possible d’en créer, soit même, pour des situations uniques.  

Pour lister les scripts tampers déjà implémentés par sqlmap, on peut utiliser la commande suivante :

Commande pour lister les scripts tampers

Parmi la soixantaine de tampers déjà disponible, on peut en citer quelques-uns :

space2comment.py

Ce script permet de remplacer tous les espaces dans un payload par des commentaires du type « /**/ ». Ainsi en ouvrant et en fermant tout de suite un commentaire, cela est interprété comme un espace par certains systèmes de base de données.

Ce script permet donc de contourner de simples filtres « anti-SQLi » implémentés par certaines applications web qui bloqueraient uniquement les entrées utilisateurs contenant des espaces.

randomcase.py

Le script « randomcase.py » permet de changer la casse de tous les mots clés SQL comme « SELECT », « WHERE », etc. Ainsi, « SELECT » peut devenir « SelEcT ». Certains filtres applicatifs ou WAF contrôlent qu’il n’y ait aucun mot clé SQL dans les entrées utilisateurs pour se protéger des injections SQL. Cependant, si ce filtre est mal implémenté, le fait de changer la casse peut potentiellement contourner la protection.

equaltolike.py

Ce script change tous les signes « = » d’un payload avec l’équivalent « LIKE » pour contourner les filtres applicatifs qui empêchent l’utilisation du signe égal dans les entrées utilisateur.

Il est important de noter que ces scripts peuvent être utilisés en même temps afin de contourner plusieurs filtres à la fois.

Conclusion

Sqlmap est donc un outil incontournable pour toute personne voulant détecter et exploiter une injection SQL. Il met à disposition un éventail de fonctionnalités pouvant répondre tant à des besoins basiques qu’à des besoins plus avancés. Toutes ces qualités en font un outil puissant, essentiel pour le métier de pentester.

Auteur : Lorenzo CARTE – Pentester @Vaadata