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):