Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Firewalling brute-force spam attacks

by hacker (Priest)
on Mar 26, 2004 at 02:46 UTC ( [id://339932]=perlquestion: print w/replies, xml ) Need Help??

hacker has asked for the wisdom of the Perl Monks concerning the following question:

I've been taking a very heavy-handed approach to stopping spam lately, to help my users maintain a nice clean, spam-reduced mail environment.

First, I should mention that my system's tolerance for spam is nil. I have several hundred rulesets configured in the MTA, to block based on common subjects/heuristics, a very tweaked SpamAssassin setup, 6 different RBLs, including blackholes.us for 7 countries, clamav, and some very detailed procmail recipes to handle the AV and other things that slip through.

That being said, spam still gets in.

The piece of the puzzle that I'm trying to solve now, is being able to firewall off people/machines that are brute-forcing accounts on servers with domains we host (quite a few). An example of this from one of the live servers looks like this.

I cooked up a quick hackish bash script to deal with these, and half-shimmed in some perl to regex out the IP. I'd like to entertain some ideas and approaches to handling this entire thing in Perl exclusively.

Currently, it looks like this, and works perfectly, though is hackish:

# Wrapped for Perl Monks, unwrap before using LOG="/path/to/mail.log" IPT="/sbin/iptables" BAN="$IPT -A INPUT -s {} -p tcp -m tcp --dport 25 -j REJECT" DUP="$IPT -D INPUT -s {} -p tcp -m tcp --dport 25 -j \ REJECT --reject-with icmp-port-unreachable" # Find the offenders and ban them from reaching port 25 grep -A1 "User unknown" $LOG | grep nr \ | perl -lne 'print /\[((?:\d+\.){3}\d+)\]/' \ | sort | uniq -d | xargs -i $BAN # Check the existing rulesets for dupes, and remove $IPT-save | sort | uniq -d | perl -lne 'print /((?:\d+\.){3}\d+)/' \ | xargs -i $DUP

The process is:

  1. Grep the mail log to catch the line directly after the "User unknown" line
  2. Grep the result returned, for the line containing the offender's IP address directly (contains the string 'nrcpts')
  3. Regex out the IP address from that line (appears last in the string, bordered by braces, eg: [1.2.3.4])
  4. Wrap each of the IPs returned in an iptables command to block that IP on port 25
  5. Check the existing list of IP addresses stored in the firewall rules for dupes and remove any which match
  6. Lather. Rinse. Repeat, hourly.

Anyone want to take a stab at converting this little shell'ism to "Pure Perl™"?

Replies are listed 'Best First'.
•Re: Firewalling brute-force spam attacks
by merlyn (Sage) on Mar 26, 2004 at 03:07 UTC
    A slightly different strategy, with slightly different tools: I was blocking for up to 4 hours all the noticed spammers, using this script:
    #!/usr/bin/env perl use strict; $|++; my $MYDOOM = "/home/merlyn/mydoom"; my $MYSOBIGF = "/home/merlyn/sobig.f"; my $HOURS = 4; sub now { my @now = localtime; sprintf "%02d-%02d %02d:%02d:%02d", $now[4]+1, @now[3,2,1,0]; } use POE qw(Wheel::FollowTail Filter::Line); POE::Session->create (inline_states => {_start => sub { my($kernel) = @_[KERNEL]; $kernel->yield("initialize_ip_list"); }, initialize_ip_list => sub { my ($kernel, $heap) = @_[KERNEL, HEAP]; @{$heap->{ip_list}} = `pfctl -t spammers -T show` =~ /(\S+)/g; $kernel->yield("start_tailing"); $kernel->yield("unblock_an_address"); }, start_tailing => sub { my($kernel, $heap) = @_[KERNEL, HEAP]; $heap->{tailer} = POE::Wheel::FollowTail->new (Filename => $MYDOOM, Filter => POE::Filter::Line->new (InputRegexp => qr/(?<=\n)(?=[^ \t])/), InputEvent => 'tailer_got_line', ); $heap->{tailer2} = POE::Wheel::FollowTail->new (Filename => $MYSOBIGF, Filter => POE::Filter::Line->new (InputRegexp => qr/(?<=\n)(?=[^ \t])/), InputEvent => 'tailer_got_line', ); }, tailer_got_line => sub { my($heap, $line) = @_[HEAP,ARG0]; my ($name, $ip) = $line =~ /\((\S+)\s+\[([\d.]+)\]\)\s+by blue\. +stonehenge\.com/ or return; return if $ip eq "127.0.0.1"; print now(), ": BLOCKING $ip ($name): "; system "pfctl -t spammers -T add $ip"; push @{$heap->{ip_list}}, $ip; }, unblock_an_address => sub { my($kernel, $heap, $state) = @_[KERNEL, HEAP, STATE]; my $ip_list_ref = $heap->{ip_list}; if (my $ip = shift @$ip_list_ref) { print now(), ": UNBLOCKING $ip: "; system "pfctl -t spammers -T delete $ip"; } my $delay = @$ip_list_ref ? 60*60*$HOURS / @$ip_list_ref : 10; $delay = 1 if $delay < 1; $delay = 60 if $delay > 60; $kernel->delay($state, $delay); }, }); POE::Kernel->run;
    Of course, the parts about what files to look for, what patterns to look for, and what commands to execute to disable and enable are all different, but maybe the design can be reused.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

Re: Firewalling brute-force spam attacks
by TilRMan (Friar) on Mar 26, 2004 at 03:42 UTC

    I cheated because I (think I) know how to use iptables.

    my $LOG = "/path/to/mail.log"; my $IPT = "/sbin/iptables"; my %known = map { $_ => 1 } get_current_offenders(); open LOG, $LOG or warn; while (<LOG>) { if (/User unknown/ ... /\[((?:\d+\.){3}\d+)\]/ || 1) { $1 and !$known{$1} and ++$known{$1} and ban($1); } } close LOG; sub get_current_offenders { my @offenders; # Let the shell have it, it's easy and only happens once open IPTABLES, "$IPT -n -LINPUT |" or die; while (<IPTABLES>) { if (/^REJECT\s+tcp.*?([\d.]{7,})/) { # Might tweak this push @offenders, $1; } } return @offenders; } sub ban { my ($offender) = @_; # Save ourselves a (not-so-)expensive exec() system($IPT, '-A', 'INPUT', '-s', $offender, qw( -p tcp -m tcp --dport 25 -j REJECT )); $? and warn; }

    -- 
    LP^>

Re: Firewalling brute-force spam attacks
by zakzebrowski (Curate) on Mar 27, 2004 at 17:36 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://339932]
Approved by Vautrin
Front-paged by Vautrin
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (3)
As of 2024-04-25 17:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found