Wednesday, April 19, 2017

Bug Bounty - Remote Code Execution in Magento 1.x

Magento is a popular ecommerce solution written in PHP. It is widely used for web shops both large and small. The most current product is Magento 2 however, Magento 1.x is still supported and widely used since the upgrade path for a heavily customised sites is largely unclear.

Both version 2 and version 1 of magento make use of the Zend framework for some functionality including the sending of email. Recently issues were found in multiple PHP frameworks which wrap PHPs native mail function.

The attacks on mail() rely on an attacker being able to set the from address on an email which then gets passed to sendmail as the envelope sender of -f argument. In magento, one area where a user can set the address from which an email originates is the Send a Product to a Friend functionality.


This is meant to allow logged in 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. This functionality is on by default but often custom designs will remove the link from the product page itself in favour of social media buttons.

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 but it also sets the sender via a -f flag passed directly to sendmail.

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);
        }

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. One such flag is the -X log flag which writes out to a log file at a path specified as an argument. It is worth noting that the flag used in the attack will only work if the target server is running sendmail and not postfix's sendmail compatibility interface, which is more common. This will accept the -X log flag to maintain compatbility but just ignores it.

 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 spec. This is because of the general rule that only the final email server should parse the local part of an email address. There are several valid email address formats that are surprising such as

"dave"@example.com

This allows PHP code from the user input to be included in the email and hence the log file. If the log file is named with a PHP extension and placed somewhere in the web root then the attacker can navigate to the log file and the PHP code will be executed.

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

One thing that makes this attack, marginally, more difficult but much 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. But the sendmail log file format helpfully quotes the email address in "<" and also removes the double quotes, so starting an email address ?php will 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 will not form a ?> 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 complete parsing of syntax happens before any code is executed.

Here is a short python script to execute the entire attack.


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.

Magento very generously paid $3000 for this bug. While possibly not the most creative attack the high value reward reflects the seriousness of RCE and the value Magento place on customers security.

Timeline:

DateActivity
2017-01-09 Issue Reported via Bugcrowd
2017-01-13 Issue triaged at Bugcrowd and sent to Magento
2017-01-13 Magento Issues security advice to disable Set Return-Path
2017-02-7 Magento Issues security patch supee-9652 https://magento.com/security/patches/supee-9652
2017-02-13 Magento marks issue as resolved pays bug bounty $3000