Enhancing E-Mail Security With Procmail

Configuring the Sanitizer

Back to the home page
Traducción española por Juan Maria Gil <me at juanmaria.com> - documento original en el website del autor
Translation to Russian by Evgeni <saaw at mail.ru> - Original document on the author's website

The sanitizer is primarily intended to process messages being delivered on the mail server itself, where the mail server software is running on the same system as the user mailboxes. This is called "local delivery". In this case, procmail is probably already the default local delivery agent, and adding in the sanitizer is very simple.

Running the sanitizer on a mail server that passes messages on to another server for final delivery is called "mail relay". Mail being passed from many domains on the Internet to server(s) on your local network that support one or a few domains (e.g. from the public sendmail gateway to the corporate Microsoft Exchange server) is called "inbound relay". When mail originates on your local network and is being passed on to many other domains on the Internet it is called "outbound relay".

It is possible to sanitize both inbound and outbound relayed mail as well as mail that is being delivered locally. Please read the instructions for configuring filtering on a relay for both inbound and outbound relay filtering, as well as the instructions for outbound filtering for an outbound relay.

Please note that the outbound instructions call for two servers. Configuring sanitizing of inbound and outbound mail on a single server is still being tested. (If anyone has something like this already set up, please let me know!)


The operation of the sanitizer is controlled by environment variables and policy files. The environment variables must be set before the sanitizer script is run. This is typically done in /etc/procmailrc, the global procmail file.

All of the capabilities of Procmail are still available and can be used to extend and enhance the behavior of the sanitizer.

Here is a sample /etc/procmailrc file:

PATH="/usr/bin:$PATH:/usr/local/bin"
SHELL=/bin/sh

POISONED_EXECUTABLES=/etc/procmail/poisoned
STRIPPED_EXECUTABLES=/etc/procmail/stripped
SECURITY_NOTIFY="postmaster, security-dude"
SECURITY_NOTIFY_VERBOSE="virus-checker"
SECURITY_NOTIFY_SENDER=/etc/procmail/local-email-security-policy.txt
SECRET="CHANGE THIS"
SECURITY_POISON_WINEXE=YES
SECURITY_POISON_WMF=YES

# This file must already exist, with proper permissions (rw--w--w-):
SECURITY_QUARANTINE=/var/spool/mail/quarantine

# Alternatively, use per-user quarantines:
SECURITY_QUARANTINE=$HOME/quarantine

POISONED_SCORE=25
# This file must already exist, with proper permissions (rw--w--w-):
SCORE_HISTORY=/var/log/macro-scanner-scores

# Alternatively, use per-user score logs:
SCORE_HISTORY=$HOME/macro-scanner-scores

DROPPRIVS=YES
# This file must already exist, with proper permissions (rw--w--w-):
LOGFILE=/var/log/procmail.log

# Alternatively, use per-user log files:
LOGFILE=$HOME/procmail.log

# Finished setting up, now run the sanitizer...
INCLUDERC=/etc/procmail/html-trap.procmail

# Reset some things to avoid leaking info to
# the users...
POISONED_EXECUTABLES=
SECURITY_NOTIFY=
SECURITY_NOTIFY_VERBOSE=
SECURITY_NOTIFY_SENDER=
SECURITY_QUARANTINE=
SECRET=

This is a very basic configuration.

Of course, if you've already got an /etc/procmailrc file you'll have to incorporate the above configuration information and INCLUDERC=/etc/procmail/html-trap.procmail call into what's already there.


Sanitizer configuration options

The following environment variables are recognized by the sanitizer:


Important note: When the above instructions say "set variablename to some value", any value at all will enable the function that the variable controls. In particular, this means that setting the variable to "NO" will not disable the function. If you wish to disable the function, set the variable explicitly to nothing:

DEBUG_VERBOSE=""
or
DEBUG_VERBOSE=

You should also read the Procmail man pages to see what environment variables control the behavior of procmail itself. Some of those environment variables are also recognized by the sanitizer.


Implementing multiple security policies

The above procmailrc file applies the same security policy to all messages that pass through the system. This may not be what you want to do; for example, you may want to relax certain checks for messages within your domain, such as not mangling Word documents that your users mail to each other.

The way you implement this is by providing different settings of the configuration variables based on the message that is being processed. For example, if you wanted to suppress the mangling of Word documents within your domain, you would add the following somewhere before the INCLUDERC=html-trap.procmail that runs the sanitizer:

:0
* ^From:.*<[a-z0-9]+@mydomain.com>
* ^To:.*<[a-z0-9]+@mydomain.com>
{
    MANGLE_EXTENSIONS='html?|exe|com|cmd|bat|pif|sc[rt]|lnk|dll|ocx|dot|xl[wt]|p[po]t|rtf|vb[se]?|hta|p[lm]|sh[bs]|hlp|chm|eml|ws[cfh]|ad[ep]|jse?|md[abew]|ms[ip]|reg|asd|cil|pps|asx|wm[szdf]|vcf|nws|\{[-0-9a-f]+\}'
}

Please note that the "From:" header in email is trivially easy to forge. If you wish to implement a less-restrictive policy for internal email, you should base your "source" rule on your local-network IP address appearing in a Received: header. For example, if you use 10.20.30.nn for your local network, you might do something like:

:0
* ^Received:[^(]+\([^ ]+ +\[10\.20\.30\.[0-9]+\]
* ^To:.*<[a-z0-9]+@mydomain.com>
{
    MANGLE_EXTENSIONS='html?|exe|com|cmd|bat|pif|sc[rt]|lnk|dll|ocx|dot|xl[wt]|p[po]t|rtf|vb[se]?|hta|p[lm]|sh[bs]|hlp|chm|eml|ws[cfh]|ad[ep]|jse?|md[abew]|ms[ip]|reg|asd|cil|pps|asx|wm[szdf]|vcf|nws|\{[-0-9a-f]+\}'
    SECURITY_TRUST_HTML=Y
    etc...
}

The exact format of your local Received: header will vary depending on what your local MTA software is. Please look at the headers of real local messages to determine what you should be looking for.

If you wanted to provide a means for the administrator to bypass sanitization completely, for example, to make it easier to recover quarantined messages and deliver them to the intended recipient, you might replace the plain INCLUDERC=html-trap.procmail with:

:0
* ! ^From:.*<root@mydomain\.com>
* ! ^X-Security: bypass sanitizer 954kJF64ljf8o6r
{
    INCLUDERC=/etc/procmail/html-trap.procmail
}
Use a different string of gibberish for your "bypass sanitizer" header so that someone who reads this page does not know how to bypass your sanitizer. If you want persons from offsite to be able to bypass the sanitizer, omit the test on the From: header.


Implementing custom security filters

It is possible to use the quarantine and notification facilities provided by the sanitizer with your own site-specific custom security policy procmail rules. The sanitizer keys notification and quarantine responses off X-Content-Security headers that are inserted into the message. You can do this within your own procmail rules to cause the sanitizer to quarantine and/or notify.

An example local-rules script is available and is updated more frequently than the examples below. Please use it instead of what is below, which should only be used for illustration.

For example, one variant of a current worm sends itself out as a randomly-named attachment to a message with no subject at all. To create a custom rule for the sanitizer to detect and trap this attack without simply poisoning all .EXE attachments, you'd do this:

  1. Put the following into /etc/procmail/local-rules.procmail (owner root, group root, mode 644)
    # Detect Hybris when sent as an anonymous message.
    #
    :0
    * > 20000
    * < 40000
    * !^From:
    * !^Subject:
    * ^Content-Type:.*multipart/mixed;
    {
            :0 B hfi
            * ^Content-Disposition:.*\.EXE
            * ^Content-Type:.*\.EXE
            * ^TVqQAAMAAAAEAAAA
            * ^SiXLG3Lv\+wdKT1hwcrOTfD7rduGAY5LvseJ7
            * ^D4TKBAAAUFVQ/1QkSAEs
            | formail -A "X-Content-Security: [$HOST] NOTIFY" \
                      -A "X-Content-Security: [$HOST] QUARANTINE" \
                      -A "X-Content-Security: [$HOST] REPORT: Trapped anonymous Hybris worm" 
    }
    
  2. Change your /etc/procmailrc file from:
    INCLUDERC=/etc/procmail/html-trap.procmail
    
    to:
    INCLUDERC=/etc/procmail/local-rules.procmail
    INCLUDERC=/etc/procmail/html-trap.procmail
    
If the anonymous worm is detected, the X-Content-Security headers are inserted into the message using formail. The sanitizer is then called normally; it will sanitize the message, and even if nothing else is wrong with the message, it will be quarantined because of the X-Content-Security headers that the custom rule inserted.
Note: The above example is very specific, and as the Hybris worm can be modified by the author as it spreads, the example should probably not be relied upon in an actual installation.

Custom rule actions that can be specified through X-Content-Security headers:
KeywordAction
NOTIFY Send notification message to $NOTIFY list and (if valid) sender.
NONOTIFY Send notification message to sender if valid, but do not send notification to $NOTIFY list.
SPOOFED_SENDER Suppress sender notification because the sender address is known to be forged. Note that specifying NONOTIFY and SPOOFED_SENDER together will send nothing; instead, don't put in any notification option.
REPORT: xxxxx Add text to a REPORT line in the notification messages. Multiple REPORT lines are permitted.
QUARANTINE Poison message. Message will be quarantined if a quarantine is configured.
DISCARD Discard message.

You can put any number of custom site security rules into /etc/procmail/local-rules.procmail - for example, to catch all anonymous .EXE attachments, add:

# Messages with .EXE attachments must have a subject line
#
:0
* > 20000
* !^Subject:
* ^Content-Type:.*multipart/mixed;
{
        :0 B hfi
        * ^Content-Disposition:.*\.EXE
        * ^Content-Type:.*\.EXE
        | formail -A "X-Content-Security: [$HOST] NOTIFY" \
                  -A "X-Content-Security: [$HOST] QUARANTINE" \
                  -A "X-Content-Security: [$HOST] REPORT: Trapped anonymous .EXE" 
}
This is much safer than trying to look for a specific executable, as in the Anonymous Hybris example.

To catch the SirCam worm based on a code snippet you could add:

# Trap SirCam (signature as of 08/01/2001)
#
:0
* > 130000
* ^Content-Type:.*multipart/mixed;
{
        :0 B hfi
        * ^Content-Disposition: attachment;
        * ^Content-Transfer-Encoding: base64
	* AAAAGgU0NhbTMyABCDTUlN|AAAAAaBTQ2FtMzIAEINNSU1F|ABkAAAABoFNDYW0zMgAQg01J
        | formail -A "X-Content-Security: [$HOST] NOTIFY" \
                  -A "X-Content-Security: [$HOST] QUARANTINE" \
                  -A "X-Content-Security: [$HOST] REPORT: Trapped SirCam worm - see http://www.symantec.com/avcenter/venc/data/w32.sircam.worm@mm.html"
}

Here is a detector for BadTrans:

# Trap BadTrans (signature as of 12/03/2001)
#
:0
* > 40000
* < 50000
* ^Subject: Re:
* 1^1 ^Content-Type:.*multipart/.*boundary="====_ABC1234567890DEF_===="
* 1^1 ^Content-Type:.*multipart/.*multipart/
{
        :0 B hfi
	* ^Content-Type: audio/x-wav;
        * ^Content-ID: 
        * ^Content-Transfer-Encoding: base64
        | formail -A "X-Content-Security: [$HOST] NOTIFY" \
                  -A "X-Content-Security: [$HOST] QUARANTINE" \
                  -A "X-Content-Security: [$HOST] REPORT: Trapped BadTrans worm - see http://securityresponse.symantec.com/avcenter/venc/data/w32.badtrans.b@mm.html"
}

Here is a detector for some variants of Klez:

# Trap Klez (signature as of 04/26/2002)
#
:0
* > 100000
* ^Content-Type:.*multipart/alternative;
{
        :0 B hfi
        * \<i?frame +src=(3D)?cid:.* height=(3D)?[0-9] +width=(3D)?[0-9]>
        * ^Content-Type:.*audio/
        * ^Content-ID:.*<
        * ^Content-Transfer-Encoding: base64
        * ^TVqQAAMAAAAEAAAA
        | formail -A "X-Content-Security: [$HOST] NOTIFY" \
                  -A "X-Content-Security: [$HOST] QUARANTINE" \
                  -A "X-Content-Security: [$HOST] REPORT: Trapped possible Klez worm - see http://securityresponse.symantec.com/avcenter/venc/data/w32.klez.removal.tool.html"
}

If you don't wish to keep quarantine copies of messages trapped in this manner, change the action from QUARANTINE to DISCARD. If you don't want to be notified, but you still want the sender of the message to be notified, change the handling from NOTIFY to NONOTIFY. For example:

        | formail -A "X-Content-Security: [$HOST] NONOTIFY" \
                  -A "X-Content-Security: [$HOST] DISCARD" \
This will keep your quarantine and postmaster mailboxes from filling up with hundreds of worm warnings from a known attack. (You must be using version 1.132 or higher to do this.)

If you don't want to quarantine, don't want to be notified, and don't want to notify the sender, then simply discard the message by sending it to /dev/null:

        :0 B
        * whatever signature rules here
        /dev/null
This will discard the message. You will not be notified, a copy will not be kept.


I can be contacted at <jhardin@impsec.org> - you could also visit my home page.

I'd be very interested in hearing from people who'd be willing to translate this page.


Created with vi   Best viewed with Any Browser

$Id: sanitizer-configuration.html,v 1.70 2006-09-10 13:32:21-07 jhardin Exp jhardin $
Contents Copyright (C) 2006 by John D. Hardin - All Rights Reserved.