Intrustion detection and prevention for Apache with mod-security
Posted by Steve on Tue 28 Dec 2004 at 19:35
mod-security is a simple intrusion detector and preventor for Apache, allowing you to wrap an additional layer of protection around your webserver.
mod-security is a rule-based IDS (intrusion protection system) which allows you to create rules describing abnormal requests to your webserver. When malicious requests are made which match your rules the request can be denied, and the details logged.
With the rise of recent PHP-based worms, (such as Santy.E, or the renamed version PhpIncludeWorm), which attempt to exploit any PHP script they find it's suddenly much more important to protect your server.
The real protection, of course, is to only install and use secure code but if you're not auditing the code yourself, and the author was careless you could suddenly find yourself attacked with no warning.
Because the mod-security module can examine all parameters passed to your server be they GET or POST requests it is a perfect place to detect and defend against these attacks.Installing mod-security on Woody
Installing mod-security on an Unstable machine
If you're running a webserver with Debian's stable distribution you will discover that the module isn't available yet, it's only available in the testing or unstable branch. This leaves you with two choices:
- Install the module from source
- Install a backport
(A backport is a Debian package which has been produced by somebody targetting one of the older releases).
Installation from source does have a couple of drawbacks - it isn't going to be upgraded via APT, and it isn't going to work well if you upgrade your installation of Apache often.
Still for such a simple module this is a good choice, so long as you install it and keep track of updates yourself. (You could do this by subscribing to the mod-security-announce mailing list).
First of all download the code, currently you can download it and unpack it by running:cd /tmp wget http://www.modsecurity.org/download/mod_security-1.8.6.tar.gz tar -pzxvf mod_security-1.8.6.tar.gz
You can always find the latest version at the modsecurity website.
Once you've downloaded the source and unpacked it you will need to install the apache development headers - so that the code can build against it:apt-get install apache-dev
Then to build it become root and execute the following command:root@undecided:~# cd /tmp/mod_security-1.8.6/apache1 root@undecided:/tmp/mod_security-1.8.6/apache1# apxs -cia mod_security.c gcc -DLINUX=22 -DEAPI -DTARGET="apache" -DHAVE_SET_DUMPABLE -DDB_DBM_HSEARCH=1 -DDEV_RANDOM=/dev/random -DUSE_HSREGEX -O1 -g -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC -DSHARED_MODULE -I/usr/include/apache-1.3 -c mod_security.c mod_security.c: In function `sec_logger': mod_security.c:3474: warning: `nbytes' might be used uninitialized in this function gcc -shared -o mod_security.so mod_security.o [activating module `security' in /etc/apache/httpd.conf] cp mod_security.so /usr/lib/apache/1.3/mod_security.so chmod 755 /usr/lib/apache/1.3/mod_security.so cp /etc/apache/httpd.conf /etc/apache/httpd.conf.bak cp /etc/apache/httpd.conf.new /etc/apache/httpd.conf rm /etc/apache/httpd.conf.new
Don't be alarmed by the messages at the end, your apache configuration files should have been left undamaged - the installation script merely adds some basic settings, and then enables the module. None of this will take effect until Apache is restarted, but first we'll need to make changes.
As there is a package available for Debian's unstable and testing distributions we can install it simply by running the following command as root:apt-get install libapache-mod-security
Following either of the previous instructions you should have some minimal configuration settings included in your Apache configuration file /etc/apache/httpd.conf.
Essentially there are two things we need to setup:
- The logging and security options
- The rules which detect attacks
As mod-security is a rule-based system we need to define the rules which "evil requests" must match to trigger our protection, then we need to setup the response the server will generate.
The general options are probably sufficient and are installed by default:# Turn the filtering engine On or Off SecFilterEngine On # Make sure that URL encoding is valid SecFilterCheckURLEncoding On # Unicode encoding check SecFilterCheckUnicodeEncoding Off # Only allow bytes from this range SecFilterForceByteRange 0 255 # Only log suspicious requests SecAuditEngine RelevantOnly # The name of the audit log file SecAuditLog /var/log/apache/audit_log # Debug level set to a minimum SecFilterDebugLog /var/log/apache/modsec_debug_log SecFilterDebugLevel 0 # Should mod_security inspect POST payloads SecFilterScanPOST On # By default log and deny suspicious requests # with HTTP status 500 SecFilterDefaultAction "deny,log,status:500"
These settings turn on the code, disable incoming data which is outside the byte range 0-255, and setup the logfiles beneath /var/log/apache.
The last line says that the module should deny requests which match malicious patterns (which we've still to define), log them, and send back a 500 status code. (Which corresponds to server error).
Now we come to the fun part. We need to write the list of rules which will match non-legitimate requests. These will really depend upon your site and the kind of scripts you're using, so we can't list rules which are too generic.
What we can do is pretty impressive:
- Match against values which are submitted
- Match against the names of submitted parameters
- Match against the output coming from the server
- Match against cookies
- Match against remote addresses.
A simple rule is to say that nobody should be sending a parameter with the value /etc/passwd - unless they are trying to exploit a vulnerable script which will allow arbitary file reading.
We can do this with the following rule:SecFilter /etc/passwd
Similarly if there is a script upon our website which is written badly enough to allow a remote user to execute a command we can detect them trying to see who they are, or looking at files with these three rules:SecFilter /bin/ls SecFilter /bin/uname SecFilter /usr/bin/whoami
The recent Santy.A worm can be matched by this rule:SecFilterSelective ARG_highlight %27
Some of the other worms I've seen attempt to use wget to download scripts which they can then execute - so you can block those with:SecFilter cd\x20/tmp SecFilter wget\x20
There are different types of rules you can use, mostly we've used "SecFilter" which matches against a string. We can also use "SecFilterSelective" which allows us to match the contents of a named parameter.
Although we've defined the general action to take against matched rules we can customize the actions on a per-rule basis. For example if we wish we can cause a redirect with the following:SecFilter /etc/passwd redirect:http://www.foo.com/bad/request.html
This will send any request to a new URL.
Or if we wish we can execute a command to log the request:SecFilter /bin/rm execute:/usr/local/bin/mail-admin.pl
To test it we need to install a CGI script, or PHP script which is a gaping security hole - then show how this is mitigated by the use of mod-security. Whilst it is important to test the system, this method is potentially dangerous - and you should definitely make sure you remove the script when you're satisfied.
Save the following script somewhere within your document root, with a name such as "vulnerable.php":<? include( $file ); ?>
This PHP script is a gaping security hole - it allows a malicious attacker to read any file upon your system - because the "$file" parameter is supplied by the remote user, and isn't checked before it is used an attacker could request your /etc/motd file with something like the following request:http://your.server.com/vulnerable.php?file=/etc/motd
As we've enabled "password protection" in our rules above we should be able to test this works by requesting:http://your.server.com/vulnerable.php?file=/etc/passwd
If the rules have been setup correctly then the following rule should match:SecFilter /etc/passwd
So, when you request that URL you should see a server error message - and the password file should be hidden.
If that worked then you can be sure that the module is active, and all you need to do is make sure that your rules are adequate - and remove the vulnerable PHP script!
There is a wealth of documentation which you might wish to read to get the most out of this module.
The primary source is the official module homepage:
Other interesting articles include: