Saturday, 25 October 2014

WAF beating.

Grab yourself a Stella it's time for some WAF beating.

This tutorial will cover WAF bypassing for SQLi, this is an in-depth tutorial designed to teach theory rather than giving you the smallest number of steps to bypass WAFs.


Web Application Firewalls are systems which proxy web traffic from users of web services to the web server and check for malicious attacks such as SQLi and XSS. Sometimes these are separate physical devices which sit between a web server and the gateway or they might be software based and run on the web server.

It's the job of the WAF to detect attacks and then take some action, it might be to alter web requests before they reach the web server to remove malicious code, to bounce or deny the request all together or possibly track malicious activity and alert staff.

Most WAFs aren't perfect, they operate primarily on blacklists and rules in order to spot malicious user input, however standards are constantly changing and WAFs have to be kept up to date with the latest rules and patterns to be effective.

Obfuscation of input is the standard method for beating WAFs, you take what looks like an SQLi or XSS attack and you modify the request so it no longer matches the patterns the WAF knows about, you can often do this through encoding parts of the input differently or using various tricks of the trade to fool the filters. It's game of cat and mouse between developers and hackers.

Spotting WAF implementation

Spotting WAFs is normally quite easy, their behaviour tends to be one of two things, either modify the query so what is sent to the server is missing malicious user input, the basic approach would be to remove certain keywords such as UNION, SELECT, CONCAT etc, if you find your requests being modified and returned without these words you've found a WAF which is removing them. The second method is to throw up either a 403 error page (Forbidden) or throw up some other custom error page with any number of custom error messages. You can tell if this is a WAF by re-sending the same query but removing keywords one at a time, if you've removed some keywords and the page renders correctly afterwards then you know you're dealing with some kind of WAF.

Enumerate illegal keywords

The first thing you should do is work out which keyword are tripping up the WAF, start with basic user input you know is allowed past the WAF and mark this as your baseline test. Now you need to add keywords into that baseline and test to see if the WAF reacts. Do this one keyword at a time and make a note of what is and isn't allowed, in the case of SQLi it's best to try all of the common SQL keywords, some common ones are listed below but this is not exhaustive:
  • SQL Commands
    • SELECT
    • UPDATE
    • DELETE
    • JOIN
    • UNION
    • WHERE
    • ORDER
  • SQL Functions
    • concat()
    • group_concat()
    • hex()
    • cast()
Apply some trial and error and get a feel for what is allowed and what is not.


Obfuscation is the last step, modify problematic keywords bypass the filters, let's start with a fictional example of a basic MySQL injection attack taken from a prior post called State of the Union, this attack is simply enumerating all of the schemas that exist in the database by reading the information_schema.schemata table:

URL UNION SELECT 1,group_concat(schema_name),3,4 from information_schema.schemata --
SELECT id, headline, news, author FROM news WHERE id = -51 UNION SELECT 1,group_concat(schema_name),3,4 from information_schema.schemata --

Let's assume that our fictional WAF doesn't allow the key words "union", "select" or "schema_name".

Let's start with the basic attacks and increase in complexity, it's unlikely the simple obfuscations will work on their own but they may be helpful to merge with other types of obfuscations.

CaSe seNsitIvity

Probably the most basic attack, simply alter the case of some of the characters for example

URL UniOn SeLEcT 1,group_concat(schema_name),3,4 from information_schema.schemata --

Be careful that you're not modifying the case of something that is case sensitive in the context of SQL, certain versions of SQL on certain systems might have case sensitive table names for example.


Comments are ignored by the SQL server and are a good source of obfuscation as they can be tricky to resolve correctly. The basic syntax of a comment is /* to start and comment and */ to end it, everything in between is ignored.

You can use this to break up words so that basic string comparisons fail For example:

There are lots of variants on this attack, you can put dummy letters inside the comments for example:

You can also nest comments, for example:

As well as throwing in broken comment tags, for example:

/*!Advanced Comments*/

There's more advanced tricks you can play with comments specifically on MySQL servers, they support conditional comments where adding an exclamation mark before your text inside of a comment you tell MySQL to actually use the text inside the comment as it would normally, the following example would execute and return you the schemas as normal:

URL /*!UNION*/ /*!SELECT*/ 1,group_concat(schema_name),3,4 from information_schema.schemata --

You can obfuscate this further by adding in the version number of MySQL you wish to conditionally check for, the server will execute the comment as normal syntax if its version is equal or greater than the value you supply, the following example will execute only if the server is equal or greater than version 5.00.00

URL /*!50000 UNION*/ /*!50000 SELECT*/ 1,group_concat(schema_name),3,4 from information_schema.schemata --

The full documentation on comments can be found here:


MySQL supports many different types of encoding such as hex, binary, base64, as well as encryption types such as Md5 and SHA1, while keywords cannot be altered with this method a lot of other values can be, such as schema names, table names and values for comparison.

For example if schema_name is blacklisted you could hex encode using a free tool making sure to add 0x to the start of the value, this means schema_name becomes 0x736368656d615f6e616d65, or you could use the base64 equivalent and then base64 decode it inside the query, for example FROM_BASE64(c2NoZW1hX25hbWU=)

MySQL now also supports XML functions so you can use functions like extractvalue() in similar ways.

White list evasion

When white listing is enabled WAFs can be very hard to bypass, if an admin configures bespoke white list rules and applies them to specific parameters it could be impossible to obfuscate the input. In our example we're passing a news_id parameter which is expected to only contain numeric data, if a custom a rule is created to white list numeric data only then injection is impossible.

However custom rules are usually applied to specific parameters, they're stored by the WAF with several properties, a reference to specific paths, the parameter name and a regex rule for what the parameter is allowed to contain. This gives us 2 new attack vectors by obfuscating both the path and the parameter name, if you malform the request in such a way the web server can understand or correct, but the WAF cannot, then you can evade triggering the white list rule to begin with.

Path evasion

In this case the added /notvalid doesn't exist on the server and the web server may process /news/index.php as normal. This depends on your web server and it's version.

URL /*!UNION*/ /*!SELECT*/ 1,group_concat(schema_name),3,4 from information_schema.schemata -- 

You can manipulate paths to break basic string compares by hopping into directories and then back again for example:

URL /*!UNION*/ /*!SELECT*/ 1,group_concat(schema_name),3,4 from information_schema.schemata --

This trick references the news folder, then jumps back a directory and then references the news folder again a second time.

Parameter evasion

Obfuscation of the parameter name may also work so instead of news_id we might use this:

URL /*!UNION*/ /*!SELECT*/ 1,group_concat(schema_name),3,4 from information_schema.schemata --

Or you could supply the same parameters multiple times, in PHP only the last occurrence is used.

URL /*!UNION*/ /*!SELECT*/ 1,group_concat(schema_name),3,4 from information_schema.schemata --

PHP also does a certain amount of cleaning of parameter names, it remove preceding spaces, and converts spaces and periods in the parameter name to underscores, if the parameter name genuinely does contain an underscore such as news_id then this is another good parameter obfuscation, for example:

URL /*!UNION*/ /*!SELECT*/ 1,group_concat(schema_name),3,4 from information_schema.schemata --

If you're dealing with an ASP based server it will remove any URL encoding which is invalid, normally a URL encoded character would be a % followed by 2 hex digits, another method for obfuscating the parameter itself, for example:

URL /*!UNION*/ /*!SELECT*/ 1,group_concat(schema_name),3,4 from information_schema.schemata --

Advanced attacks

Many of the attacks above wont work simply on their own, however combining them together in new an innovative ways can help beat a WAF that can otherwise detect the individual obfuscations, part of being a good hacker is experimenting and persisting.

If you're having trouble bypassing a WAF you could fingerprint the specific type of WAF by looking at behaviour and analyzing error responses, sometimes custom errors will contain the WAF name or hint at version number.

Once you know what you're dealing with check to see if the blacklist rules are publicly available, many of the free WAFs that are commonly in use such as Mod_Security are open source so you can study the blacklist rules directly, look for exceptions and edge cases which you might not otherwise try and be more methodical in your testing.

Lastly you can use applications to help you with attacks such as SQLmap and obfuscaters such as

1 comment: