Using ModSecurity inspectFile in OpenLiteSpeed

How Can We Help?
< Back
You are here:
Print

The @inspectFile operator runs a Lua program or external program (which can be a script) of your own to examine variables in the target list. Most commonly it is used to examine uploaded files and that is the only way it has been tested for this article. It’s not a feature used that commonly as it is not provided as part of the functionality of the RuleSet providers (Comodo or OWASP), but if you wish to examine data transferred before it actually is used by the web server it is the most powerful way to do it.

Using @inspectFile can be somewhat problematic as your script might not be executed, or if it is, it may not do what you expect. The purpose of this knowledgebase article is to give you some lessons, hard won, in understanding the use of @inspectFile.

Many of the details of this article are specific to OpenLiteSpeed using the SpiderLabs ModSecurity library (its included library). If you are using LiteSpeed Enterprise the issues are fewer and they are denoted below.

Basic Setup

The steps for using @inspectFile

  • Write a rule using inspectFile
  • Write a script which does the inspectFile and outputs the values expected
  • Set up a test for your rule/script

Write a Rule Using inspectFile

Most of the time, when writing an inspectFile rule, you will need to make it chained. All rules require IDs and it’s easiest to add the ID as part of the preceding rule in the chain. Also running a script can add overhead and you can use the preceding rule to limit the frequency that the script is run.

An example rule might be:

SecRule ARGS:b "@streq inspectfail" "chain,phase:request,log,deny,id:159" 
SecRule FILES_TMPNAMES "@inspectFile /usr/local/lsws/cxscgi.sh"

This gives us a two step rule:

  • Examine the arguments of the request. The rule will only run the script if there is an argument b with the exact value inspectfail
  • Run the script /usr/local/lsws/cxscgi.sh to examine the data passed up. Our test below will assume it’s a file upload. FILES_TMPNAMES is the file name uploaded and is the recommended value.

This rule can be inserted into a rules include file, or as module parameters when the mod_security module is loaded (as in OpenLiteSpeed), or through Rules Definition in the WAF (as in Enterprise).

Additional SpiderLabs (OpenLiteSpeed) Requirements

Not relevant for LiteSpeed Enterprise.

According to the reference documentation , using FILES_TMPNAMES requires the SecTmpSaveUploadedFiles directive to be set to On, or the SecUploadKeepFiles directive to be set to RelevantOnly. In our testing we strongly recommend the following be added to your rules to make writing and debugging rules easier:

SecRequestBodyAccess On
SecUploadKeepFiles On
SecUploadDir /tmp/modsec
SecDebugLog /tmp/auditlog-debug.txt
SecDebugLogLevel 9

You must precreate the /tmp/modsec directory and the LiteSpeed user must have read/write access to this directory.

SecDebugLog and SecDebugLogLevel are debugging tools that will be discussed below.

Write a Script to do the inspectFile

The really important thing to remember about the user script is that the first character written to standard output is used as the return code for the operation. 1 is failure and 0 is success. The following is a sample script which may help you to debug the facility.

#!/bin/bash
echo "1 FAIL!";
echo $(date) >> /usr/local/lsws/cxscgi.log
args=("$@")
ELEMENTS=${#args[@]}
echo "  There are ${ELEMENTS} command line parameters" >> /usr/local/lsws/cxscgi.log
for (( i=0;i<$ELEMENTS;i++)); do echo " Param[${i}]: ${args[${i}]}" >> /usr/local/lsws/cxscgi.log
done
echo "  Script run and it returned it failed" >> /usr/local/lsws/cxscgi.log
exit 1

You must have the execute bit on for this script for the LiteSpeed user.

There are a number of notes here:

  • #!/bin/bash indicates the bash interpreter is used.
  • echo "1 FAIL!" causes the engine to interpret this as a failure. In initial testing you’re going to want to do that. Eventually, you’ll want your script to do things like run a virus scan or something similar
  • The remaining lines are used to output the command line parameters and the date and time to a log file (written in this example to /usr/local/lsws/cxscgi.log). In my case the LiteSpeed user was given read/write access to the /usr/local/lsws directory, but if you don’t want to do that, you may choose to write your log to a different location, where the LiteSpeed user has access. Having this information available will help in debugging as it will let you know that the script was run and what parameters were passed to it.

It is highly recommended that you simply run this script manually as the LiteSpeed user if you can, and you should see it write to the output log file with the date/time and any command line parameters you pass to it. This will verify that it is executable and that it can write to the directory where you expect to log to.

Set up a Test for Your Rule/Script

It is recommended that you use curl to test your rule. You may wish to write a simple script so that the request is repeatable. In the example above, I wrote a simple script with one line in it.

curl --http1.1 -F file=@/usr/local/lsws/cxscgi.sh 'http://127.0.0.1:8088/phpinfo.php?b=inspectfail' -v -s > /usr/local/lsws/403error-1.txt 2>&1

This script will set the argument b to the correct value to hit the first part of the chain, upload the script file and write the output to the file /usr/local/lsws/403error-1.txt.

Debugging Tips

As with any debugging, it is recommended that you isolate the condition as much as possible. A single rule set should be used to test inspectFile. You can add it to your larger rule sets once it has been validated.

You should check the LiteSpeed error.log file and verify that there were no errors in loading the rules.

There are a number of suggestions already mentioned in the example above. For OpenLiteSpeed, a debugging log will be written in /tmp/auditlog-debug.txt and it will contain details which will show you any issues that the SpiderLabs library may be having in executing the rule or your script. This log should be examined in detail.

We recommend that initially, you create a script that writes to a log file. You should verify that this log file is updated.

Once you have the script executed and the rule is causing the transaction to fail correctly (as you can see in the 403error-1.txt file in the example above), you can change the rule to return success and validate that the rule no longer causes a failure. At this point you can modify your script to perform the examination function you wish to perform.