Blocking more spammers

This won’t work for everyone, but perhaps it will give you an easy place to start.

I’ve noticed lately that I’m getting HUNDREDS of messages to bogus email addresses in the space of a few minutes, or perhaps hours, from a particular address or block of addresses. I suppose the theory is that if they send to <dictionary> it will eventually hit a valid address.

The goal here is to identify a host that is sending these kinds of messages, and just block them at the firewall. I want to make sure that they are not one of my secondary MX’es, which, by no fault of their own, are just relaying messages to me. And I want to make sure that I don’t block someone who innocently mistypes an address. So there are some checks for that. Specifically, I have a list of addresses that are valid MXes. And I only inflict punishment if they send more than 5 messages.

The /usr/bin/BLOCK script is a shell script that adds them to my firewall deny list. Since that’s typically a one-liner, it’s left as an exercise to the sysadmin.

use strict;
use warnings;

my @exceptions = qw(;
my %spammers = ();

while (<>) {
    next unless /User unknown in local/;
    my $skip = 0;
    s/.*?RCPT from (.*?):.*$/$1/;
    foreach my $x (@exceptions) {
        $skip++ if m/$x/;
    next if $skip;

    # So, we should now have someone that's not an MX, who just sent
    # us email to a bogus address. What should we do about it?
    my $spammer = $_;
    $spammer =~ s/^([^[]*)[//;
    my $host = $1;
    $spammer =~ s/][^]]*$//;

    if ($spammers{$spammer} > 5) {
        # This person is almost certainly a spammer.
        warn "$host (at $spammer) appears to be a spammer. Adding them to the firewall";
        # Except, if they are passing bogus stuff in the address,
        # somehow. Not that I think they could ...
        if ($spammer =~ /[^.d]/) {
            warn "Hmm. Troubling. Strange stuff in the address. Better skip this one";
        `/usr/bin/BLOCK_no_log $spammer`;
        # And now we don't need to keep that data around any more
        delete $spammers{$spammer};

To use this, just pipe syslog through it, thus:

tail -f /var/log/maillog | ./