“PHPMailer continues to be the world’s most popular transport class, with an estimated 9 million users worldwide. Downloads continue at a significant pace daily. “Probably the world’s most popular code for sending email from PHP! Used by many open-source projects: WordPress, Drupal, 1CRM, SugarCRM, [..], Joomla! and many more.”

Today, a security researcher named Yongxiang Li of Asiasecurity uncovered a critical vulnerability in PHPMailer that could potentially be used by attacker to read local file. The vulnerability number are CVE-2017-5223. All PHPMailer <= 5.2.21 has been affected by this vulnerability. CVE-2017-5223 local file disclosure vulnerability if content passed to msgHTML() is sourced from unfiltered user input. Reported by Yongxiang Li of Asiasecurity. The fix for this means that calls to msgHTML() without a $basedir will not import images with relative URLs, and relative URLs containing .. will be ignored.

Vulnerability Analysis

See encodeFile function on class.phpmailer.php file.

This function receives a $path variable, which is then loaded into the file_get_contents function. If the $path variable is controllable, any file can be read:

protected function encodeFile($path, $encoding = ‘base64’)
{
try {
if (!is_readable($path)) {
throw new phpmailerException($this->lang(‘file_open’) . $path, self::STOP_CONTINUE);
}
$magic_quotes = get_magic_quotes_runtime();
if ($magic_quotes) {
if (version_compare(PHP_VERSION, ‘5.3.0’, ‘<‘)) {
set_magic_quotes_runtime(false);
} else {
//Doesn’t exist in PHP 5.4, but we don’t need to check because
//get_magic_quotes_runtime always returns false in 5.4+
//so it will never get here
ini_set(‘magic_quotes_runtime’, false);
}
}
$file_buffer = file_get_contents($path);
$file_buffer = $this->encodeString($file_buffer, $encoding);
if ($magic_quotes) {
if (version_compare(PHP_VERSION, ‘5.3.0’, ‘<‘)) {
set_magic_quotes_runtime($magic_quotes);
} else {
ini_set(‘magic_quotes_runtime’, $magic_quotes);
}
}
return $file_buffer;
} catch (Exception $exc) {
$this->setError($exc->getMessage());
return ”;
}

The AddAttachment and AddEmbeddedImage functions are finally found by the trace to the encode File function. The AddAttachment function is used to send an attachment in a message, which can be triggered if the attachment name is controllable.

Viewing AddEmbeddedImage function, the function is to deal with the contents of the picture in the mail, and now $path can be controlled as long as the trigger. Now is to find controllable points:

public function addEmbeddedImage($path, $cid, $name = ”, $encoding = ‘base64’, $type = ”, $disposition = ‘inline’)
{
if (!@is_file($path)) {
$this->setError($this->lang(‘file_access’) . $path);
return false;
}

Back to the function found msgHTML function call the function, msgHTML function is used to send html format mail, the call process is as follows:

if ($this->addEmbeddedImage(
$basedir . $directory . $filename,
$cid,
$filename,
‘base64’,
self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))

View $url

public function msgHTML($message, $basedir = ”, $advanced = false)
{
preg_match_all(‘/(src|background)=[“\’](.*)[“\’]/Ui’, $message, $images);
if (array_key_exists(2, $images)) {
foreach ($images[2] as $imgindex => $url) {
// Convert data URIs into embedded images
if (preg_match(‘#^data:(image[^;,]*)(;base64)?,#’, $url, $match)) {
$data = substr($url, strpos($url, ‘,’));
if ($match[2]) {
$data = base64_decode($data);
} else {
$data = rawurldecode($data);
}
$cid = md5($url) . ‘@phpmailer.0′; // RFC2392 S 2
if ($this->addStringEmbeddedImage($data, $cid, ’embed’ . $imgindex, ‘base64’, $match[1])) {
$message = str_replace(
$images[0][$imgindex],
$images[1][$imgindex] . ‘=”cid:’ . $cid . ‘”‘,
$message
);
}

$url is parsed by src = “xxxxx” in $message, $url is finally parsed as xxxxx, and $message is the custom content of the message we sent. This control point to find, you can successfully use the vulnerability.

How to fix

Update to PHPMailer 5.2.22

More info:

https://github.com/PHPMailer/PHPMailer/blob/master/SECURITY.md