Fighting Dictionary and DoS Attacks with sendmail

(Based on a post to vps-mail on 28 Feb 2005)

1. Limit the number of recipients per incoming envelope

In a dictionary attack, the attacker will often send a single message envelope to multiple recipients, so that the SMTP conversation (from the attacker's perspective) looks something like:

EHLO [domain.name]
MAIL FROM:<spammer.domain>
RCPT TO:<a@yourdomain>
RCPT TO:<b@your.domain>
 ...
RCPT TO:<zzzzz@yourdomain>
DATA
...

Last time I checked, AOL limited the number of recipients per envelope to 10. You can do the same by adding the following to your mc file:

define(`confMAX_RCPTS_PER_MESSAGE',`10')dnl

After it sees 10 recipients, sendmail will issue a temporary failure response 452, beginning with recipient 11 and continuing forever (unless the spammer notices the failure).

Legitimate mail servers encountering the 452 Too many recipients response will queue the mail, and try recipients 11-20 (etc.) later. Clueless spammers (including trojaned Windows boxes) will just give up.

2. Throttle sendmail if too many bad recipients

Another technique is to have your server slow down the conversation with the attacker. The following mc directive will impose a 1-second delay after your server returnes two consecutive "user unknown" responses (to multiple recipients in the same envelope):

define(`confBAD_RCPT_THROTTLE',`2')dnl

For the rest of that envelope, *every* response to the remaining RCPT commands will result in a 1 second delay. (The number 2 is arbitrary; I have used it in my configuration in combination with max recipients per message of 10, with good success.)

3. Limit the rate of connections (sendmail 8.13+, not VPS1)

To limit the number of connections per minute (the "per minute" is configurable, but I won't mention it here) to a specified number:

In your mc file, after defining the access db feature, add this line:

FEATURE(`ratecontrol', ,`terminate')dnl

In your access db, add lines that read:

ClientRate:206.81.85.4             2
ClientRate:67.92.16                2
ClientRate:61.191.231              2

(The number 2 is arbitrary--it limits the number of connections from those networks to two per minute. The number 2 might be too restrictive, since each of those IP address fragments matches 254 addresses. Maybe 10 would be better?)

You can "wildcard" a ClientRate to (say) 10 connections per minute for all other addresses not matched by other ClientRate-tagged lines, with a line like:

ClientRate: 10

(Caution: This affects legitimate e-mail clients who relay mail through your server using SMTP AUTH ... so don't set the wildcard too low. If you know their IP addresses, you can specifically remove any limits with something like:

ClientRate:customerdomain.com       0

(A zero means no limits).

One other thing: if your mc file uses FEATURE(delay_checks), you should specify the ratecontrol FEATURE like so:

FEATURE(`ratecontrol', `nodelay',`terminate')dnl

(Scan through your mc file; if you don't see delay_checks, then don't use the second argument nodelay.)

(BTW: note the the quotes in the mc file are of two types: opening
back-quote, and closing apostrophe!!)

4. Limit simultaneous connections (sendmail 8.13+, not VPS1)

To limit the number of simultaneous connections any server (single IP address, host, or network) can have to your mail server at any one time, add

FEATURE(`conncontrol', , `terminate')dnl

after the FEATURE for the access db.

Then, in access db:

ClientConn:206.81.85.4         2
ClientConn:67.92.16            2
ClientConn:61.191.231          2

(The 2 is arbitrary, and maybe too low.) Like ClientRate, you can have a wildcard:

ClientConn:                    5

or allow unlimited connections:

ClientConnection:127.0.0.1     0

Also, if you use the delay_check feature, you should specify the connection control feature as:

FEATURE(`conncontrol', `nodelay', `terminate')dnl

{BTW, both the conncontrol and ratecontrol will return temporary 421 failures and drop the connection. [That is what the `terminate' argument does--drops the connection]. Because it is temporary (4xx), legitimate mail servers will queue the mail and retry later. Also, 421 is a "special" kind of temporary failure--I think it is the only 4xx that actually drops the connection immediately.)

5. Reject "slammers" (sendmail 8.13+, not VPS1)

(This one is rather "daring," so do it at your own risk!)

Incoming mail servers that connect to your server are supposed to wait for your server to give its initial greeting before saying HELO/EHLO and proceeding with the SMTP conversation. Some spammers connect to your box and immediately say HELO/EHLO without waiting. You can reject these with a line like the following in your mc file:

FEATURE(`greet_pause',`700')dnl

The above (which I use on my own server) causes sendmail to wait 700 milliseconds (0.7 seconds) before returning its greeting. If, during that 0.7 seconds, the incoming mail server sends an SMTP EHLO or HELO, your server will permanently reject all further SMTP commands (with a 554 code) from that connection. (Do you see why this might be controversial? Rumor has it that there are a few "legitimate" domains that don't wait for your server's greeting to issue HELO/EHLO ...)

If you know of hosts/domains/ip addresses/networks that are "slammers" but you want to let them e-mail to your server, you can add entries like the following to access db:

GreetPause:okslammer.com                 0
GreetPause:127.0.0.1                     0

Last Words

Use the above at your own risk, and definitely monitor /var/log/maillog to make sure they don't break your server!) After modifying your mc file, remember (on non-VPS1 FreeBSD, at least):

cd /etc/mail; make; make install; make restart

After modifying /etc/mail/access, remember:

cd /etc/mail; make

(Restarting sendmail isn't needed when you regenerate a db ...)

Good luck!