Earlier this week I became aware that one of my domains was blacklisted on SpamCop. Ouch. My first thought was that it was no big deal. I’ve often been blacklisted by Yahoo and others simple because I send out opt-in emails (very tame stuff like order confirmations). Its a pain but you typically just have to prove that you aren’t a spammer and you’ve just tripped a sensitive filter.
Unfortunately after much digging through logs I realized this wasn’t the case. I was the victim of a rapidly spreading exploit known as email injection that took advantage of my super secure, locked down tight as a drum code.
Heres how email injection works:
A would be spammer (the email variety, not a search engine spammer) googles for an email contact form. If they find a ‘contact us’ page that is vulnerable they manipulate the form fields to add/change email headers. They accomplish in much the same way a hacker would perform SQL injection or website search results injection. By entering hexadecimal characters in the form field they are able to add carriage returns and spaces. So the following string entered in a form field such as “Your Email”:
“sender@somesite.www%0ACc:victim@victimsdomain.xxx%0ABcc:victim2@victimsdomain.xxx,victim3@victimsdomain.xxx”
will result in a carbon copy of the email being sent to victim@victimsdomain.xxs and a blind carbon copy being sent to victim2@victimsdomain.xxx and victim3@victimsdomain.xxx
As you can see it is easy to manipulate the headers and as a result you can get really fancy and change the subject of the email, mime-type, sender, and the body of the message. The end result is an email open relay. I grabbed a couple of IP’s that were used to POST the data and sure enough they were listed in my database of current open proxies. SecurePHP has a full rundown of the examples.
To secure your email contact form, check each form field against this function. If any one of them fails you can report an error or just silently bail on sending the email. I don’t believe that its necessary to run this check on the body field of the message as this doesn’t have any effect on the headers.
function containsInjectionAttempt($input) {
if (eregi(“\r”, $input) ||
eregi(“\n”, $input) ||
eregi(“%0a”, $input) ||
eregi(“%0d”, $input) ||
eregi(“Content-Type:”, $input) ||
eregi(“bcc:”, $input) ||
eregi(“to:”, $input) ||
eregi(“cc:”, $input)) {
return true;
} else {
return false;
}
}
The problem even exists for the popular CMS Drupal. I can’t tell from this bug report if its been fixed yet but it appears to still be an open issue so you may want to run a test on your own site if you are running Drupal.