About the CVE 2016 10033 and CVE 2016 10045 vulnerabilities

giantsloar edited this page Jan 3, 2017 · 13 revisions

On December 16th 2016, a remote code execution vulnerability in PHPMailer was reported by Dawid Golunski, and described in an advisory. This vulnerability can occur in the following circumstances:

  1. The script is using PHPMailer's default isMail() transport, i.e. using PHP's built-in mail() function.
  2. The 'From' address is set from user input.
  3. The Sender property is not set explicitly.
  4. The server is not running the postfix mail server

In the absence of a preset Sender property, it is copied from the from address. This in turn is built into a string, prefixed with -f to designate a sender address, and passed into the $additional_parameters parameter of the PHP mail() function.

All addresses used by PHPMailer are validated before being used, however, it's possible to construct a valid email address that also constitutes an executable command when passed to the shell via mail().

This vulnerability constitutes CVE-2016-10033, and was thought to be resolved by applying escapeshellarg() to the address, and released as PHPMailer 5.2.20, with a subsequent cleanup in PHPMailer 5.2.21.

Act II

The documentation for mail() mentions that escapeshellcmd() is applied internally to the command generated by mail(), and that 'dangerous' characters should be escaped. Unfortunately this clashes with what escapeshellarg() does, and the nested combination of escapeshellcmd(escapeshellarg($address)) can result in a crafted address breaking out of the escaping, resulting in an executable command again. This was also spotted by Dawid Golunski (advisory) and reported as CVE-2016-10045. Unhelpfully, an exploit for this was posted on an open mailing list the same day, making this a 0-day vulnerability.

This is a much more insidious problem because it's really a problem with PHP internals, and the only reliable mitigation is to prevent use of valid addresses that might also be exploits, which requires breaking RFC compliance for sender addresses. The escapeshellcmd and escapeshellarg functions make assumptions about the underlying shell environment (for example what character it uses for escaping, which may not be \, or what character set is in use), so their use cannot be considered safe especially across platforms - the only safe route is to apply severe restrictions to the sender address in this context. A patch to this effect was provided by @Zenexer and released in PHPMailer 5.2.20. @Zenexer also provided a technical description of the problems surrounding shell escaping in PHP.

A proper fix for this would be to rewrite the PHP mail function so that it treats $additional_parameters as an array of individual configuration elements that can be processed separately, not a string, or more thoroughly, to not invoke sendmail via a shell at all, avoiding escaping issues altogether.

You're safe if...

You need to be doing one key thing wrong in order to be vulnerable to this attack: using a user-supplied address as the From address, for example:

$mail->setFrom($_POST['email']);

If you're not doing that, you've nothing to worry about, but unfortunately this is quite a common pattern on naïve 'contact us' forms (see Stack Overflow for many examples) because it means you can reply to the message and it will go directly to the supposed sender. However, this is bad practice because it's forging the from address, and so it will likely fail SPF and DMARC checks and you will have difficulty getting messages delivered. The correct way to do this is along the lines of:

$mail->setFrom('contact@example.com'); //Send from a fixed, valid address in your own domain, perhaps one that allows you to easily identify that it originated on your contact form
$mail->addAddress('me@example.com'); //Send to a fixed, valid address in your own domain
$mail->addReplyTo($_POST['email']); //The submitter's address (supposedly) - this is automatically validated before it's accepted so you should check the return value from this function

You are also safe if you're using PHPMailer's SMTP transport (i.e. you call $mail->isSMTP() in your code), as that transport does not execute shell commands.

You're indirectly protected if you use postfix as your mail server (the default in many Linux distros), because it lacks support for sendmail's -X option that permits the creation of executable commands. However, you shouldn't rely on this, as it's possible that other exploits for the same vulnerability will affect postfix.

Don't think you're safe if...

You're not using PHPMailer? Both of these vulnerabilities occur in other popular PHP email libraries and applications too - for example Swiftmailer, zend_mail, and roundcube, and there may be many more.

How to avoid issues in future?

CVE-2016-10045 particular has enormous implications well beyond PHPMailer, so check your sources, and help their maintainers spot and fix problems like this before they escape into the wild. There may be other exploits found for mail() (keep an eye on Dawid Golunski's white paper on the subject), so the most important thing is to make sure you stay up to date with all your packages, not just PHPMailer.