Bugtraq mailing list archives

Re: SMTP server account probing


From: Tobias.Kreidl () NAU EDU (Tobias J. Kreidl)
Date: Wed, 10 Mar 1999 14:30:25 -0700


Scott Fendley said on Tue, 09 Mar 1999 16:16:13 -0600:

Couldn't you just compile sendmail with tcp_wrapper support, and have a
script parsing your logs so that if someone manages to get n # of pokes at
your system then their Ip address and/or DNS server will be placed in the
hosts.deny.  Then as an admin you remove those that need to be removed
after the problem user has been properly slapped or you could possibly run
an automatic removal of k # of hours (or days).

I did this a year or so ago, using a shell script.  Via a cron job, it can look every
10 minutes or however often you wish and if it sees an incoming mailbox exceeding a
certain size that has received email more than N times from a particular user, it
automatically puts an entry into /etc/hosts.allow (and since sendmail is run through
inetd, the effect is instantaneous).  In this case, hosts.deny is empty, but you could
readily make the appropriate change to use hosts.deny, if desired.

It's one way to protect against mail bombings, as well.  Right now, the code checks
if the mailbox (inbox) has exceeded a certain size before parsing for repetitive senders,
but it'd be trivial to change the code to skip the mailbox size checking by making
MAXSIZE equal to 1.  Note that for systems with lotsof users and big inboxes, the
checking process can take up considerable time and CPU resources.  Another caveat is that
if you have also "friendlies" that send from the same sending host, they, too,
will be blocked.  This could be devastating for, say, email coming from somewhere like
aol.com, so I'd be very careful if and whether you actually use this code.  Or, follow
Scott's suggestion of removing the entry periodically to keep legitimate email
from bouncing.  I wrote this more as an exercise and as a proof of concept and so it's
not been thoroughly exercised and may contain various "gotchas" I haven't even thought
about.

 Tobias J. Kreidl, PhD
 Email: Tobias.Kreidl () nau edu

#!/bin/sh -
#
# mailmonitor -- check if incoming mail boxes exceed a limited size; if so,
# check if mail originated from more than N from the same address, and if
# so, then block that host from being able to send mail until manually
# edited out.  Tested under Solaris 2.X and freeBSD 2.X.
#
# TK 97-Jan-17
# updates: TK 97-Jan-20/21 (initial version)
#
# TK 97-Jan-27: Add list of host exceptions.
#
# TK 98-Apr-11 -- Use "From: " instead of "From " in search.  Otherwise,
# MAILER-DAEMON can dominate in the "From " line.  Grep out any possible
#   trailing ">" in address.
#
# To do:
#   Consider option to flush all mail in /var/spool/mqueue containing
#   that entry.
#   Add an option to skip checking specific accounts.
#
# Copyright (c) 1998 by Tobias Kreidl.  Feel free to distribute this
# code provided this acknowledgment header remains.  The user assumes all responsibilities
# for any use of this code and by using it, releases the author of any liabilities
# or other problems that might result from use of the code either "as is" or after
# any modifications are made to it.
#

PATH=/usr/bin:/usr/sbin:/usr/local/bin; export PATH
unalias rm

# name of this program (important for grepping purposes)... preferably,
# the same full path name as put in the cron entry...
PNAME="/usr/local/bin/mailmonitor"
# limit in bytes:
MAXSIZE=4000000
# limit of messages from one user:
MAXNUM=20
# full path to hosts.allow file:
HAFILE="/etc/hosts.allow"
# list of exceptions for receiving bulk mail (if none, set to "");
# separate the list items with spaces.
# You can exclude hosts you trust here, as well as possibly the
# machine itself on which this runs.
EXCEP="root@localhost mylocalmachine.mydomain.com"
# whom to send mail message reports to:
MAILTO="root, info () anotherhost org"
#

# see if running -- never ever run more than one version of this
# routine at the same time !!!
PS="/bin/ps"
switch="-ax"
flag=`uname`
number="3"
if [ $flag = "SunOS" ] ; then
  switch="-ef"
  number="2"
fi

test=`$PS $switch | grep -v grep | grep $PNAME | wc -l`
if [ $test -gt $number ] ; then
  echo "Another version of this routine is already running! Abort..."
  exit 0
fi

uqname () {
# subroutine to return list of unique non-local email addresss of the
# mail in a user's inbox.  Returned variable is UNAMES, which overwrites
# any previous definition.
  name=$1
  UNAMES=""
  LOC="/var/mail"
  UNAMES=`grep "^From:"' ' $LOC/$name | grep @ | awk '{print $2}' | sort | uniq`
}

mailcount () {
# subroutine to check frequency of occurrence of non-local email senders
# inputs: acount name, email_address_of_sender
  LOC="/var/mail"
  COUNT=0
  name=$1
  email=$2
  # COUNT=`grep "^From $email" $LOC/$name | wc -l`
  COUNT=`grep "^From: $email" $LOC/$name | wc -l`
}

# set initially so that multiple informational messages are not sent out
newinfo="no"

list=`/bin/cat /etc/passwd | awk -F: '{print $1}'`
for name in $list
do
# check size
  echo "$name"
  if [ -f /var/mail/$name ] ; then
    size=`ls -l /var/mail/$name | awk '{print $5}'`
    home=`grep "^$name:" /etc/passwd | awk -F: '{print $6}'`
    # debug --
    echo "home dir is: $home"
  else
    size=0
  fi

  if [ $size -gt $MAXSIZE ] ; then
# debug --
    echo "Too big -- size is $size"
    echo "" >>/tmp/LISTmonit$$
    echo "email for $name overflowed with $size bytes: " >>/tmp/LISTmonit$$
    # check box for multiple messages...
    uqname $name
    for addr in $UNAMES
    do
      mailcount $name $addr
    # check for exceptions
      test=`echo $EXCEP | grep -i $addr`
      if [ XYZ$test = "XYZ" ] ; then
      # process
        if [ $COUNT -gt $MAXNUM ] ; then
          # debug
          echo "$addr has sent $COUNT messages."
          echo "$addr has sent $COUNT messages." >> /tmp/LISTmonit$$
          # see if already denied -- note syntax of string MUST be
          # sendmail: this.organization.org: DENY
          # for this to work consistetly!
          sender=`echo $addr | awk -F@ '{print $1}'`
          hostname=`echo $addr | awk -F@ '{print $2}'`
          # eliminate possible trailing ">"  98-Apr-12
          hostname=`echo $hostname |awk -F\> '{print $1}'`
          echo "hostname is: $hostname"
          test=`grep "sendmail: $hostname:" $HAFILE | grep DENY | awk -F: '{print $2}'| awk
'{print $1}'`
          # debug --
          echo "search yields: $test"
          if [ XYZ$test = "XYZ" ] ; then
          # this is a new entry...
            newinfo="yes"
            da=`date`
            string="sendmail: $hostname: DENY # ($sender) $da"
            # edit
            /bin/ed $HAFILE <<EOF
0a
$string
.
w
q
EOF

          else
          # was already in the file
            echo "host entry $hostname already denied..."
          fi
        # no, not an excessive number of messages from that user
        fi
      else
      # address was an exception
        echo "The address $addr is an exception ($COUNT messages received)"
      fi
    done
  # looped through all excessively large mailboxes
  fi
# looped through all mail users
done

#  track and inform...
if [ $newinfo = "yes" ] ; then
  # inform
  LIST=`cat /tmp/LISTmonit$$`
  da=`date`
  /usr/ucb/mail -s "Mail OVERFLOWED" $MAILTO <<EOF
The incoming mailbox for the following account(s) exceeded the
limit of $MAXSIZE bytes on $da.
The following users have sent more than $MAXNUM messages...
$LIST

Entries have been made, denying sendmail access to the pertinent hosts
(the file $HAFILE should be reviewed for accuracy).  Note that these
denials will remain until these entries are manually deleted.

This is an automated response.
EOF

else
# no need to send out mail -- nothing has changed
  echo "No changes detected..."
fi

# clean up
rm -f /tmp/LISTmonit*
# or use  "rm -f /tmp/LISTmonit$$" if you wish
exit 0



Current thread: