首先介绍一下主角PHPMailer

PHPMailer continues to be the world’s most popular transport class, with an estimated 9 million users worldwide. Downloads continue at a significant pace daily.

PHPMailer以全球约900万的用户量成为最受欢迎的邮件类。下载量每天还在上升。

Probably the world’s most popular code for sending email from PHP! Used by many open-source projects: WordPress, Drupal, 1CRM, SugarCRM, [..], Joomla! and many more

PHPMailer可能是全球PHP发送邮件最流行的代码。亦被诸多开源项目所采用,包括WordPress、Drupal、1CRM、SugarCRM、Joomla!等。

用户量这么大的一个邮件类出现问题也算是一个重磅炸弹了。

出现漏洞的位置

这里看到的代码是5.2.16版本的。PHPMailer使用的是PHP自带的mail()函数。这次出出现问题的源头是mail()函数的第五个参数$params未经过滤。

可以看到这里有一个条件,当开启safe_mode的时候就不能执行这个函数。

pic1-1

根据mailPassthru这个函数名就可以追到这个$params赋值的地方。

pic1-2

$this->Sender将值赋给了$params

pic1-3

上面看到在$address赋值给$this->Sender之前经过一系列的参数过滤。 最关键的是这里,要仔细看一下。

pic1-4

首先$address经过trim()的过滤去掉了可能存在的 \t\n\r\0\x0B

下面的一组判断是:

($pos = strrpos($address, '@')) === false or (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and !$this->validateAddress($address)

首先判断了$address中是否含有@

然后再判断@后面是否有任意8位字符(其实就是看有没有汉字和汉字标点)

pic1-5

重点来了,validateAddress()函数是用来检查输入的邮件地址是不是合法的Email地址的。这次漏洞利用主要是突破这个函数中的限制。

pic1-6

pic1-7

首先说一下条件最苛刻但利用起来比较简单的方法,根据上面的代码可以看到,如果在$patternselect'noregex'的时候,直接跳过了正则表达式的检测,条件:没有安装pcre,且PHP版本小于5.2.0。

pic1-8

pic1-9

从这里来看,这个漏洞利用起来比较鸡肋,但是正则表达式能绕过,这个漏洞就没有那么鸡肋了。

这里根据phithon大神的总结自己又复现了一遍:

pic1-10

pic1-11

首先看最容易理解的第二组:(?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)

这里主要是针对 \t以及回车\x0D换行\x0A的匹配。

接下来是第三组:(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\))

第三组中套用了第二组,并对第三组进行递归,也就是说在第三组过滤特殊字符同时也引入了问题


Monburan

好奇|爱折腾