[9895] in bugtraq
Re: SMTP server account probing
daemon@ATHENA.MIT.EDU (Tobias J. Kreidl)
Fri Mar 12 14:53:30 1999
Date: Wed, 10 Mar 1999 14:30:25 -0700
Reply-To: "Tobias J. Kreidl" <tjk@jan.ucc.nau.edu>
From: "Tobias J. Kreidl" <Tobias.Kreidl@NAU.EDU>
To: BUGTRAQ@NETSPACE.ORG
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