原文:
http://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html
PHPMailer
类使用 PHP自带的默认的mail()
函数作为它的默认传输方式。参见:
http://php.net/manual/zh/function.mail.php
必选的有三个参数。
- string $to: 电子邮件收件人,或收件人列表;
本字符串的格式必须符合 » RFC 2822。例如:
user@example.com
user@example.com, anotheruser@example.com
User <user@example.com>
User <user@example.com>, Another User <anotheruser@example.com>
- string $subject: 主题;
电子邮件的主题; 本项不能包含任何换行符,否则邮件可能无法正确发送。 - string $message: 消息内容;
所要发送的消息。
行之间必须以一个 LF(\n)分隔。每行不能超过 70 个字符。
>>> doc mail
function mail($to, $subject, $message, $additional_headers = unknown, $additional_parameters = unknown)
Description:
发送邮件
发送一封电子邮件。
还有两个可选项:$additional_headers
和$additional_parameters
。
是这样实现的。在phpmailer库的class.phpmailer.php
中
/**
* Send mail using the PHP mail() function.
* @param string $header The message headers
* @param string $body The message body
* @link http://www.php.net/manual/en/book.mail.php
* @throws phpmailerException
* @access protected
* @return boolean
*/
protected function mailSend($header, $body)
{
$toArr = array();
foreach ($this->to as $toaddr) {
$toArr[] = $this->addrFormat($toaddr);
}
$to = implode(', ', $toArr);
$params = null;
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
// CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
if (self::isShellSafe($this->Sender)) {
$params = sprintf('-f%s', $this->Sender);
}
}
if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) {
$old_from = ini_get('sendmail_from');
ini_set('sendmail_from', $this->Sender);
}
$result = false;
if ($this->SingleTo and count($toArr) > 1) {
foreach ($toArr as $toAddr) {
$result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
...
其中关键在于这一行。
$result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
然后把同样的参数传给PHP的默认mail()
函数。
/**
* Call mail() in a safe_mode-aware fashion.
* Also, unless sendmail_path points to sendmail (or something that
* claims to be sendmail), don't pass params (not a perfect fix,
* but it will do)
* @param string $to To
* @param string $subject Subject
* @param string $body Message Body
* @param string $header Additional Header(s)
* @param string $params Params
* @access private
* @return boolean
*/
private function mailPassthru($to, $subject, $body, $header, $params)
{
//Check overloading of mail function to avoid double-encoding
if (ini_get('mbstring.func_overload') & 1) {
$subject = $this->secureHeader($subject);
} else {
$subject = $this->encodeHeader($this->secureHeader($subject));
}
//Can't use additional_parameters in safe_mode, calling mail() with null params breaks
//@link http://php.net/manual/en/function.mail.php
if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) {
$result = @mail($to, $subject, $body, $header);
} else {
$result = @mail($to, $subject, $body, $header, $params);
}
return $result;
}
public function setFrom($address, $name = '', $auto = true)
{
$address = trim($address);
$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
// Don't validate now addresses with IDN. Will be done in send().
if (($pos = strrpos($address, '@')) === false or
(!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
!$this->validateAddress($address)) {
...