Intercepting php mail() spam with sendmail and formail

I have noticed alot of people talking about mail header inections this past week. I have actually had a run in with this on one of my servers this last week too. On one of our servers where we host around 50 clients I have recieved in the neighboorhood of 7000 delivery failure notices to the server’s postmaster account. Almost all of them being failed spam messages. I was told that sendmail client on the server was for sure configured such that it couldn’t be used directly via smtp as an open relay, which means the e-mails were most likely coming from a compromised mail form script. So I spend an entire day tracking all of them down and patching them. I was pretty sure I got them all, but then the next day I got a bunch more failures which dated the original e-mail about 26 hours after the holes were all patched.

So, I was trying to think of a way to be able to monitor the php mail() so I can have it flag me when it detects possible spam being sent with it. Then I was reminded of an article fom the November 2005 php|architect by Ben Ramsey title mail() hacks. The article basically looks at various ways to intercept the mail() function for site testing purposes. So I am now using a variant of the same technique to catch spam being sent through php.

Php allows you to specify the path of your sendmail program via the sendmail_path ini option. Whatever program this points to is then passed a formatted e-mail message (with headers) via its STDIN whenever the php mail() program is called. So simply put you can swap out any program/script for sendmail to allow you to log e-mails, redirect it, or even modify it’s headers.

A while ago I wrote a script that I use with my servers’ postmaster accounts to notify me whenever there are any serious problems. This lets me know when there are any abnormalities (a large number of delivery failures, missing accounts, server errors, reports etc.) without me ever actually having to sift through each an every e-mail. So I figured it would be fairly trivial to just add some logic in there to sift through mail submitted via the mail() functions. The only problem was to find a way to intercept the messages.

Unix has a handy little program called formail (yes with one ‘m’) that makes it extremely easy to edit an email’s headers via the command line. So using this program I am now able to add a Bcc line with my address to any function sent with mail. In order to make it easy for my script to determine which e-mails come from php I am also adding a custom header: "x-php-formmail: yes." Also since I amp having a severe problem with injections I have taken the extra, temporary step of removing all other Bcc Addresses but my own and moving them to a different custom header.

The script looks like this:

Eventually, once I am sure that I have all the holes patched I will remove the ‘-R bcc x-original-bcc’ and at that point usage of this script will be completely transparent from a programming perspective.

Curse the spammers.

For those of you who would like more information about mail header injections I found this site. It has some pretty decent information: What they are, how they happen, how you fix them, it’s all covered.

Chris Shiflett also did a podcast with Marcus Whitney recently about mail header injections.

Then of course there is always the good ‘ole php manual. The comments have quite a bit to say about mail header injections. However, I recommend caution when reading through them, as always there of some good recommendations and some very, very bad recommendations.

3 thoughts on “Intercepting php mail() spam with sendmail and formail”

  1. Hi nice text but I miss the full code you added in php.ini. Would be more usefull to put all configuration or nada.


  2. An example could be:

    cat /usr/local/bin/trapmail

    /usr/sbin/formail -R bcc x-original-bcc -f
    -A “Bcc:
    -A “x-php-formmail: yes”
    | /usr/sbin/sendmail -t -i

Leave a Reply

Your email address will not be published. Required fields are marked *