Attacking PHP’s mail() function in Magento

For those who host Magento and have to apply security patches released on an ad-hoc basis such things can be disruptive and some what mystifying. There you are steadily developing and improving your site as planned when with out warning you must drop everything and patch the live code before some unspecified bad thing happens.

A quick check with magereport.com reveals many sites are slow to apply patches. I believe by looking at potential exploits and trying them out we can better understand the intention of the patches and the risks that an un-patched site exposes.

Magento patch SUPEE-9652 was a very small change. Only a single source file was altered with only a few lines of code changed.

The patch was to prevent a specific attack on PHP’s mail() function which in some circumstances allowed an attacker to execute PHP code on a site whilst only authenticated as a customer.

Forgotten Feature

In Magento 1.X, one area where a user can send an email is from the “Send a Product to a Friend” functionality. The use of this feature is configurable but turned on by default. Often custom designs will remove the link from the product page itself in favour of social media buttons but you can still navigate to the url.

Screen Shot 2017-06-13 at 14.48.41
Email a product recommendation to a friend form

“Send a Product to a Friend” is meant to allow customers to send an email to a friend about a product they think they may be interested in. To prevent abuse it is normally only for logged in customers and the admin can set limits on the number of emails sent per hour etc.

By default Magento uses the supplied email address to set the From: header on the sent email only. However, within the Magento admin backend is a setting labelled  ‘Set Return-Path’ this does indeed set the return path header in email. However it also sets the sender envelope address which is sent to PHP’s mail() function via a -f flag passed directly to sendmail.

Screen Shot 2017-06-13 at 14.51.51
The “Set Return-Path” option which should always be set to No

Code from app/code/core/Mage/Core/Model/Email/Template.php

if ($returnPathEmail !== null) {
 $mailTransport = new Zend_Mail_Transport_Sendmail("-f".$returnPathEmail);
 Zend_Mail::setDefaultTransport($mailTransport);
 }

Late in 2016 Dawid Golunski disclosed vulnerabilities in several wrapper scripts for PHP’s mail function which used the from address to set the sender address on sendmail. The problem comes that several valid forms of email address can be used to escape the sendmail command and set additional flags on the sendmail operation. Sendmail arguments are separated by spaces ” “.

Both the from email addresses and that of the recipient are validated as being of a valid format.
However, the local part of an email address in particular can contain more than is normally expected and still be valid according to the specification. There are several valid email address formats that are surprising such as

"dave"@example.com
!dave!xyz%abc@example.com

This quoted local name format crucially can contain spaces which are the way sendmail arguments are generally separated.

This means it is possible for an attacker to add extra sendmail parameters which then get sent to the command line sendmail and perform actions as well as attempting to send email. One flag that can cause extra behaviour is the -X log flag which writes out to a log file at a path specified as an argument. It is worth noting that this flag will only work if the target server is running sendmail and not postfix’s sendmail compatibility interface or any other Message Transfer Agent (MTA). Postfix will, for instance, accept the -X log flag to maintain compatibility but ignore it.

My Old Friend PHP

The -X flag allows the caller to specify the location and name of the log file. If it is named with a PHP extension and placed somewhere in the web root then the attacker can navigate to the log file and any PHP will be executed.

e.g. "dave\" -Xmedia/log.php  some"@test.com

Therefore in order to get our code running on the remote server we need some way of putting it within the email that would be sent. One thing that makes this attack, marginally, more difficult (but more fun) is that the users message is escaped before being included in the email and hence log file. This turns any “<” characters, which are necessary for PHP code tags, into their html equivalents &lt;  This means it is not possible to include executable PHP code as part of the message body itself. However, the sendmail log file format helpfully quotes the email address in “<” and also removes the double quotes.

Starting an email address “?php will then create a valid PHP tag when shown in the log file.

For example an email address of “?php echo ‘hello’; “@example.com  will get converted into <?php echo ‘hello’; @example.com>

A PHP closing tag cannot be created by the same method however. The email address validation will not accept a domain ending in a “?” character and so the logfile quoting there is no way to form a closing PHP tag “?>” tag for us.

The last piece of the puzzle is the __halt_compiler() PHP function. This means that PHP code parsing will end and the rest of the log file does not need to be valid PHP code. This is important since in PHP complete parsing of syntax happens before any code is executed.

So to get this working an attacker uses a sender email with the -X flag and path.

e.g.

"d\" -oQ/tmp/ -Xmedia/log.php some"@test.com

And then includes there PHP code in the recipient email address.

"?php readfile('/etc/passwd'); __halt_compiler(); "@test.com

In this case the action is just to read the contents of /etc/passwd however a malicious attacker is more likely to use it to upload a web shell.

The Patch

The fix applied by Magento as patch  SUPEE-9652 was to add email validation to the parameters before calling PHP’s mail() function this prevents email address of the form “dave”@example.com being used since the -f flag is already prefixed and -f”dave”@example.com fails validation. This effectively means Magento can not send an emails to addresses of the quoted format. However, these were normally prevented from being used by client side javaScript so it is unlikely they would be legitimate. The original attack relied on the server sending email via the relatively uncommon sendmail program. By preventing the use of the quoted format and email address containing spaces this has also prevented further attacks which have been published by sending extra parameters to exim.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s