Tue, 06 Mar 2007


mod_security is a web application firewall often used in combination with PHP. It is usually used to block attacks based on patterns defined by regular expressions. Therefore it tries to parse the incoming HTTP request into what it believes to be parameters for the web application and logs or disallows requests matching the defined patterns.

When it parses POST requests with the application/x-www-form-urlencoded content-type it mistakes a not encoded ASCIIZ byte as the end of the post data and does not scan anything behind that point. This allows bypassing the ruleset easily.

Affected versions

Affected is mod_security <= 2.1.0

Detailed information

When mod_security receives a request it parses it into web application parameters in a way it believes is correct. Because the way it parses the incoming data follows the rules defined in RFCs and not the reality of how the HTTP request parsers are implemented in Perl, Python, Java, PHP there are a number of bypass vulnerabilities when the RFC and reality mismatch.

One of the these differences is the way ASCIIZ bytes are handled when they occur in POST data of the application/x-www-form-urlencoded content-type. Because mod_security handles POST data of this kind as a C string it does not touch anything behind the first ASCIIZ byte because in the eyes of mod_security this is the end of the data.

Unfortunately for mod_security this is not how the HTTP parsers of the different script languages handle this situation. Most script languages (Perl, Python, ...) just ignore the ASCIIZ byte and parse the data as if it is legal. Since PHP 5.2.0 this also applies to PHP.

Proof of concept, exploit or instructions to reproduce

To reproduce it just take a standard install of mod_security 2.1.0 and PHP 5.2.0

Place the following little PHP script into your webroot that is vulnerable to XSS. (of course not on a server connected to the internet)

  if (isset($_POST['var'])

Now call it with a command like

$ echo -e "&var=<script>alert(/xss/);</script>" > postdata
$ curl http://localhost/test.php --data-binary @postdata -A HarmlessUserAgent

The example should not be blocked (because this is the default configuration) but in your error.log you will find a line saying that a possible XSS attack was detected.

Now try the same with a ASCIIZ byte embedded.

$ echo -e "\000&var=<script>alert(/xss/);</script>" > postdata
$ curl http://localhost/test.php --data-binary @postdata -A HarmlessUserAgent

This time there should be no log message in your error.log, because mod_security cannot see the var parameter behind the ASCIIZ byte.


Every script language (and sometimes the applications do it even themself) parses HTTP data in a different way. It is not feasible to write a single parser (mod_security) that understands all these dialects correctly. Therefore there are other ways to bypass mod_security depending on the languages used, this is just the simplest one.