What we know is that email is sent using one of the oldest protocols on the internet: RFC 821 first defined the initial version of the Simple Mail Transfer Protocol (SMTP) in 1982. Since that time, there have been many improvements to the protocol but the basic format remains largely unchanged. SMTP is a text-based protocol where individual commands and their arguments are separated using a set of control characters (newlines, spaces, dot symbol etc.).
The basic idea of SMTP Command Injection is to insert these control characters into parameters of a web application that are later used in an email. By doing so an attacker can break out of the context of one SMTP command and insert new commands to modify different aspects of the email that is sent in the background.
Why should I care?
Depending on the attacked functionality of the web application, the consequences can range from reputational damage over compromised user accounts to compromised systems. An attacker could use the injected SMTP commands to modify the content of a 'contact us' or self-registration email, turning it into a vehicle for spam or phishing. When attacking a password reset, an attacker could insert a second email recipient to receive a copy of the password reset email and gain access to user accounts. In rare cases where operating system tools such as Unix mail command are used to send email, this command injection technique can also be used to execute operating system commands.
Our example starts with an imaginary web shop. It provides new users with an innocuous looking self-registration function. The user enters their first name, last name and email address. A few seconds later, they receive a friendly welcome email to the address they provided. Below you can see the HTTP POST request used to register a user.
POST /register.jsp HTTP/1.1 Host: cannedunicorn.contextis.com Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Content-Length: 4415 Connection: close Pragma: no-cache Cache-Control: no-cache title=Mr&firstname=Tilman&lastname= Bender& email@example.com&messageSubject=Thanks+for+registering&messageFrom=registration%40cannedunicorn.contextis.com& password=secret& password-confirm=secret®ister=Continue
After sending the above request, we receive the following email:
Looking at the email with a text editor reveals the following email including its mail headers
Received: from […SNIP…] Date: Thu, 3 Mar 2016 13:46:50 +0000 (GMT) From: firstname.lastname@example.org To: email@example.com Subject: Thanks for registering Return-Path: firstname.lastname@example.org Sender: email@example.com MIME-Version: 1.0 Content-Type: text/plain; Dear Mr Bender, Welcome to Context's canned unicorn meat delivery service. kind regards, your friends at Context
At a very high level, the message consists of two parts: a set of message headers (everything up to Content-Type) and the message body (marked above in grey) separated from the headers by a blank line. Upon closer inspection, we find that the values of some parameters in the above HTTP-request show up again in the email as header-values: messageSubject, messageFrom.
These would be potential candidates for a SMTP Command Injection tests. While those two parameters are rather obvious candidates for injection, the format for email addresses defined in RFC 2822 hints at two more candidates: The format also allows email addresses of the following form:
Tilman Bender <firstname.lastname@example.org>
So we can consider the firstname and lastname parameters as candidates for injection as well.
Testing for SMTP Command Injection
The attacker starts with the messageSubject parameter, which is reflected in the Subject SMTP header of the welcome email.
To determine if the parameter is vulnerable the attacker appends two additional characters to the value of messageSubject: carriage return (CR) and line feed (LF) followed by the text X-Foo: bar. The two control characters will indicate to the processing SMTP server that the Subject: command is finished. A new header named X-FOO with the value bar then follows the CRLF. Using the X- Prefix is still a common method to define custom protocol headers and is also used in protocols other protocols such as HTTP and FTP but has found wide use with software processing emails add additional information such as the results anti-virus or anti-spam checks. For this reason, most mail servers leave these headers alone. If the victims email gateway were configured restrictively stripping out custom headers, the attacker could use one of the standard headers such as blind carbon copy (BCC:) instead of X-Foo to have a copy of the email forwarded to an address under their control.
The following snippet shows the modified messageSubject parameter with its new value. The characters highlighted in yellow introduce the extra carriage return (represented by the hexadecimal ASCII code 0x0d) and line feed (hex ASCII: 0x0a), whereas the characters highlighted in blue introduce the new header and its value. The control characters such as CR, LF, colon and space have been encoded using URL encoding.
After submitting the modified HTTP request, the attacker receives the following email:
Received: from ... Date: Thu, 3 Mar 2016 13:50:50 +0000 (GMT) From: email@example.com To: firstname.lastname@example.org Subject: Thanks for registering X-FOO: bar Return-Path: email@example.com Sender: registration%40cannedunicorn.contextis.com MIME-Version: 1.0 Content-Type: text/plain; Dear Mr Bender, Welcome to Context's canned unicorn meat delivery service. kind regards, your friends at Context
The important part to understand here is that the attacker can modify whatever comes after the Subject header. Conveniently, the attacker also finds they can control what appears in the From: header of the email via the messageFrom parameter. With that, the attacker has everything they need to mount a very convincing phishing attack.
The Most Convincing Phish
The attacker wants to use the registration function to send phishing emails enticing users to submit their credentials to an attacker-controlled website. Forging the sender’s address is easy, as the attacker just needs to modify the messageFrom parameter.
What is left for the attacker to do is use SMTP commands that will tell the SMTP server to treat the attacker’s email content as the primary content, and treat the original content in a way so it is not displayed to the end-user. One way to achieve this is by inserting a new Content-Type header to change the type to multipart/mixed.
This will tell an email client that the message consists of two parts, where the attacker’s injected content is the first part and will be displayed to the user. The original content will be treated as an attachment and not displayed. To achieve this, the attacker sets the messageSubject as follows:
Submitting the registration form with the modified messageSubject parameter will result in the following email:
Received: from […SNIP…] Date: Fri, 4 Mar 2016 13:43:48 +0000 (GMT) From: firstname.lastname@example.org To: email@example.com Subject: Change your password MIME-Version: 1.0 Content-Type: multipart/mixed;boundary=ContextContextContext Return-Path: firstname.lastname@example.org Sender: email@example.com --ContextContextContext Content-Type: text/html Content-Transfer-Encoding: base64 RGVhciBVc2VyLDxicj5QbGVhc2UgY2xpY2sgPGEgaHJlZj0iaHR0cDovL3d3dy5jb250ZXh0LmNvbSI+aGVyZTwvYT4gdG8gY2hhbmdlIHlvdXIgcGFzc3dvcmQu --ContextContextContext-- MIME-Version: 1.0 Content-Type: text/plain; Dear Mr Bender, Welcome to Context's canned unicorn meat delivery service. kind regards, your friends at Context
The coloured text in the message above is the result of our SMTP Command Injection. The original text of the email has been appended after the ending boundary, but since the attacker chose the content type to be multipart/mixed this content will be ignored. The content of the message has been base64-encoded to prevent characters inside the content from interfering with the parsing of the email. When decoded, the message reads as follows:
Dear User, Please click <a href="http://www.context.com">here</a> to change your password.
The result looks as shown below:
So here we are: The attacker has just coerced the web application into sending a phishing email to an unwitting user.
How do I protect my web application?
Here are some steps to prevent this from happening to your website:
- Do not provide the attacker with more input fields than necessary. Fields like the subject of an email or the sender's email address should be filled by the server processing the user's registration request and not be unnecessarily exposed to the user.
- Do not rely blindly on APIs provided by frameworks. As users of JavaMail found out, the framework authors might consider input validation your responsibility.
- Do validate all input fields using a white-list approach. That is: define the allowed content for input fields as strictly as possible and reject anything that does not match your definition of valid input.
- Input fields, that make it into the To or Subject email headers of an email must not be allowed to contain the carriage return or newline characters.
Summary and Conclusions
This blog post showed only a simple scenario of what could happen, if user input is included into email messages without prior validation. Phishing and distribution of unsolicited email in your name is only one of the possible consequences. Fortunately, SMTP Header injections are a rare find these days, since most of the libraries do validate trivial injection vectors such as the recipient. However, as a developer you should always make sure that any field included into an email has been properly validated.