Some time ago I found a blind SQL injection on a bug bounty target. The web page was built with ASP.NET and had a big attack surface. After crawling through hundreds of pages and looking at endpoints, I found one accepting a parameter called something like table
(next to a dozen other parameters). A simple manual test by inserting a few special characters showed up to be promising, as it resulted in a SQL error. I first had problems reproducing this vulnerability because the session had to be in a specific state. You had to request specific pages after logging in to reach this session state and get the vulnerability function in the background called.
By messing with the parameter, I was able to provoke an error like this one:
Incorrect syntax near ' or SQLi WHERE 1=1 AND (CONVERT(char(10),Prc,121) = '.
Unclosed quotation mark after the character string ') ) AS Result
WHERE Row BETWEEN (1 - 1) * 10 + 1 AND 1 * 10'.
As can be seen, some part of the query was printed out and the injected string looks to be in the conditional/where part of the query, something like
SELECT x FROM y WHERE
(SELECT a FROM b WHERE 1=1 AND (CONVERT(char(10),Prc,121) = $INJECTION.X ) )
AS Result
WHERE RowNum BETWEEN (1 - 1) * 10 + 1 AND 1 * 10
Provoking an error for this vulnerability was one thing, exploiting it was different. Because in bounty hunting the impact is important, I was in the need to extract some data. I tried using sqlmap with several tamper scripts, but always failed to extract something. Maybe this also had to do with my limited ability to use sqlmap at this time. The target often timed out when experimenting with it, so this was a long trial and error process. Because I knew the timeout was not provoked by an invalid SQL query, this seemed to be caused by a web application firewall (WAF), dropping the answer when receiving potential malicious input (or too many requests in a short timeframe).
Char by char and keyword by keyword I tried to figure out what WAF was beeing used and what rules where present. It looked like a few keywords were blacklisted and a few were stripped. When approaching a WAF, the first thing I do is always trying to figure out how the filters/rules are implemented: Is there a blacklist blocking everything when detecing something fishy? Which values are encoded? Do specific values get stripped or replaced? Which values get escaped? In this case, it seemed like a combination of blacklisting and stripping. When attacking a ruleset which strips values, I always get a bit happier because most of the time its trivial to bypass it. The printed error also helped a lot figuring out how to bypass it. So I began trying different variations:
I noticed very fast that using an SELECT
keyword was not possible:
Input | Output |
---|---|
' FOO SELECT BAR |
' FOO BAR |
' FOO SELEC BAR |
' FOO SELEC BAR |
' FOO SELECTBAR |
' FOO BAR |
So they strip the keyword SELECT
. Do they do this recursive?
Input | Output |
---|---|
' FOO SELSELECTECT BAR |
' FOO SELECT BAR |
Success! Lets go on. We still need to comment out the end of the query because we don’t know how the structure is. So how can we make a comment?
Input | Output |
---|---|
' FOO -- BAR |
' FOO BAR |
' FOO /* BAR |
' FOO BAR |
' FOO --/*-- BAR |
' FOO BAR |
' FOO --SELECT-- BAR |
' FOO BAR |
Wait, why is it recursive here? I dont know, but lets go on..
Input | Output |
---|---|
' FOO --- BAR |
' FOO - BAR |
' FOO ------ BAR |
' FOO BAR |
' FOO ---/*--- BAR |
' FOO -- BAR |
Wonderful.
Before beeing able to extract, I still had to fight a bit with the syntax and found a working query with a syntax similar like this one:
column_xyz where $SQL) AS Result---/*---
I had two options now: (a) Learn how to use sqlmap with its tamper scripts and maybe write a custom one or (b) write a simple exploit by myself.
I went with (b) here (and later on coded a similar app for myself to experiment with sqlmap more). The python script I wrote simply iterated over the value to extract with SUBSTRING
and tried all possible chars. So the usual blind SQL injection exploitation mechanism. Mine was not optimized, but it was enough for a proof of concept and to extract some sensitive data. As you can see in the screenshot I also added some validation steps before.
This vulnerability was awarded with a high bounty. Because I found some similar vulnerabilities on the target, I was able to use this bypass for the other ones as well.