I’ve seen a bunch of attempts to hack at my zencart admin area and an old phpBB2 install that is no longer there. Both of these don’t work, and just return a 404 error, but it’s kind of annoying to keep spending compute cycles on this sort of behavior. Besides, now that I have installed the statsd and graphite packages, I am set up to provide a graph of whatever I want. So why not track the hackers, lock them out, and provide a graph of activity at the same time? So here goes.
2016-12-28 edit: see my github repo at https://github.com/dminear/apache_hack_check
I also run mod_perl in the Apache2 server, so I wanted to hook in to the PerlAccessHandler and do the work. The following is the Apache2 config lines:
PerlModule ModPerl::DanHandler <Location /> #SetHandler perl-script PerlAccessHandler ModPerl::DanHandler </Location>
And the following is the Perl module that is in the /usr/local/lib/site_perl/ModPerl directory on my Ubuntu machine:
package ModPerl::DanHandler;
use strict;
use warnings;
use FileHandle;
use IO::Socket::INET;
use Apache2::Log;
use Apache2::RequestRec ();
use Apache2::Connection ();
use Apache2::Const -compile => qw(FORBIDDEN OK :log);
BEGIN {
mkdir "/tmp/bad_ips";
chmod 0777, "/tmp/bad_ips"
}
my $sock = IO::Socket::INET->new(PeerPort => 8125,
PeerAddr => '127.0.0.1',
Proto => 'udp');
sub handler {
my $r = shift;
my $str = $r->connection->remote_ip();
# if there is an attempt to access "zencart/admin", then put the ip on the block list
if ($r->unparsed_uri() =~ /zencart\/admin$/ ||
$r->unparsed_uri() =~ /zencart\/+admin\/+/ ||
$r->unparsed_uri() =~ /phpbb2/i ) {
#$r->log_error("BAD IP: $str");
$sock->send( "hacker.unparsed_uri." . $r->unparsed_uri() . ":1|c\n" ) if defined $sock;
my $fh = FileHandle->new( "> /tmp/bad_ips/$str");
if (defined $fh) {
print $fh "dummy";
$fh->close;
}
}
# check the block list
if (-e "/tmp/bad_ips/$str") {
$sock->send( "request.blocked:1|c\n" ) if defined $sock;
return Apache2::Const::FORBIDDEN;
} else {
$sock->send( "request.allowed:1|c\n" ) if defined $sock;
$sock->send( "request.hostname." . $r->hostname() . ":1|c\n" ) if defined $sock;
return Apache2::Const::OK;
}
}
1;
So now when someone comes in and tries to access /zencart/admin (or some gyrations thereof), the IP address gets stored in the tmp directory as a filename. On every request, the remote IP address is checked, and if found returns a 403 Forbidden response. The nice thing is that this happens for any request thereafter. Because it’s early in the request stage, there’s not too much overhead. Plus I get the satisfaction of watching the banned IP addresses grow.
Then there’s some logic to update the statsd server based on good or bad requests. Here’s a screen capture of it in action (click on the image to enlarge):

