Email Header Injection Exploit

NYPHP - PHundamentals

A botnet is scanning the web to locate PHP scripts which are vulnerable to a email header injection exploit. All PHP scripts which send email based on input data, such as when a user supplies a "From" or a "To" address, may be vulnerable. In addition, the botnet attempts to inject headers into any other fields it finds on a form such as hidden fields.

Discussion

A distributed network of machines is currently being employed to scan PHP-based websites in search of scripts which might be vulnerable to an injection-style security exploit. The exploit, if successful, permits the botnet to send emails to arbitrary destinations. A common target of the botnet is the kind of web-based feedback form which submits an email to a user-provided designated address. The botnet script injects malicious email headers into the form's fields, which are then passed to the mail server. The mail server parses those headers and then, if the attack was successful, it sends an email to the address designated in the maliciously injected headers. We assume the attacker is collecting a list of vulnerable sites which may be used later as an open relay for spam or large scale deployment of viruses/worms.

As of this writing (September 2005), the source of this botnet has been been traced to a number of different remote hosts such as the following:

	medres.cmdik.pan.pl (resolves to a website based in Poland)
	67.110.225.236.ptr.us.xo.net (resolves to XO Communications in the U.S.)
	

The botnet attempts to insert an email with its own headers which, in turn, contains a BCC header. This BCC header has been found to contain one of the email addresses in the list below (current as of this writing on September 2005). No doubt the receipt of an email at any of these addresses must serve as an alert which lets the attacker know that a vulnerable script has been found.

  • jrubin3546@aol.com
  • mkoch321@aol.com
  • wnacyiplay@aol.com
  • kshmng@aol.com
  • homeiragtime@aol.com
  • bergkoch8@aol.com

A Google search for the injected email addresses reveals that scans have been taking place since at least July 8, 2005

Are My Scripts Vulnerable?

If you have a PHP script that relies on user input for generating an email, then your script is potentially vulnerable to this exploit.

You can determine if you have already been a victim of the email header injection exploit by examining your mail log or your access log for the email addresses or remote host addresses listed above. Important: The email and remote host lists are not exhaustive, and the botnet is likely to use additional BCC email addresses and will likely "originate" from many different remote hosts.

To determine if you have already been a victim of this particular botnet, you can grep through your mail server logs for the list of emails, using a command something like this:

grep -f exploitaddresses.lst /var/log/maillog

(or wherever your mail log is located)

If any are found, cross reference the time of the mailing to times in your web server logs to help determine the exploitable script. Modify any such scripts to properly filter input fields and any other fields that may be used by your script to generate an email.

If you have grepped your access log you will immediately know the name of the script that was accessed.

Important: The email and remote host addresses listed can be used as a starting point to determine if you have already been a victim of this particular exploit. However, keep in mind that the attacker can change them at any time and so searching for these in your logs is not a solution for preventing future attacks. Your scripts may STILL be vulnerable. Therefore you should review the code for any of your scripts that rely on user input for constructing an email. In addition, you should examine any other fields in your scripts that may NOT rely on user input but which are still used in the generation of an email. Note the two error log snippets below.

In Snippet No. 1, the botnet has tried to manipulate form variables, a hidden form variable ('_submit') and other variables such as the session ID.

Snippet 1:

array(53) {
  ["HOME"]=>
  string(1) "/"
  ["PATH"]=>
  string(29) "/sbin:/bin:/usr/sbin:/usr/bin"
  ["_submit"]=>
  string(29) "yrfockfsy@example.com"
  ["password"]=>
  string(29) "yrfockfsy@example.com"
  ["PHPSESSID"]=>
  string(445) "yrfockfsy@example.com
Content-Type: multipart/mixed; boundary=\"===============1678997057==\"
MIME-Version: 1.0
Subject: 8526bb87
To: yrfockfsy@example.com
bcc: Homeiragtime@aol.com
From: yrfockfsy@example.com

This is a multi-part message in MIME format.

--===============1678997057==
Content-Type: text/plain; charset=\"us-ascii\"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit

fvf
--===============1678997057==--
"

In Snippet No. 2, the botnet has again tried to manipulate form variables, a hidden form variable ('_submit') and other variables such as the session ID but in this case, the submitted values have been changed.

Snippet 2:


array(53) {
  ["HOME"]=>
  string(1) "/"
  ["PATH"]=>
  string(29) "/sbin:/bin:/usr/sbin:/usr/bin"
  ["_submit"]=>
  string(25) "rfljy@example.com"
  ["password"]=>
  string(438) "rfljy@example.com
Content-Type: multipart/mixed; boundary=\"===============1104808547==\"
MIME-Version: 1.0
Subject: da79e5ec
To: rfljy@example.com
bcc: Homeiragtime@aol.com
From: rfljy@example.com

This is a multi-part message in MIME format.

--===============1104808547==
Content-Type: text/plain; charset=\"us-ascii\"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit

twjgdcbd
--===============1104808547==--
"
  ["PHPSESSID"]=>
  string(25) "rfljy@example.com"

Solution

To prevent an email header injection attack, you need to filter all input data including any other fields on your forms (such as hidden fields) that may be used by your scripts in the creation of an email. The actual filtering implementation is a matter of programming style.

Filter Before Submit

You can prevent the attack by performing a data validation procedure that will not allow a script to run until the unwanted strings have been removed. For example, the following patterns can be used with PHP's preg_match function. If the function returns false, you can prevent the user from proceeding.

Pattern for filtering fields such as names
'/^[a-z0-9()\/\'":\*+|,.; \- !?&#$@]{2,75}$/i'

Pattern for filtering email addresses
'/^[^@\s]+@([-a-z0-9]+\.)+[a-z]{2,}$/i'

For example, using the pattern for email addresses, you might do the following:

	$emailPattern = '/^[^@\s]+@([-a-z0-9]+\.)+[a-z]{2,}$/i';
	if (!preg_match($emailPattern, $emailFieldToTest)){
		print 'Please review the email address you entered. There seems to be a problem';
	}

Filter After Submit

You can allow a user (or, in this case, the botnet) to submit the form but then clean the data prior to actually processing it. A function like the one below can be used for this purpose.

function safe( $name ) {
   return( str_ireplace(array( "\r", "\n", "%0a", "%0d", "Content-Type:", "bcc:","to:","cc:" ), "", $name ) );
}

/*************
 NOTE: str_ireplace is a PHP5 function. If you are using an
 earlier version of PHP, you can use preg_replace
 with the i modifier.
*************/

Another method for filtering after the submit might look like the following. Be sure to change $_POST to $_GET if you are using that method.

foreach( $_POST as $value ){
  if( stripos($value,'Content-Type:') !== FALSE ){
    mail('admin@somehwere.com','Spammer Bot Attempt',$_SERVER['REMOTE_ADDR']);
     exit("{$_SERVER['REMOTE_ADDR']} Has been Recorded");
  }
}

Why Does This Exploit Work? or Is PHP Not Secure?

This exploit has nothing to do with the behavior of PHP and, therefore, is not a shortcoming of PHP. The exploit "works" because of the MIME and SMTP standards.

A basic MIME message is broken into two parts; a header and a body. The header and body are separated by a blank line, i.e., \r\n\r\n (although most mail clients will accept UNIX and other line delimiters, like \n\n or even \n\r\n\r). MIME is also multi-part which means that messages can be embedded in other messages. This is done by having the top level header declare a boundary, which is then used to process further header/body pairs.

If someone is able to inject content into this top level header, they can now essentially control the structure of the entire message. This is done by creating their own boundary, thus defining what's a body and what's a header.

The other significant point of weakness that's taken advantage of is the fact that MIME (and subsequently SMTP) can deal with multiple headers of the same name. Meaning, if there is a To: followed by a To: they are interpreted "correctly" with no errors.

Putting it all together, this allows an attacker to define additional recipients for a message, not to mention adding Bcc: and Cc: headers using multiple headers of the same name. In addition the attacker can define body parts to be sent as the content of the message by defining a multi-part message. So even user input intended as content is vulnerable to the injection of a header instruction. That is why all input data must be filtered.

Conclusion

This article examined the specifics of a particular script exploit and is not meant to protect all PHP scripts from all exploits. As often happens, once one security hole has been closed, another one is found. The important lesson, as has already been stressed in other PHundamentals, is the need to know exactly how your script will behave and, most importantly, exactly what data you expect to receive from the script. This methodology is illustrated in the following excerpt from the PHundamental "Spoofed Form Submissions:"

A solid 'architecture' would assure that:

  1. You know what you are sending out
    Keep track of forms you've put on your site and develop a policy for accepting form submittals (for instance time outs, multiple forms per user id, multiple submissions, not accepting forms you don't expect, etc). This can be implemented using tokens.
  2. You know what you should be getting back
    This is crucial. Just because a <select> field contains certain values, don't think you can't get back something totally different such as PHP code, SQL, etc.
    1. know the fields you need to have back to accept the form as valid
    2. restrict exactly what values you'd accept as input
    3. always minimize taking data from forms (or any external source) and using it directly in database queries, or other inner and intimate parts of your application.
See PHundamental's article Spoofed Form Submissions

Additional Information

Email Injection

Botnet Defined - Wikipedia

"Pro PHP Security" by Chris Snyder & Michael Southwell

"Essential PHP Security" by Chris Shiflett

Notes

Document last modified on October 6, 2005. The regular expression was modified and a note was added concerning the str_ireplace function.


Contributors to this note include the following:

  • Rolan Yang
  • Chris Snyder
  • Billy Reisinger
  • Ken Robinson
  • Hans Zaunere
  • Chris Shiflett
  • Jordan Bradford
  • the PHundamentals team: Jeff Siegel and Mike Southwell