Stopping Directory Harvest Attacks

Brad Beckenhauer Brad at beckenhauer.com
Tue Mar 28 05:08:49 IST 2006


Hello all,

First off my disclaimer...  I'm not a programmer and this script is one
of my first perl writing ventures. A guys gotta start somewhere!  I was
getting hammered with DHA (Directory Harvest Attacks) and decided to
write my own.  Given that stopping DHA attacks has come up a couple of
times on this forum, I hope that some of you will find this useful and
a starting place to develop this script further.

Julian,  I thought it would be cool to use some of your phishing logic
to re-write the code to use a database instead.  

This perl script parses the mail.logs looking for multiple rejections
from the same IP Address. Presume this is a Directory harvest Attack if
the number of occurances of an IP Address is above the user defined
limit of $SCORE, then create an iptables DROP statement for that IP
Address.  Each time the script is run, it will remove the previous
iptables entries, rescan the mail.log and add new entries.  If an IP
offender no longer appears in the mail.log, then they are dropped off
the blocked "list".  This works great a a cron job every hour and if you
roll your mail logs daily the offending IP address is dropped off the
list.

Again, it's far from perfect, feel free to adapt it if you like it, but
please share with the rest of us.  

#!/usr/bin/perl
#
# harvest.pl
#
# version 1.0
# Date: 10 September 2005
#
# Find possible email "directory harvest attacks" from mail.logs
#
# Copyright:
# This program is free software; you can redistribute it
# and modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation.
#
# This program is distributed WITHOUT ANY WARRANTY; without even 
# the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
#
# Purpose: 
# Parses the mail.logs looking for multiple rejections from the same
# IP Address. Presume this is a Directory harvest Attack if the
# number of occurances of an IP Address is above the
# user defined limit of $SCORE, then create an iptables DROP
# statement for that IP Address.  Each time the script is run, it will
# remove the previous iptables entries, rescan the mail.log and add
# new entries.  If an IP offender no longer appears in the mail.log,
# then they are dropped off the blocked "list".
#
# Rotating your email logs Daily helps keep the list cleaner.
#
# When executed, this perl script 'harvest.pl will create two output
# files in the current directory:
#
# iptables.sh      Contains the iptables entires to be added to iptables
# iptables-undo.sh Removes all entries created in the last run.
#
#
#  Idea and initial code by: Brad Beckenhauer
#
#  Credits: David Kirby.
#
#

# This is the threshhold for the number of times an
# IP Address with a 550 error can occur in the mail logs before
# being added to the block list.
# SCORE should be set high enough that deleted user accounts
# on your system do not "trigger" a false block.
$SCORE=10;

# Turn on console output, Shows what IP Addresses will be blocked.
$DEBUG=1;    

# read input from the /var/log/mail.log file
foreach $log (</var/log/mail.log >) {
   open(FILE,$log);
   while (<FILE>) {
      chomp;    # Remove newlines
      # create list of 55x errors
      # 554 needs to be fixed to make sure that 'found' is numeric
      if (/\]: 554 Service unavail/) { push @found, $_; }
      # create list of 450 errors
      if (/\]: 450 Client host rejected\</) { push @found, $_; }
   }
   close(FILE);
}

# parse list of 55x errors, extracting each IP
foreach $entry (@found) {  
   ($ip)=$entry=~/.*\[.+?\].*\[(.*)\]/;
   $iplist{$ip}++;   # count each IP found
}

# sort list of IPs ascending by number of occurances
@iplist=sort { $iplist{$a} <=> $iplist{$b}; } (keys %iplist);

# Print a report to console
if ( $DEBUG ) {
   printf "%-15s %-5s\n","\nIP Address"," Count";
   printf "%-15s %-5s\n","---------------","-----";
}

open( OUT,  ">>/usr/local/sbin/dha.sh" );
if ( \! -f "/usr/local/sbin/dha-undo.sh" ) {
   print OUT "#!/bin/sh\n";
}
close(OUT);

# if the undo file exists, do this routine
if ( -f "/usr/local/sbin/dha-undo.sh" ){  
#   if ( $DEBUG ) { print " Undo file exists, reading it\n"; }
   open(UNDO, "/usr/local/sbin/dha-undo.sh") || die " can't open
iptables-undo.sh";
   open(OUT, ">/usr/local/sbin/dha.sh") || die " can't open
iptables.sh";

# read in the undo file and send it to the OUT file
   while ( <UNDO> ) {  
      # need to SKIP the first two lines of the input 
      # as they contain header
      print OUT $_ ;
   }
   close(UNDO);
   close(OUT);
} else {
   if ( $DEBUG ) { print "Undo file does not exist, creating\n"; }
}

# create a new BLANK file w/headers to "undo" the
# new entries added to the table
open( OUT, ">/usr/local/sbin/dha-undo.sh" );
print OUT "#!/bin/sh\n";
close(OUT);

# open for append the iptables file for new IPs that exceed SCORE
open( NEW,  ">>/usr/local/s/dha.sh" );
open( UNDO, ">>/usr/local/sbin/dha-undo.sh" );

# loop for each IP address found and add it to the tables.
foreach $ip (@iplist) {
  if ($iplist{$ip} >= $SCORE ) {
     print NEW  "iptables -A INPUT -s $ip -p tcp -m tcp --dport 25 -j
DROP\n";
     print UNDO "iptables -D INPUT -s $ip -p tcp -m tcp --dport 25 -j
DROP\n";
     if ( $DEBUG ) { printf "%15s\t%5d\n",$ip,$iplist{$ip};  }
  }
}

if ( $DEBUG ) {
   printf "\nrun /usr/local/sbin/dha.sh to ADD these entries to the
firewall\n";
   printf "run /usr/local/sbin/dha-undo.sh to REMOVE all entries from
the firewall\n\n";
}

close(NEW);
close(UNDO);
chmod 0755, '/usr/local/sbin/dha-undo.sh';
chmod 0755, '/usr/local/sbin/dha.sh';





More information about the MailScanner mailing list