In this tutorial, we examine Database Links, a feature found in Microsoft SQL server that allow user’s to setup trusted links to other servers. Database links can be abused to gain access to sensitive data and escalate privileges/further access on the network.
What is a Database Link?
Lets go back to the beginning and look at what Microsoft define as a Linked Server.
Linked servers enable the SQL Server Database Engine and Azure SQL Managed Instance to read data from the remote data sources and execute commands against the remote database servers (for example, OLE DB data sources) outside of the instance of SQL Server.
Microsoft also provide us with some advantages of using Linked Database Servers.
Linked servers enable you to implement distributed databases that can fetch and update data in other databases. They are a good solution in the scenarios where you need to implement database sharding without need to create a custom application code or directly load from remote data sources. Linked servers offer the following advantages:
- The ability to access data from outside of SQL Server.
- The ability to issue distributed queries, updates, commands, and transactions on heterogeneous data sources across the enterprise.
- The ability to address diverse data sources similarly.
So when database links are configured correctly, they can provide organisations, particularly those that manage lots of different data sources, some powerful features.
Why should I care?
So as we’ve seen above, linked servers can offer some great data management features. However, it is also commonly misconfigured. For example, what if we told you, you could login to a misconfigured SQL server as a standard user account and (configuration dependant) could come out as a Systems Administrator account on the other side?
As this post is a tutorial, we will be re-creating this attack chain in a lab environment. For the reader following along, we have the three following hosts.
- Thanos.MCU.lab (Windows Server 2016, Domain Controller)
- VisionSQL.MCU.Lab (Windows Server 2016, Running MS SQL Server 2016)
- WandaSQL.MCU.Lab (Windows Server 2016, Running MS SQL Server 2016)
When you create a linked server, you must assign a security role to that link. ie. a user account. A common misconfiguration that seen when conducting internal infrastructure assessments are that linked servers are setup using the SA account. The SA account would have access to all databases on the target server, so therefore is most likely the easiest account to use for the connection.
For the lab, the following linked servers have been configured.
- A link between VisionSQL and WandaSQL - this allows VisionSQL to access WandaSQL using the security role links-user. This user has basic privileges and only has access to objects that are available to public users.
- A link from WandaSQL, back to VisionSQL - this allows WandaSQL to access VisionSQL using the security role Sysadmin. This is the administrative user, which we will use to execute commands and compromise the server.
Eventually we end up with the following chain:
The graphic above shows that we have created a chain of linked servers, that starts with low privileged access and finishes with sysadmin access on the server that we started from.
Discovering Linked Database Servers
Now that the lab has been setup, the scenario can begin. An Active Directory domain user account under the name of
MCU\PPots has been compromised during a penetration test and we have discovered this account is able to logon to the database server: VisionSQL.
The next step involves using the
MSSQLClient.py Impacket tool in order to communicate with the Microsoft SQL Database Service.
We can authenticate to the database with the following command:
mssqlclient.py MCU.lab/Ppots:Tonystark1@visionSQL -windows-auth. Now that we are logged into the database, we can enumerate any linked servers with the following syntax:
Looking at the output above, we can see that the server has links to two other servers, FalconSQL and WandaSQL.
Lets pick on WandaSQL and see what permissions the database link has been set up with. To do this, we are going to use
openquery() to run a command on the remote server and see what permissions we have.
The following SQL command can be used to check the permissions of the link, it will return either a 1 or a 0 to indicate true or false.
The output above shows that the link from VisionSQL to WandaSQL is not configured as a System Administrator.
For the final step in our enumeration, we are going to check whether it is possible to run stored procedures over the enumerated database links. In order to run a stored procedure over a database link, the link must be configured with the option
We can check this with the following command:
The command above returned a 1, indicating that
RPC Out is enabled for the database link setup between VisionSQL to WandaSQL.
- We can authenticate to one of the MS-SQL servers (VisionSQL) with our domain user account.
- We identified two database links present on VisionSQL.
- We found out that the link between VisionSQL and WandaSQL was not configured as a System Administrator.
- We learned that
RPC Outwas enabled for the database link between VisionSQL and WandaSQL.
Abusing MS-SQL Database Links to Steal Hashes
You might remember from the previous section, when doing our discovery, we noted that the first link had
RPC Out enabled. This allows us to run stored procedures on the database server WandaSQL whilst only needing to authenticate to VisionSQL.
It will not be possible to run the
xp_cmdshell stored procedure over the first database link, as we discovered that we did not have System Administrator privileges. However, what if we use a stored procedure with Public permissions?
Talking about the ins and outs of xp_dirtree is out of scope for this post, however a high-level overview is that it instructs the server to connect to a folder and list the contents. This functionality can be abused to connect to a UNC path and it will automatically send its Windows credentials when doing so, allowing us to capture them or relay them.
The important part, is that this stored procedure by default, is configured with Public permissions.
As we have Public permissions across the first database link we discovered and
RPC Out is enabled, we will be able to execute this attack. Ensure that you have a listener or relay tool listening, to receive the connection from the database. In the example below, we are using the tool
Responder.py listening in Analyse mode.
The following command can be used to run a stored procedure over a database link. Note, that in order to run a stored procedure over a database link, a value must be returned. Therefore, you must include the
SELECT 1; at the start of the query each time to satisfy this requirement.
The command returns the value 1 due to our
SELECT 1; that we mentioned previously. However, when we flick to our terminal with Responder running, we can see that the server has attempted to connect to us and has sent us the NetNTLMv2 hash for the Domain User
Now that we have a hash, we can subject it to offline password cracking or even combine this with an SMB Relaying attack to take advantage of a lack of SMB Message Signing on an internal network.
Compromising the Domain Through Privileged Database Links
So far we haven’t spoke about the fact that database links can be nested, when I say nested, consider the following example code.
First we open a connection to SQL1, then from SQL1 we are connecting to SQL2. Then from SQL2, we are connecting to SQL3, then finally we are connecting to SQL4 and seeing if the link between SQL3 and SQL4 is configured with a System Administrator.
In the last example, we are going to connect to WandaSQL via the database link from VisionSQL, check to see if there are any database links on WandaSQL and whether or not they are privileged. If they are, we will abuse them to execute
xp_cmdshell and compromise the database server.
We can enumerate database links on WandaSQL like so.
Then we check if the second database link between WandaSQL and VisionSQL is configured with a privileged user account.
Like before, we’ll also check if
RPC Out is enabled, allowing us to run stored procedures:
- We have a database link between VisionSQL and WandaSQL.
- We’ve then identified a second database link from WandaSQL back to VisionSQL.
- We’ve identified that the second database link is configured as a Systems Administrator.
- We’ve also identified that
RPC Outis enabled, allowing us to run stored procedures over the database links.
As we have systems administrator privileges and
RPC Out is enabled, this means that we will be able to run the stored procedure
xp_cmdshell and execute operating system commands on the server itself.
The stored procedure
xp_cmdshell is disabled by default and is rarely found enabled and only a systems administrator account is able to enable it. Fortunately, there is way to execute it through our database links.
The following commands will re enable
xp_cmdshell through the two database links we have identified:
xp_cmdshell now re enabled, we can now execute commands against the server through the database links.
The following commands will execute
xp_cmdshell and leverage the elevated privileges of the SQL Server in order to add a new domain admin account (Note, the
SELECT 1; from the previous section).
After running the commands, if we open a PowerShell prompt on the box as our lower-privileged user account
MCU\PPots, we can see that a new Domain Administrator account has been created called
MCU.lab\Dannrocks resulting in a domain-wide compromise.
In order to minimise the abuse of configured database links, the following best practice advice can be followed.
- Ensure that database links are configured with the principle of least privilege. Ideally, create a separate account that is only used for that link, with minimal privileges (enough to access the databases/tables for the purposes of the link).
- Ensure that
RPC Outis disabled, to prevent attackers from running stored procedures over database links.
- Restrict access to the database server and ensure that only accounts that have a genuine business need are the ones that are granted access. Avoid granting access to large groups in Active Directory, such as
- Finally, if you don’t use the database links, make sure they are removed. This can prevent them from being abused if a user ever gains unauthorised access.
- Laurent Gaffie (@PythonResponder) for the Responder.py tool.
- Secure Auth (@SecureAuth) for the Impacket Collection.
- Scott Sutherland (@_nullbind) and the rest of the NetSPI Team for all their research on Securing Microsoft SQL Server.
Disclaimer: All information provided within this post is for educational purposes only.