I feel like I should write something here about recent mail issues. Due to an unforeseen injection problem on the contact page Eric and I had written for my father’s plant nursery, a large number of bots from zombie computers were injecting extra email headers. The net effect of this was a huge spike in mail traffic, up to, I believe, somewhere around >8k msgs/min at one point in the past week.
I have fixed the problem, but I’d like to bring what caused it to light so anyone implementing similar functionality on a site hosted here can handle it appropriately. The first problem is php’s mail() function. Here is how I have it used, directly from contact.php:
mail("$to", "$subject", "$message", "From: $name<$email>“);
The problem here is that this function does no sanity checks on these variables at all. It’s a simple wrapper to a system call for emailing a message. As such, if invalid data is entered into one of these variables, it will get passed directly to the OS. In our situation, the attacker was entering tons of extra headers into the $name field on the page. Most of these headers were bcc:. When passed to the OS, these extra headers get interpreted instead of the simple From: header I have here.
A mediocre solution to this is to limit field size. I’ve implemented some field length restrictions, but a better solution is to include a Captcha into the code. I’ve implemented a simple one that is visible to everyone in rabbitridgenursery’s web directory anyone on the server can view. However, after including a captcha image on the submit form, my father was still receiving spam from it! This brings us to problem number 2:
The way the captcha works is:
1. A user visits contact.php
2. contact.php makes a request to php_captcha.php for an image.
3. php_captcha.php creates the image and sets a session variable containing the actual value embedded in the captcha image.
4. The user POSTs the form, whereupon the entered code is compared against the one in the session variable.
5. If the two variables are equal, the email gets sent.
This intended operation was obviously not working though, as the spam was still coming. A closer inspection gave the reason: The bot was not making any GET requests to contact.php, and thus no requests to php_captcha.php. It was simply POSTing to the page, without the captcha variable set. So without the request to the captcha page, the session variable never gets set, and the final comparison for validation is done between two empty variables. Obviously this was making a successful comparison, and the mail was still getting sent.
The final solution includes field length limits (why would you ever need > 100 characters for your email or name?) and a check for the captcha session variable being empty. This appears to have captured all the spam attempts, which I’ve been logging since I edited the code. This log can be seen by anyone at http://mytor.net/log. As you can see, the ‘attack’ has still been going on for quite a while. I’m going to set up a script later today to parse this file and add appropriate (ie, non-’mistake’ failed captchas) IPs to our global deny list.
In all actuality most users probably never noticed the issue except my father. If anyone wants any more details, email me at loren@mytor.net.