Posted on

Tracking down malicious PHP spam scripts

If you’ve ever had anything to do with running a website, you’ve probably been affected by some form of malicious activity.  Commonly this includes spamming your contact forms, injecting iframes to virus ridden websites in your templates, and, quite often, uploading php scripts in order to send out spam.  Hosting open source applications like wordpress and joomla make servers particularly susceptible to this kind of attack, and often you won’t know until you receive reports from your upstream provider that you’ve been black listed for spamming.

I’ve worked with a number of server management companies over the years, and when we’ve experienced these spam issues there’s never been a clear strategy for pinpointing the offending script.  With a bit of digging in the mail logs you can usually find the user that’s sending the spam, and ascertain if apache is the culprit, but finding the actual script responsible for the spamming is pretty much impossible. So when we had a notification from spamcop recently that our server had been sending spam, I resolved to find a solution myself.

After a bit of googling and trial and error, I pieced together a solution that will log every email sent from php, along with details of the calling script, and the remote user’s IP address. This solution will serve us well in pinpointing and eliminating these issues in the future.

Please note that this solution requires access to ssh and your php.ini file, so probably won’t work for reseller hosting accounts. The solution is as follows:

1) Create a custom sendmail bash script

By default, when php sends mail, it calls the standard /usr/sbin/sendmail command, and sendmail really doesn’t have any provisions for logging extra metadata about a message send.  So we want to create a new script, which does the logging for us, and then passes on the actual send back to sendmail.

So I created a script in /usr/local/bin/ called sendmail-php-logged, with the following content:

logger -p "sendmail-php site=${HTTP_HOST}, client=${REMOTE_ADDR}, script=${SCRIPT_NAME}, filename=${SCRIPT_FILENAME}, docroot=${DOCUMENT_ROOT}, pwd=${PWD}, uid=${UID}, user=$(whoami), args=$*"
/usr/sbin/sendmail $*

What this is doing is using the logger command, to log metadata regarding the calling script, via whatever the standard mail log is for your system. The mail log file path is defined in /etc/syslog.conf, but on our servers mail is logged to the standard /var/log/maillog. After logging, the script then passes the original sendmail arguments to the standard sendmail command to actually send the email.  Note that the sendmail command may be different on your installation, but you can easily locate the path by typing which sendmail.

2) Update PHP to use the new logged sendmail command

Once this script is set up, you need to tell php to use it.  What you need to do is update the sendmail_path line to the following:

sendmail_path = /usr/local/bin/sendmail-php-logged -t -i

Once you do this, and restart apache, you should see logs in your mail log whenever an email is sent via php.  You’ll need to send a few test emails, maybe submit a few web forms or write a simple test file that runs the mail function, then check your mail log by running cat /var/log/maillog | grep “sendmail-php”. You should see a number of lines of output.  But sadly the work doesn’t stop there, as by default PHP doesn’t set the environment variables you need to fetch the vital script information, so they’ll all show up blank. This brings us to step 3:

3) Force PHP to store $_SERVER variables as environment variables

Unfortunately, the way in which PHP calls sendmail, the bash script doesn’t have access to the same variables that PHP does, so we need to specifically tell PHP to store the variables in a way that the bash script can read.  To do this we use php’s putenv() function. putenv accepts a single string parameter, with both the variable and the value, as so:

putenv( 'SCRIPT_FILENAME=/var/www/html/test.php' );

We could, if we wanted, update our applications to set these environment variables just prior to calling php mail(), but in this instance the goal isn’t to track our own scripts (although this is still handy) but really to track other nasty scripts some awful individual has uploaded, which we have no control over.  So we need these environment variables to be loaded up BEFORE their php script even has a chance to run.  To do this we need to use php.ini’s auto_prepend_file feature.

First create a php file somewhere that php has access to.  I called mine put_environment_variables.php. The code for that file is as follows:

 * This passes all SERVER variables to environment variables, so they can be used by called bash scripts later
 foreach ( $_SERVER as $k=>$v ) putenv("$k=$v");

Finally, update php.ini to load this file on every request, as follows:

auto_prepend_file = /aro/put_environment_variables.php

And once that’s done, and apache is reloaded, you should see the script file name and ip address showing in your logs.

2 thoughts on “Tracking down malicious PHP spam scripts

  1. Thanks mate, it has helped to identify those nasty scripts. The only issue I’ve had was with the open_basedir php restriction so I had to add /tmp to all of the config files and run the script from in there.

    thanks again


  2. Thank you so much! My server started acting up and had no idea where to start to look for the culprit. You saved me lots of time!

Leave a Reply