Tarpitting with Apache and mod_security2
Like any other superhero, I like to fight the evil in this world. At least on Sundays. Today's evil of choice were the incessant beatings my virtual server has to take from these stupid scans for a phpMyAdmin installation:
220.127.116.11 [19/Aug/2007] "GET /phpmyadmin/main.php 18.104.22.168 [19/Aug/2007] "GET /phpMyAdmin/main.php
First we prep our Debian server with mod_security. Since I am using Apache 2.2 which is not working with libapache2-mod-security because of a dependency problem, I decided to turn directly to mod_security2, the successor to the 1.x series.
So, let's follow the hint on the download page to add this to /etc/apt/sources.list
# Packages for Etch deb http://etc.inittab.org/~agi/debian/libapache-mod-security2/etch ./ # Packages for Sarge deb http://etc.inittab.org/~agi/debian/libapache-mod-security2/sarge ./
and do the Debian mantra:
apt-get install libapache2-mod-security2
To test whether my Apache is now secure (ha!), I hooked a trivial mod-security-basic file into my /etc/apache2/conf.d:
SecServerSignature "Zanussi Machine 3.1415"
This line should make the hacker's life difficult as there are no known vulnerabilities for Zanussi. And indeed we see in the response headers:
Server: Zanussi Machine 3.1415 mod_perl/2.0.2 Perl/v5.8.8
which again proves that Perl can run on anything.
So what about these phpMyAdmin scans? We simply add a rule to delay each request for 5 seconds (\ line wrapper should be removed):
SecRule REQUEST_URI /phpMyAdmin \ pause:5000,log,noauditlog,status:402,deny
For this purpose we have turned on the rule engine and have then defined an action which will be taken if a particular rule triggers but has no action defined. While I do not plan to have this, I decided to log the request, but allow it. Still I let the server return code 406 (Not Acceptable).
The rule language looks much cleaner than in the 1.x series and it always follows the same pattern:
- First a variable coming from the request (or the response), be it a header, a URI parameter, the body or an internal variable is looked at,
- then the condition (can also be a regexp) is tested on that variable, and if that is satisfied,
- then a set of actions is taken.
In the case that the request URI contains /phpMyAdmin the server will wait for 5000 milliseconds, will log the request, but will refrain from writing a detailed audit.
Then I thought that it would be appropriate to return 402 (Payment Required) as status. Also superheroes have humor. On Sundays.
A final note: True, the above setup isnogood in the case of a DoS attack, but also for this scenario you can do something with mod_security2.
AsTMa fragment attached.