[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

SPAMSHIELD



All-
I am attaching a script with this email called spamshield.pl.
Its a perl script whose aim to read the maillog file every 5 minutes or
so, and determine whether or not a certain smtp server should be
considered a spammer or not. The response to the decision that a server is
a spammer is a null route being added to the server's routing table.

The script seems to be working well in the sense that it is reading the
maillog and making judgements on certain servers being "spammers", however
I am not seeing null routes being added.

If you could please look through the script and see if you find any syntax
errors, I would greatly appreciate it. 

Charles

#!/usr/bin/perl -T

$version = 1.40;
$DEBUG=0;

###############################################################
# customize below
###############################################################

$log = "/var/log/messages";  		# sendmail log location
$lastlines=1500;			# how many lines at the end of the log should we look at
$spamthreshold=200;			# this is how many mails can be seen from a single IP
					# in the last $lastlines lines in the logfile before
					# considering it spam. adjust this to accomodate
					# busy systems and events like coming up after a
					# long downtime (when a lot of mail will be delivered
					# from various hosts)

$dontblock="/usr/local/spamcontrol/dontblock"; 		# list of IP hosts that
					       		# are never to be blocked
$blockactive="/usr/local/spamcontrol/blocked"; 		# these hosts are currently
					       		# blocked by SpamShield - 
					       		# for sysadmin review
$blockignore="/usr/local/spamcontrol/blockignore"; 	# be silent about these ones

$securetmp="/usr/local/spamcontrol"; 			# enter directory name that CANNOT(!) be
				     			# used by anyone except the uid under
				     			# which this program is run


$blackhole="yes";					# this MUST be an unused IP number on the
							# local network, or error messages and chaos
							# might ensure. undefine to not add a route,
							# this should only be used on machines with
							# known stable routing engines.

# who will receive alerts ? undefine to stop mail alerts
$maintainer="charles\@lunarmedia.net";

# define locations of programs below , systems vary
$SENDMAIL="/usr/sbin/sendmail";
$TAIL="/usr/bin/tail";
$AWK="/usr/bin/awk";
$GREP="/usr/bin/grep";
$SORT="/usr/bin/sort";
$CAT="/bin/cat";
$DATE="/bin/date";
$ROUTE="/sbin/route";
# $WINNUKE="/usr/local/spamcontrol/winnuke"; # define if retaliatory action desired

###############################################################
# end of user-customized parts
###############################################################

$ENV{'PATH'} = '/bin:/usr/bin:/usr/sbin';   
$HOST = `hostname`;

my @loglines = ` $TAIL -$lastlines $log |$GREP "from=" `;
my $line;

 if ($DEBUG > 5) {
  print "Trying to open $securetmp/ss-mailstats\n";
 }
open(TMPLIST, "|$SORT > $securetmp/ss-mailstats") || die "Cannot open file $securetemp/ss-mailstats :$!\n";

foreach $line (@loglines) {

 if ($DEBUG > 8) {
  print "$line";
 }

 $line =~ /^.*?from=(.+?),.*?nrcpts=(.+?),.*?relay=.*?(\[.*?\])??$/;

 $from=$1;
 $nrcpts=$2;
 $relay=$3;

 $relayip="127.0.0.1";  # mail sent by local user
 if ($relay ne "") {
  $relay =~ /^\[(.*?)\]$/;   # cut out brackets
  $relayip=$1;
 }

 print TMPLIST "$relayip $nrcpts $from\n";


 if ($DEBUG > 5) {
   print "$relayip $nrcpts $from\n";
   # print "from:$from\nnrcpts:$nrcpts\nrelay:$relay\nrelayip:$relayip\n\n";
   # print "$from $nrcpts $relayip\n";
 }


} # foreach $line

close(TMPLIST);

##############
# We have the list in the tmp file, now add up all the nrcpts values and
# calculate a total. if it goes over a threshold, it's spam
##############

 if ($DEBUG > 6) {
 print "Opening $securetmp/ss-mailstats for processing.\n";
 }

my @iplines = `$CAT $securetmp/ss-mailstats`;

$ncount=0;
$lastip="-.-.-.-";
$lastsender="";

# this log is for administrative control/checks
open(TMPFILE2, "|$SORT -rn > $securetmp/ss-ipstats") || die "Cannot open file $securetemp/ss-mailstats :$!\n";

foreach $line (@iplines) {

 if ($DEBUG > 6) {
  print "$line";
 }

 $line =~ /(.*?)\s(.*?)\s(.*)/;
 $nowip = $1;
 $sender = $3;

 if ($DEBUG > 7) {
  print "1:$1\n2:$2\n3:$3\n";
 }

 if ($lastip ne $nowip) {
   if ($lastip ne "-.-.-.-") {  # we are not at the beginning of the file
     print TMPFILE2 "$ncount nrcpts from $lastip IP\n";

     if ($ncount > $spamthreshold ) { 			# buzz! buzz! buzz!
	&actspam ; # pass current @_
     } # not over spam threshold - end of spam alert handling

   } # if ($lastip ne "-.-.-.-")
   $ncount = 0; 	# we saw a new IP in the sorted ss-mailstats file
   $lastip = $nowip;
   $lastsender = $sender;
 }

 $ncount += $2; # we saw this IP on the last line, add up number of recipients
	        # we are not losing this parameter above, as spam control
	        # is only asserted after a block is completely added up
} # end of foreach $line (@iplines) loop

# Exception handling: shit, there is a spam all the way at the EOF, which we might
# to act on, must write to ss-ipstats log, too

print TMPFILE2 "$ncount nrcpts from $lastip IP\n";

if ($ncount > $spamthreshold ) { 			# buzz! buzz! buzz
  &actspam ; # pass current @_
} # not over spam threshold - end of spam alert handling


close (TMPFILE2);

# the end
# this program makes most sense in a crontab file , called every 3 minutes or
# so, depending on the number of sendmail log files you look at and the
# processing time it takes:
# SpamShield <tm> - uses minimal CPU (1s/1000 log lines examined). Run often.
# 0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57 * * * *	/usr/local/spamcontrol/spamshield.pl

#######################################################################
#######################################################################
# ------------------------------ end main ----------------------------
#######################################################################
#######################################################################

sub actspam {
      # print "Spam from: $line , count: $ncount\n";
      $ip = $lastip;   # we really have detected spam from the line before!
      $ip =~ /^(\d{1,3}?.\d{1,3}?.\d{1,3}?).\d{1,3}?$/;
      $cnet = $1;
      # print "$ip is in network $cnet \n";

      if (`$GREP $ip $blockignore` eq "") {

      if (`$GREP $ip $dontblock` eq "") { 
	if ($DEBUG>3) {print "$ip not in dontblock file - proceeding\n";}
	if (`$GREP $ip $blockactive` eq "") {

	  if ($WINNUKE ne "") {	# nuke spammer on Win95/NT machines. Crude.
	   `$WINNUKE $ip >/dev/null 2>/dev/null &`;
	   `$WINNUKE $ip >/dev/null 2>/dev/null &`;
	   `$WINNUKE $ip >/dev/null 2>/dev/null &`;
	   sleep (10);
	  }
	  if ($DEBUG>3) {print "$ip not yet blocked - now filtering\n";
			 print "debug: route add -host $ip reject\n";}
	  if ($blackhole ne "") {  		# must be root to do this !
	   system("$ROUTE -n add -host $ip reject >/dev/null 2>/dev/null");
	  }
	  open (BLOCK,">> $blockactive"); # ignore errors
	  $date = `$DATE`;
	  $time_utc = time;
	  print BLOCK "$ip $time_utc BLOCKED because of spam from $lastsender at $date"; # CR from $date
	  close (BLOCK);

	  if ($maintainer ne "") {
	   open(MAIL,"|$SENDMAIL $maintainer") || die "Couldn't open mailer:$!\n";
	   print MAIL <<EOF;
From\: "SpamShield" <root>
Subject\: SpamShield WARNING: spammer nuked

Warning: SpamShield <tm> on host $HOST has blocked the ip route to $ip because
of spamming activity from (possibly) $lastsender : $ncount mail rcpts
detected from this IP !
Check logfiles immediately, this is a spam in progress.

EOF
	   close(MAIL);
	  }

	} else { if ($DEBUG>3) {print "$ip already blocked.\n";}
	}
      } else {

	if (`$GREP $ip $blockactive` eq "") {

		if ($DEBUG>3) {
		print "Spam attack from unblockable local host $ip - Alert!\n";
		}

	 open (BLOCK,">> $blockactive"); # ignore errors
	 $date = `$DATE`;
	 $time_utc = time;
	 print BLOCK "$ip $time_utc NOT BLOCKED: WARNING: Local Spam from $lastsender at $date"; # CR from $date
	 close (BLOCK);

	 if ($maintainer ne "") {
	  open(MAIL,"|$SENDMAIL $maintainer") || die "Couldn't open mailer:$!\n";
	  print MAIL <<EOF;
From\: "SpamShield" <root>
Subject\: SpamShield ALERT: local spam !

ALERT: SpamShield <tm> on host $HOST has detected a local spam coming from $ip with a possible sender
address of $lastsender, but was configured not to block this
IP number. $ncount mail rcpts detected from this IP !
This is a LOCAL spam in progress !!!! Check logfiles immediately.

EOF
	  close(MAIL); # did I say the formatting above sucks ?
	 }
	} else { if ($DEBUG>3) {print "$ip not blocked, but reported as local spam.\n";}
	       } # end of if-not-blockactive-else
      } # end of dontblock or else test
     } else { if ($DEBUG>3) {print "Ignoring spam from host $ip by config.\n"}
	    } # end of blockignore test

}
####################
# end of sub actspam