Custom rules are where we tweak and refine our security configuration as part of the overall system tuning which is an integral part of an administrator’s role in security. This third part of the OWASP CRS series explains how to edit, upload, enable and observe custom rules in action on the LoadMaster. We’ll follow a simple 4-step process on the LoadMaster. Along the way, the corresponding API commands will be provided.
This is typically done locally on your personal computer. A good choice is vim (though any text editor will do), as the syntax highlighting for the ModSecurity rule syntax aids understanding.
For this blog, we will provision a whitelist aimed at protecting a login form. These rules will immediately block requests to unknown endpoints, submitting unknown parameters or parameters with the wrong format (i.e., special characters). The fact that we do not run this imaginary login application on one of our servers does not matter for the sake of this exercise: All we want is to configure the LoadMaster and if the request passes the LoadMaster WAF and hits the backend, then we will simply get a 404 and that’s OK.
There is more detailed information available on this configuration available from netnea.com tutorials, under step 8 here.
So, we would look to create a file titled modsecurity_allowlist.conf with the following contents
SecMarker BEGIN_ALLOWLIST_login # START allowlisting block for URI /login SecRule REQUEST_URI "!@beginsWith /login" \ "id:11001,phase:1,pass,t:lowercase,nolog,skipAfter:END_ALLOWLIST_login" SecRule REQUEST_URI "!@beginsWith /login" \ "id:11002,phase:2,pass,t:lowercase,nolog,skipAfter:END_ALLOWLIST_login" # Validate HTTP method SecRule REQUEST_METHOD "!@pm GET HEAD POST OPTIONS" \ "id:11100,phase:1,deny,status:405,log,tag:'Login Allowlist',\ msg:'Method %{MATCHED_VAR} not allowed'" # Validate URIs SecRule REQUEST_FILENAME "@beginsWith /login/static/css" \ "id:11200,phase:1,pass,nolog,tag:'Login Allowlist',\ skipAfter:END_ALLOWLIST_URIBLOCK_login" SecRule REQUEST_FILENAME "@beginsWith /login/static/img" \ "id:11201,phase:1,pass,nolog,tag:'Login Allowlist',\ skipAfter:END_ALLOWLIST_URIBLOCK_login" SecRule REQUEST_FILENAME "@beginsWith /login/static/js" \ "id:11202,phase:1,pass,nolog,tag:'Login Allowlist',\ skipAfter:END_ALLOWLIST_URIBLOCK_login" SecRule REQUEST_FILENAME \ "@rx ^/login/(displayLogin|login|logout).do$" \ "id:11250,phase:1,pass,nolog,tag:'Login Allowlist',\ skipAfter:END_ALLOWLIST_URIBLOCK_login" # If we land here, we are facing an unknown URI, # which is why we will respond using the 404 status code SecAction "id:11299,phase:1,deny,status:404,log,tag:'Login Allowlist',\ msg:'Unknown URI %{REQUEST_URI}'" SecMarker END_ALLOWLIST_URIBLOCK_login # Validate parameter names SecRule ARGS_NAMES "!@rx ^(username|password|sectoken)$" \ "id:11300,phase:2,deny,log,tag:'Login Allowlist',\ msg:'Unknown parameter: %{MATCHED_VAR_NAME}'" # Validate each parameter's uniqueness SecRule &ARGS:username "@gt 1" \ "id:11400,phase:2,deny,log,tag:'Login Allowlist',\ msg:'%{MATCHED_VAR_NAME} occurring more than once'" SecRule &ARGS:password "@gt 1" \ "id:11401,phase:2,deny,log,tag:'Login Allowlist',\ msg:'%{MATCHED_VAR_NAME} occurring more than once'" SecRule &ARGS:sectoken "@gt 1" \ "id:11402,phase:2,deny,log,tag:'Login Allowlist',\ msg:'%{MATCHED_VAR_NAME} occurring more than once'" # Check individual parameters SecRule ARGS:username "!@rx ^[a-zA-Z0-9.@_-]{1,64}$" \ "id:11500,phase:2,deny,log,tag:'Login Allowlist',\ msg:'Invalid parameter format: %{MATCHED_VAR_NAME} (%{MATCHED_VAR})'" SecRule ARGS:sectoken "!@rx ^[a-zA-Z0-9]{32}$" \ "id:11501,phase:2,deny,log,tag:'Login Allowlist',\ msg:'Invalid parameter format: %{MATCHED_VAR_NAME} (%{MATCHED_VAR})'" SecRule ARGS:password "@gt 64" \ "id:11502,phase:2,deny,log,t:length,tag:'Login Allowlist',\ msg:'Invalid parameter format: %{MATCHED_VAR_NAME} too long (%{MATCHED_VAR} bytes)'" SecRule ARGS:password "@validateByteRange 33-244" \ "id:11503,phase:2,deny,log,tag:'Login Allowlist',\ msg:'Invalid parameter format: %{MATCHED_VAR_NAME} (%{MATCHED_VAR})'" SecMarker END_ALLOWLIST_login
Now that the custom rules file is ready, it is a simple step to upload and make it available to the Virtual Service(s) on the LoadMaster.
Import the modsecurity_allowlist.conf into the LoadMaster in the Web Application Firewall located in the left-hand navigation, click on Custom rules, and select ‘Choose File’ to select the custom rule file under the title WAF Custom Rules.
Once the custom rules file is selected, select ‘Add Ruleset’ to finalize the uploaded.
The Custom Rules section allows for both custom rules and associated data files to be uploaded. Individual custom rules files can be loaded, as shown above, or as a package of rules in a gzip-compressed tar ball (.tar.gz). Data files are referenced from custom rules and must be uploaded before the corresponding custom rule file.
Custom rule files can be downloaded directly or deleted (if the custom rules are not in use by any Virtual Service).
Uploading the custom rules file via RESTful API is easily achieved as follows:
curl -X POST --data-binary "@./modsec_allowlist.conf" -k https://bal:password@<<LoadMaster IP Address>>/access/addowaspcustomrule\?filename\="./modsec_allowlist.conf"
The LoadMaster Virtual Services (VS) provide the user the ability to configure specific settings (WAF, SSL, Content Rules, Authentication and SSO etc.) for an application.
With the modsecurity_allowlist.conf imported, navigate to Virtual Services, View/Modify Services and select the application Virtual Service that this custom rule should be used with. Once selected, navigate to WAF. The custom rule file will be there, select the checkbox so the tick is available and click the ‘Apply’ button.
To see the individual rules in the modsecurity_allowlist.conf file, click on the file name modsec_allowlist. Individual custom rules can be selected here and disabled (or enabled) as appropriate. For the purposes of this blog, all the custom rules should be enabled as shown below.
To facilitate this example, we are configuring the Audit Mode to Audit All and setting a low Anomaly Scoring Threshold of 5.
Under Advanced Settings, ensure that Inspect HTML POST Request Bodies is checked. This should result Enable JSON Parser, Enable XML Parser and Enable Other Content Types being checked. Ensure that Enable Other Content Types is checked, otherwise form parameters are ignored. This is shown in Figure 4 below.
Applying the custom rule file to the Virtual Service via RESTful API is easily achieved as follows:
curl -k https:// bal:password@<<LoadMaster IP Address>>/access/modvs\?vs\=<<Virtual Service IP>>\&port\=<<Virtual Service Port>>\&prot\=<<Virtual Service Protocol>>\&CustomRules\="modsec_allowlist"
The LoadMaster Virtual Service handling of custom rules includes the following features:
For the purposes of this test, I am running a standard Apache server (version: 2.4.46) as my back-end application. There is no special configuration of the application, it is a default installation.
Running the first test, this will pass through the WAF to the application and result in a 404 error as expected.
curl http://10.35.56.31/login/displayLogin.do
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>404 Not Found</title> </head><body> <h1>Not Found</h1> <p>The requested URL was not found on this server.</p> <hr> <address>Apache/2.4.29 (Ubuntu) Server at 10.35.56.31 Port 80</address> </body></html>
With the second test, there is an additional query string i.e., debug=1, that the WAF rule set does not know. In this, case the WAF will block the request with a 403 error.
curl http://10.35.56.31/login/displayLogin.do\?debug\=1
<html><head><title>403 Forbidden</title></head><body>Access denied</body>
There are several logs that the LoadMaster generates and the most interesting here is the WAF Event Log File under System configuration –Logging Options –System Log Files. The log generated from this access attempt is shown here:
2021-04-13T08:25:50+00:00 lb100 wafd: [client 10.0.31.231] ModSecurity: Access denied with code 403 (phase 2). Match of "rx ^(username|password|sectoken)$" against "ARGS_NAMES:debug" required. [file "/tmp/waf/2/modsec_allowlist.conf"] [line "39"] [id "11300"] [msg "Unknown parameter: ARGS_NAMES:debug"] [tag "Login Allowlist"] [hostname "10.35.56.31"] [uri "/login/displayLogin.do"] [unique_id "8aeea40b-c364-47c0-82db-3982b36e6bb7"]
We are looking at several entries in this log
The LoadMaster logs are mirrored to the ModSecurity logs and the mapping is:
Custom rules provide administrators a flexible way to create / customize their unique WAF configuration within LoadMaster for their specific application and allow the handling of false positives. Next week, we will demonstrate how False Positive analysis is performed on the LoadMaster and the enhanced visibility that this brings.
If you are currently looking at utilizing Web Application Firewall (WAF), make sure to check out the Kemp WAF here…
Part 1: Introduction to OWASP CRSPart 2: How do you run OWASP CRS on load masterPart 3: Deploying custom rulesPart 4: False Positive AnalysisPart 5: Reporting