WordPress hosting and the BitNinja WAF - How to do it right? (Part 3 - The BitNinja safe minimum ruleset)
Nikolett Hegedüs

WordPress hosting and the BitNinja WAF - How to do it right? (Part 3 - The BitNinja safe minimum ruleset)

In the preceding articles, I’ve talked a lot about the BitNinja safe minimum ruleset template and how you should enable it on your “/” location (or on “*/wp-admin/*” if needed) if you’re hosting mainly Wordpress websites. So I’d like to give you a little more explanation about the rules that are part of the safe minimum.

There are currently 15 rules from the OWASP Core Ruleset in the BitNinja safe minimum ruleset template, after thorough testing and evaluation. These are part of the following categories:

  1. Scanner Detection (1 / 5)
  2. Protocol Attack (4 / 10)
  3. Local File Inclusion (2 / 4)
  4. Remote File Inclusion (1 / 4)
  5. Remote Code Execution (2 / 11)
  6. PHP General Attacks (5 / 13)

The first number indicates the number of rules that are active in that category, and the second number shows how many rules are in that category.

(There are 5 more rules in the safe minimum in the BitNinja Ruleset category, divided into the following subcategories: Virtual Honeypot, Wordpress Backdoor Protection, and Drupal Remote Execution Protection - but we will cover these rules in a later article.)

To explain the rules a little bit more, I will show you the ModSecurity SecRule part of the code to the description of every rule. The SecRule is the main ModSecurity directive, and it is used to analyse data and perform actions based on the results.

In general, the format of SecRule is the following:

SecRule VARIABLES OPERATOR [ACTIONS]

The VARIABLES specify which places to check in an HTTP transaction. The most general variable is ARGS, which refers to all arguments of the transaction, including the POST payload.

The OPERATOR specifies a regular expression, pattern or keyword to be checked in the variables. Operators begin with a @ character.

The ACTIONS part specifies what to do whenever the operator performs a successful match against a variable.

REQUEST_HEADERS_NAMES|REQUEST_HEADERS → marked with yellow, these are the VARIABLES
"@pmf scanners-headers.data" → marked with green, this is the OPERATOR

1. Scanner Detection

In the Scanner Detection category, only one rule is enabled in the safe minimum ruleset template:

#913110: Found request header associated with security scanner

SecRule REQUEST_HEADERS_NAMES|REQUEST_HEADERS "@pmf scanners-headers.data"

This rule checks the header section of the HTTP requests. It searches for specific phrases in the headers that refer to certain security scanners, e.g. “acunetix-product”, “acunetix-scanning-agreement” or “x-scanner”. With this rule we block these scanners from scanning our servers without our consent.
You can find the full list of phrases at the OWASP ModSecurity CRS Github page.

2. Protocol Attack

In this category, 4 rules are enabled:

#921100, called HTTP Request Smuggling, which looks for a comma character in the Content-Length or Transfer Encoding request headers.

SecRule REQUEST_HEADERS:'/(Content-Length|Transfer-Encoding)/' ","

This rule also checks the HTTP request headers and searches for a comma character in the Content-Length or the Transfer-Encoding sections. It uses the following regex to check the headers:

'/(Content-Length|Transfer-Encoding)/' ","

 The comma character is considered malicious in these requests because it would indicate that there are more than one request headers with the same name. In these instances, Apache treats the data in a similar manner as multiple cookie values.

#921110, also called HTTP Request Smuggling, which looks for a CR (carriage return) / LF (linefeed) character in combination with a HTTP/WEBDAV method name.

This would point to an attack attempt to inject a second request into the first request, thus bypassing tests carried out on the primary request.

#921140: HTTP Header Injection Attack via headers
SecRule REQUEST_HEADERS_NAMES|REQUEST_HEADERS "(\n|\r)"

This rule looks for Carriage Return and Linefeed characters in the HTTP request headers, because these characters may cause problems if the data is returned in a response header and is interpreted by the client.

The Carriage Return (CR) character is %0d when it’s URLdecoded, and the Linefeed (LF) character is %0a.

#921150: HTTP Header Injection Attack via payload (CR/LF detected)

SecRule ARGS_NAMES "(\n|\r)"

This rule detects Carriage Return (CR) and Linefeed (LF) characters in all HTTP request argument names.

3. Local File Inclusion

In the Local File Inclusion category, there are two rules that are enabled:

#930120: OS File Access Attempt

SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@pmf lfi-os-files.data"

This rule looks for certain OS filenames in the HTTP request arguments. It also checks the cookies found in the HTTP request, but not in Google’s __utm cookies. It checks the arguments and the POST payload in the HTTP request and searches for the filenames, that can be found in the following file.
This rule helps blocking those requests thats’ aim is to access your wp-config.php file, for example.

#930130: Restricted File Access

SecRule REQUEST_FILENAME "@pmf restricted-files.data"

This rule checks the HTTP request filename. The request_filename variable holds the relative request URL without the query string part (e.g. /index.php). It detects attempts to retrieve application source code, metadata, credentials and version control history, that are possibly reachable in a web root.
It looks for specific phrases in the request filename, e.g. .htaccess, .htpasswd, /.git/, /.gitignore, wp-config.php, wp-config.old, /config/config.yml, etc. These filenames can be associated with Apache web servers, version control systems like Git or SVN, Wordpress, Symfony, Drupal, Magento, Sublime Text, ASP .NET, Node.js or Composer.
The phrases are listed in this file.

4. Remote File Inclusion

In this category, one rule is enabled:

#931110: Possible RFI Attack: Common RFI Vulnerable Parameter Name used with URL Payload

SecRule QUERY_STRING|REQUEST_BODY "(?i:(\binclude\s*\([^)]*|mosConfig_absolute_path|_CONF\[path\]|_SERVER\[DOCUMENT_ROOT\]|GALLERY_BASEDIR|path\[docroot\]|appserv_root|config\[root_dir\])=(file|ftps?|https?):\/\/)"

The query_string variable contains only the query string part of a request URI.

For example, from the following URL: the “source=hp&q=happy” is the query string part

 The value in query_string is always provided raw, without URL decoding taking place.

5. Remote Code Execution

In this category, 2 rules are enabled:

#932160: Remote Command Execution: Unix Shell Code Found

SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@pmf unix-shell.data"

This rule detects some common sequences found in shell commands and scripts. It checks all arguments in the HTTP request for specific phrases, e.g. “bin/bash”, “etc/shadow”, “usr/bin/uname”, etc.

 The full list of phrases this rule is looking for can be found on Github.

#932171: Remote Command Execution: Shellshock (CVE-2014-6271)

SecRule ARGS_NAMES|ARGS|FILES_NAMES "^\(\s*\)\s+{"
This rule detects exploitation of "Shellshock" GNU Bash RCE vulnerability.

 The Shellshock vulnerability allows the attacker to remotely issue commands on the server, exploiting a bug concerning environment variables in the GNU Bash.

6. PHP General Attacks

In this category, 5 rules are enabled:

#933130: PHP Injection Attack: Variables Found

SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@pmf php-variables.data"

This rule checks the HTTP request arguments and looks for certain PHP variable names, for example $HTTP_ENV_VARS, $GLOBALS, $_FILES, $_GET or $_SERVER, etc.

The comprehensive list can be found on Github.

#933140: PHP Injection Attack: I/O Stream Found → “php://” syntax

SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* \ "@rx (?i)php://(std(in|out|err)|(in|out)put|fd|memory|temp|filter)"

The “php://” syntax can be used to refer to various object, such as local files (for LFI), remote urls (for RFI), or standard input/request body. Its occurrence indicates a possible attempt to either inject PHP code or exploit a file inclusion vulnerability in a PHP web app.

This rule uses the following regular expression: (?i)php://(std(in|out|err)|(in|out)put|fd|memory|temp|filter)

And would catch a request that sends the following string in the GET parameter:

?-dallow_url_include%3don+-dauto_prepend_file%3dphp://input


#933150: PHP Functions: High-Risk PHP Function Names (base64_decode)

SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "@pmf php-function-names-933150.data"

Detecting PHP function names is useful to block PHP code injection attacks. There are many PHP functions. We have to strike a balance between robust detection of PHP code in content, and the risk of false positives.

The list of PHP functions is divided into four groups of varying attack/false positive risk. Four separate rules are used to detect these group of functions.

Rule 933150 contains ~ 40 words highly common to PHP injection payloads and extremely rare in natural language or other contexts. Examples are: ‘base64_decode’, ‘file_get_contents’, wp_remote_fopen, wp_remote_get, wp_remote_head, wp_remote_post, wp_remote_request, etc. These function names are defined on the following page.

These function names are highly indicative of a PHP injection attack.

#933170: PHP Injection Attack: Serialized Object Injection

SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_HEADERS|ARGS_NAMES|ARGS|XML:/* \

"@rx [oOcC]:\d+:\".+?\":\d+:{.*}"


PHP Object Injection is an application level vulnerability that could allow an attacker to perform different kinds of malicious attacks, such as code injection, SQL injection, path traversal and application denial of service, depending on the context.

The vulnerability occurs when user-supplied input is not properly sanitized before being passed to the unserialize() PHP function. Since PHP allows object serialization, attackers could pass ad-hoc serialized strings to a vulnerable unserialize() call, resulting in an arbitrary PHP object injection into the application scope.

#933180: PHP Injection Attack: Variable Function Call Found

SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "@rx \$+(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*|\s*{.+})(?:\s|\[.+\]|{.+}|/\*.*\*/|//.*|#.*)*\(.*\)" \

PHP ‘variable functions’ provide an alternate syntax for calling PHP functions. An attacker may use variable function syntax to evade detection of function names during exploitation of a remote code execution vulnerability. An example to use ‘file_get_contents’ function while evading rule 933150:

$fn = 'file_' . 'get_' . 'contents';

echo $fn('wp-co' . 'nfig.php');

Some examples of obfuscated malware:

$OOO0000O0(...)

@$b374k(...)

$_[@-_]($_[@!+_] )

A little statistics...

There were 1,865,179 WAF incidents from 11th June 2018 to 11th July 2018.

1,808,347 of these incidents (95,2%) was created in Log only mode, meaning that the attacker IP was not greylisted. The remaining 56,832 (4,8%) was created in Challenge and greylist mode, or so-called production mode, and have been put on the BitNinja greylist.

 The WAF rule that generated the most incidents was the Restricted File Access rule (930130) from the Local File Inclusion category. There are 19,388 incidents with this rule ID.

On second place is the High-risk PHP function names rule (933150) from the PHP General Attacks category with 8341 incidents in one month.

The third rule with the most incidents is the PHP injection attack: variables found (933130), also from the PHP general attacks category, which caught 4490 incidents.

On the 4th and 5th places are the HTTP request smuggling CR/LF in combination with HTTP/WEBDAV from Protocol Attack category (921110), with 3069 incidents; followed by the HTTP header injection attack via payload (CR/LF detected), rule id: 921150, also from Protocol Attack category, with 764 incidents.


What to do if you’d like to test more rules?

The BitNinja safe minimum ruleset template is really permissive. That’s why we advise that if you have the time, you should really look into the different WAF rules one-by-one, and see if it will be safe for your server to enable more rules.

But if you decide to enable a new rule, please, only enable it at first in Log only mode. This way it won’t greylist the IP addresses, but it will show you what kind of incidents it would generate. After a few days, you can decide if you can enable the rule in Challenge and greylist mode without any worries.

Some useful websites when trying to make sense of WAF incident data:

URL decoder (and encoder)

Online PHP decoder

Online PHP sandbox

I hope that this article series was useful to you. If you have anything to add or comment, don’t hesitate, to write to us!

Stay safe!


Also, check out the other parts:

-Part 1: for the basics

-Part 2: for the domain patterns

Share your ideas with us about this article

Previous posts

Critical zero-day vulnerability in MODX Revolution patched by BitNinja WAF
Content Management Systems (CMS) are highly vulnerable to zero-day attacks recently. Lately, the Drupal was picked on by the hackers. Now the ModX CMS is in the target. CVE-2018-1000207: The new MODX vulnerability Two critical vulnerabilities have been found in MODX Revolution <= 2.6.4 in the past few days. Exploiting it  , the hackers can remote code execution so they can compromise the website and modify (spoil/delete) the files and directories. This vulnerability has already got a CVE number: CVE-2018- 1000207. With a single web request, the attacker can create a custom file...
Journey through Europe with a Trabant-Ninjas are breaking the record
We are checking in with an unusual article. We would like to share an upcoming story about a great Journey, which will start on 28th of July. Why is it worth mentioning? Well, one member of this great Adventure is one of our Ninjas, and we’re really proud of him. They will travel around Northern Europe with a Trabant 601 car in 30 days. Breaking the record No one in this world has traveled this distance (7000KM = 4 349.59 miles ) with a Trabant (which only has a 2-stroke engine). This car sooner or later will be counted as a veteran. The adventure has a charity aspect as well, w...