4.8.5.5 Tester MS Access

Sommaire
Comme expliqué dans la section générique sur les SQL injection, les vulnérabilités d'injeection SQL surviennet chaque fois qu'une entrée utilisateur est utilisée dans la construction d'une requête SQL sans avoir été correctement vérifiée et nettoyée. Ce type de vulnérabilités permet à un attaquant d'exécuter du code SQL avec les privilèges de l'utilisateur connecté à la base de données. Dan scette section nous allons décrire les techniques d'injection SQL spécifiques à Microsoft Access

Détection
Lorsque l'on test une application basée sur SQL, détecter la technologie spécifique de base de données utilisée est la première étape pour évaluer correctement les vulnérabilités potentielles. Une approche commune consiste à injecter des attaques courantes (par exemple des simples quotes, guillemets, etc.) afin de déclencher des exception en base de données. A condition que l'application ne traite pas les exceptions avec des pages personnalisées, il est possible de détecter le SGBD sous-jacent en observant les messages d'erreur.

Selon la technologie web utilisée, les applications basées sur MS Access vont répondre avec l'un des messages d'erreur suivants :

Fatal error: Uncaught exception 'com_exception' with message Source: Microsoft JET Database Engine

ou

Microsoft JET Database Engine error '80040e14'

ou

Microsoft Office Access Database Engine

Dans tous les cas, on a une confirmation que l'on test une application utilisant une base de données MS Access.

Test basique
Malheureusement, MS Access ne supporte pas les opérateurs typiques traditionnellement utilisés dans les tests d'injection SQL :


 * Pas de caractères de commentaires
 * Pas de requêtes multiples
 * Pas d'opérateur LIMIT
 * Pas d'opérateurs SLEEP, BENCHMARK ou similaires
 * Et bien d'autres

Cependant, il est possible d'émuler ces fonctions en combinant plusieurs opérateurs ou en utilisant des techniques alternatives. Comme indiqué, il n'est pas possible d'utiliser l'insertion de commentaires,   ou   afin de tronquer la requête. Mais on peut contourner cette limitation en injectant un caractère 'null'. Utiliser un octet nul  dans une requête SQL oblige MS Access à ignorer tous les caractères suivants. Cela s'explique par le fait que les chaînes sont terminées par NULL dans la représentation interne utilisée par la base de données. Il est important de mentionner que le caractère 'null' peut aussi poser des problèmes, puisqu'il peut aussi tronquer des chaînes au niveau du serveur web. Dans ces situations, on peut cependant utiliser une autre caractère : 0x16 (%16 en format encodé URL).

considérons la requête suivante :

SELECT [username],[password] FROM users WHERE [username]='$myUsername' AND [password]='$myPassword'

On peut tronquer la requête avec les deux URLs suivantes :

http://www.example.com/page.asp?user=admin'%00&pass=foo http://www.example.com/page.app?user=admin'%16&pass=foo

L'opérateur  n'est pas implémenté en MS Access, il est cepdendant possible de limiter le nombre de résultats en utilisant les opérateurs   ou.

http://www.example.com/page.app?id=2'+UNION+SELECT+TOP+3+name+FROM+appsTable%00

En combinant ces opérateurs, il est possible de sélectionner des résultats spécifiques. La concaténation de chaîne est possible en utilisant les caractères  et

Il y a aussi beaucoup d'autres fonctions qui peuvent être utilisées pour les tests d'injection SQL, notamment :
 * ASC: Retourne la valeuer ASCII du caractère passé en entrée
 * CHR: Retourne le caractère de la valeur ASCII passée en entrée
 * LEN: Retourne la longueur de la chaîne passée en paramètre
 * IIF: Instruction IF, par exemple : IIF(1=1, 'a', 'b') retourne 'a'
 * MID: Extraction d'une sous-chaîne. Par exemple : mid('abc',1,1) retourne 'a'
 * TOP: Spécifie le nombre maximum de résultats que la requête doit retourner depuis le début. Par exemple : TOP 1 retournera seulement un enregistrement.
 * LAST: Renvoie seulement le dernier enregistrement d'un ensemble. Par exemple : SELECT last(*) FROM users renverra le dernier enregistrement du résultat.

Certain de ces opérateur sont essentiels pour exploiter des injection SQL en aveugle. Pour les autres opérateurs avancés, voir les documents en référence.

Enumération d'attributs
Pour énumérer les colonnes d'une table dans une base de données, il est possible d'utiliser une technique commune basée sur les erreurs. En bref, on peut obtenir les noms des attributs en analysant les messages d'erreur et en répétant la requête avec différents sélecteurs. Par exemple, admettant que nous connaissons l'existence d'une colonne, nous pouvons aussi obtenir le nom des autres attributs avec la requête suivante :

' GROUP BY Id%00

Dans le message d'erreur renvoyé, il est possible d'observer le nom de la colonne suivante. A partir de ce moment, nous pouvons répéter la méthode jusqu'à ce nous ayons les noms de tous les attributs. Si l'on ne connait pas le nom du premier attribut, on peut tout de même insérer un nom fictif et obtenir le nom de ce premier attribut dans le message d'erreur.

Obtenir le schéma de la base
Par défaut, il existe diverses tables système dans MS Access qui peuvent être utilisées pour obtenir les noms des tables et des colonnes. Malheureusement, dans la configuration par défaut des versions récentes de MS Access, ces tables ne sont pas accessibles. Cependant cela vaut toujours le coup d'essayer :


 * MSysObjects
 * MSysACEs
 * MSysAccessXML

Par exemple, s'il y a une vulnérabilité d'injection SQL par UNION, on peut essayer la requête suivante :

' UNION SELECT Name FROM MSysObjects WHERE Type = 1%00

D'autre part, il est toujours possible de brute-forcer le schéma de la base en utilisant une liste de mots standard (Ex: FuzzDb).

Dans certains cas, les développeurs ou les administrateurs système ne réalisent pas qu'inclure le véritable fichier .mdb dans le webroot de l'application permet de télécharger la base en entier. Les fichiers de bases de données peuvent être trouvés avec la requête suivante :

http://www.example.com/page.app?id=1'+UNION+SELECT+1+FROM+name.table%00

où  est le nom du fichier .mdb et   est une table valide. Dans la cas où la base est protégée par mot de passe, de multiples logiciels peuvent être utilisés pour casser le mot de passe. Voir les références.

Test d'injection SQL en aveugle
Les vulnérabilités Blind SQL Injection ne sont en aucun cas les plus faciles à exploiter quand on test des cas réels. Dans le cas de versions récentes de MS Access, il n'est de plus pas possible d'exécuter des commandes shell ou de lire/écrire des fichiers arbitrairement.

Pour ce qui est des injection SQL en aveugle, l'attaquant ne peut qu'inférer le résultat de la requête en mesurant des différences de temps ou dans les réponses de l'application. Nous supposons ici que le lecteur connait déjà les principes des attaques par injection SQL en aveugle, puisque la suite va se concentrer sur les détails spécifique à MS Access.

L'exemple suivant sera utilisé :

http://www.example.com/index.php?myId=[sql]

Où le paramètre id est utilisé dans la requête suivante :

SELECT * FROM orders WHERE [id]=$myId

Considérons le paramètre  comme vulnérable à une injection SQL en aveugle. En tant qu'attaquant, nous voulons extraire le contenu de la colonne 'username' de la table 'users', sachant que nous connaissons déjà le schéma de la base.

Une requête typique peut être utilisée pour deviner le premier caractère du nom d'utilisateur de la 10e ligne :

http://www.example.com/index.php?id=IIF((select%20MID(LAST(username),1,1)%20from%20(select%20TOP%2010%20username%20from%20users))='a',0,'no')

Si le premier caractère est un 'a', la requête renverra 0, sinon elle renverra 'no'.

En utilisant une combinaison des fonctions IFF, MID, LAST et TOP, il est possible d'extraire le premier caractère du nom d'utilisateur d'une ligne donnée. Comme la requête retourne un ensemble d'enregistrements, et non un seul, il n'est pas possible de l'utiliser directement. Heureusement on peut combiner de multiples fonctions pour extraire uen chaîne donnée.

Considérons que l'on veut récupérer le username du la 10e ligne. D'abord, on peut utiliser la fonction TOP pour sélectionner les dix premières lignes :

SELECT TOP 10 username FROM users

Ensuite, en utilisant ce sous-ensemble, on peut extraire la dernière ligne avec la fonction LAST. Une fois qu'on a une et exactement une seule ligne contenant notre chaîne, on peut utiliser les fonctions IFF, MID et LAST pour découvrir la valeur réelle de username. Dans notre exemple, nous employons IFF pour renvoyer un nombre ou une chaîne. En utilisant cette astuce, nous ponvons savoir si la réponse est vraie ou non, en observant les messages d'erreur de l'application. Comme  est numérique, la comparaison avec une chaîne résulte en une erreur SQL qui peut potentiellement fuiter dans les pages. Dans le cas contraire, une page standard  sera surement retournée.

Par exemple, nous pouvons avoir la requête suivante :

http://www.example.com/index.php?id='%20AND%201=0%20OR%20'a'=IIF((select%20MID(LAST(username),1,1)%20from%20(select%20TOP%2010%20username%20from%20users))='a','a','b')%00

qui renvoie TRUE si le premier caractère est 'a' et FALSE sinon.

Comme mentionné, cette méthode permet de deviner les valeurs de chaînes choisie dans la base de données.


 * 1) En essayant toutes les valeur affichables, jusqu'à ce que nous trouvions une correspondance.
 * 2) En devinant la longueur des chaînes avec ka fonction LEN, ou simplement en nous arrêtant après avoir trouvé tous les caractères.

Les injection SQL en aveugle basées sur le temps sont aussi possibles : [http://technet.microsoft.com/it-it/library/cc512676%28en-us%29.aspx.

Références

 * http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html
 * http://packetstormsecurity.com/files/65967/Access-Through-Access.pdf.html
 * http://seclists.org/pen-test/2003/May/74
 * http://www.techonthenet.com/access/functions/index_alpha.php
 * http://en.wikipedia.org/wiki/Microsoft_Access