[Vulnerability] PHPMailer practice -Day5 command execution vulnerability [CVE-2016-10045 and CVE-2016-10033]

He began to practice [red] team of PHP-Audit-Labs code audit Day5
link: https://github.com/hongriSec/PHP-Audit-Labs
interested students can go to Exercise
Prior knowledge:
content title comes from PHP SECURITY 2017 CALENDAR
Day. 5 - Postcard code is as follows:

class Mailer {
  private function sanitize($email) {
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
      return '';
    }

    return escapeshellarg($email);
  }

  public function send($data) {
    if (!isset($data['to'])) {
      $data['to'] = '[email protected]';
    } else {
      $data['to'] = $this->sanitize($data['to']);
    }

    if (!isset($data['from'])) {
      $data['from'] = '[email protected]';
    } else {
      $data['from'] = $this->sanitize($data['from']);
    }

    if (!isset($data['subject'])) {
      $data['subject'] = 'No Subject';
    }

    if (!isset($data['message'])) {
      $data['message'] = '';
    }

    mail($data['to'], $data['subject'], $data['message'],
      '', "-f" . $data['from']);
  }
}

$mailer = new Mailer();
$mailer->send($_POST);

Vulnerability Analysis:
This question is actually a study by the command php mail function built-triggered execution vulnerability. Let's look at the usage php built-in mail function:

mail () function is defined:

(PHP 4, PHP 5, PHP 7)

Features:

PHP mail () function is used to send e-mail from the script.

definition:
mail(to,subject,message,headers,parameters)

Notes: PHP function requires a run-mail mail system installed and running (such as: sendmail, postfix, qmail, etc.). The procedure used is defined by the configuration settings in the php.ini file. Read more in our PHP Mail Reference Manual.

Description:
parameter description
to essential. Provision email recipients.
subject essential. Provisions subject of the email. Note: This parameter can not contain any newline characters.
message essential. Define the message to be sent. Use LF (\ n) to separate the rows. Each line should be limited to 70 characters.
headers Optional. Additional provisions of the title, such as From, Cc, and Bcc. Should use CRLF (\ r \ n) separated by an additional header.
parameters Optional. Send e-mail to the procedure laid down additional parameters.

在Linux系统上, php 的 mail 函数在底层中已经写好了,默认调用 Linux 的 sendmail 程序发送邮件。而在额外参数( parameters )中, sendmail 主要支持的选项有以下三种:

  • -O option = value

    Select the queue message QueueDirectory = queuedir

  • -X logfile

    This parameter can specify a directory to record detailed logs situation when sending mail.

  • -f from email

    This parameter allows us to specify our e-mail address to send mail.

example

PHP Easy E-Mail

通过 PHP 发送电子邮件的最简单的方式是发送一封文本 email。

In the following example, we first declare a variable ($to, $subject, $message, $from, $headers), then we have mail()to send a letter E-mail functions use these variables:

<?php
$to = "[email protected]";         // 邮件接收者
$subject = "参数邮件";                // 邮件标题
$message = "Hello! 这是邮件的内容。";  // 邮件正文
$from = "[email protected]";   // 邮件发送者
$headers = "From:" . $from;         // 头部信息设置
mail($to,$subject,$message,$headers);
echo "邮件已发送";
?>

Of course, this question if this is just a problem, it will be obvious too simple, we continue to look down, 在 第3行there is such a bunch of code:

    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {

The main role of this string of code is to ensure that a valid email address $ email only the first five parameter. Let's look at the definition of filter_var () function:

the filter_var () function is defined:

Features:

filter_var()A variable filter function specified by the filter.
If successful, the filtered data is returned. If it fails, it returns FALSE.

Description:

mixed filter_var ( mixed $variable [, int $filter = FILTER_DEFAULT [, mixed $options ]] )

parameter description
variable essential. The provisions of the variables to be filtered.
filter Optional. To use a predetermined filter's ID. The default is FILTER_SANITIZE_STRING. See Complete PHP Filter Reference Manual, see the possible filters. Filter ID may be an ID name (such FILTER_VALIDATE_EMAIL) or ID number (such as 274).
options Optional. The provisions of an associative array of flags / options or a single flag / option. Check each filter possible flags and options.

Here Insert Picture Description
这里主要是根据第二个参数filter过滤一些想要过滤的东西。
About filter_var()in FILTER_VALIDATE_EMAILthis action option, we can look at this post PHP FILTER_VALIDATE_EMAIL . This is inside a conclusion caught my attention: none of the special characters in this local part are allowed outside quotation marks( 表示所有的特殊符号必须放在双引号中). filter_var()The problem is that we are 双引号中嵌套转义空格still able to detect. At the same time due to the underlying regular expressions, we passed 重叠单引号和双引号deceive filter_val()into thinking that we are still in double quotes, so we can bypass the detection. Below is an example of a simple, convenient appreciated

<?php
var_dump(filter_var('\'is."\'\ not\ 1"@admin.com',FILTER_VALIDATE_EMAIL));
var_dump(filter_var('"is.\ not\ 2"@admin.com',FILTER_VALIDATE_EMAIL));
var_dump(filter_var('"is.""\ not\ 3"@admin.com',FILTER_VALIDATE_EMAIL));
?>

Here Insert Picture Description
Of course, since the introduction of special symbols, while bypassing the filter_var()detection for the mailbox, but because of PHP mail()function in the underlying implementation, a call escapeshellcmd()function, a mail address input by the user is detected, even if there is a special symbol resulting in, will be escapeshellcmd()a function of the processing escape, so there is no way to achieve the purpose of the command execution. the escapeshellcmd () function in the underlying code (detailed points where (lines 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;
	}

We continue to analyze the topic of code 第七行:

    return escapeshellarg($email);

This code is mainly handling $emailincoming data.

escapeshellarg function definition:

(PHP 4 >= 4.0.3, PHP 5, PHP 7)

Features:

escapeshellarg - transcode the string parameter may be used in shell command in

Description:
escapeshellarg ( string $arg ) : string

escapeshellarg()Will increase in a single quote and string can cite any single quotes or transcoding already present, so that to ensure that directly pass a string shellfunction, and also to ensure safety. For some parameters entered by the user should use this function. shell 函数包含 exec(), system() 执行运算符.

parameter description
arg Parameters need to be transcoded.

return value:

After a string conversion.

The role of specific functions, you can refer to the following cases:

<?php
var_dump(escapeshellarg("1234"));
var_dump(escapeshellarg("1'2'3"));
var_dump(escapeshellarg("12'  3"));
?>

Here Insert Picture Description
那我们前面说过了PHP的mail()函数在底层调用了 escapeshellcmd()函数对用户输入的邮箱地址进行处理,即使我们使用带有特殊字符的payload,绕过 filter_var() 的检测,但还是会被 escapeshellcmd() 处理。然而 escapeshellcmd()escapeshellarg 一起使用,会造成特殊字符逃逸,下面我们给个简单例子理解一下:

<?php
$param="127.0.0.1' -v -d a=1";
$a=escapeshellarg($param);
$b=escapeshellcmd($a);
$cmd="curl ".$b;
var_dump($a)."\n";
var_dump($b)."\n";
var_dump($cmd)."\n";
system($cmd);
?>

Here Insert Picture Description
详细分析一下这个过程:

  1. 传入的参数是

    127.0.0.1' -v -d a=1
    
  2. 由于escapeshellarg先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。所以处理之后的效果如下:

    '127.0.0.1'\'' -v -d a=1'
    
  3. 接着 escapeshellcmd 函数对第二步处理后字符串中的 \ 以及 a=1' 中的单引号进行转义处理,结果如下所示:

    '127.0.0.1'\\'' -v -d a=1\'
    
  4. 由于第三步处理之后的payload中的 \\ 被解释成了 \ 而不再是转义字符,所以单引号配对连接之后将payload分割为三个部分,具体如下所示:
    Here Insert Picture Description
    所以这个payload可以简化为 curl 127.0.0.1\ -v -d a=1' ,即向 127.0.0.1\ 发起请求,POST 数据为 a=1'

总结一下,这题实际上是考察绕过 filter_var() 函数的邮件名检测,通过 mail 函数底层实现中调用的escapeshellcmd() 函数处理字符串,再结合 escapeshellarg() 函数,最终实现参数逃逸,导致 远程代码执行 。

实例分析:

这里实例分析选择 PHPMailer 命令执行漏洞CVE-2016-10045 和 CVE-2016-10033 )。项目代码可以通过以下方式下载:

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

漏洞POC 本站提供安全工具、程序(方法)可能带有攻击性,仅供安全研究与教学之用,风险自负!

漏洞分析:

  • CVE-2016-10033

在github上直接diff一下,对比一下不同版本的 class.phpmailer.php 文件,差异如下:
Here Insert Picture Description
这里在 sendmailSend 函数中加了 validateAddress函数,来针对发送的数据进行判断,判断邮箱地址的合法性。另外针对传入的数据,调用了 escapeshellarg函数来转义特殊符号,防止注入参数。然而这样做,就引入了我们上面讨论的问题,即同时使用 escapeshellarg函数和 escapeshellcmd()函数,导致单引号逃逸。由于程序没有对传命令参数的地方进行转义,所以我们可以结合 mail函数的第五个参数-X 写入 webshell

下面详细看一下代码,漏洞具体位置在 class.phpmailer.php 中,我们截取部分相关代码如下 :
Here Insert Picture Description
在上图第12行处没有对 $params变量进行严格过滤,只是简单地判断是否为 null,所以可以直接传入命令。我们继续往下看,我们发现在上图第12行,当 safe_mode 模式处于关闭状态时, mail()函数才会传入$params变量。

进一步跟跟进 $params参数,看看它是怎么来的。这个参数的位置在 class.phpmailer.php中,我们截取部分相关代码,具体看下图 第11行
Here Insert Picture Description
很明显$params 是从 $this->Sender传进来的,我们找一下$this->Sender,发现这个函数在 class.phpmailer.php中,截取部分相关代码,具体看下图 第10行

Here Insert Picture Description
这里在 setFrom 函数中将$address 经过某些处理之后赋值给$this->Sender。我们详细看看 $address变量是如何处理的。主要处理函数均在class.phpmailer.php 文件中,我们截取了部分相关代码,在下图 第三行 中使用了 validateAddress来处理 $address变量。
Here Insert Picture Description
所以跟进一下 validateAddress函数,这个函数位置在class.phpmailer.php 文件中。我们看看程序流程,相关代码如下:
Here Insert Picture Description
分析一下这段代码,大概意思就是对环境进行了判断,如果没有prce并且 php 版本 <5.2.0 ,则 $patternselect = 'noregex'。接着往下看,在 class.phpmailer.php文件中,有部分关于 $patternselectswich操作,我只选择了我们需要的那个,跟踪到下面的noregex
Here Insert Picture Description
这里简单的只是根据@符号来处理字符,所以这里的payload很简单。

a( -OQueueDirectory=/tmp -X/var/www/html/x.php )@a.com

然后通过linux自身的sendmaillog的方式,把log写到web根目录下。将日志文件后缀定义为.php ,即可成功写入webshell

  • CVE-2016-10045

diff一下5.2.205.2.18发现针对 escapeshellcmdescapeshellarg做了改动。
这里其实有个很奇妙的漏洞,针对用户输入使用 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

实际上,可用于攻击的代码只是在之前的基础上多了一个单引号。之所以这次的攻击代码能够成功,是因为修复代码多了 escapeshellcmd函数,结合上 mail()函数底层调用的 escapeshellarg 函数,最终导致单引号逃逸。
Here Insert Picture Description
我们的 payload 最终在执行时变成了

'-fa'\\''\( -OQueueDirectory=/tmp -X/var/www/html/test.php \)@a.com\'

按照刚才上面的分析,我们将payload化简分割一下就是-fa\(、-OQueueDirectory=/tmp、-X/var/www/html/test.php、)@a.com',这四个部分。最终的参数就是这样被注入的。

漏洞利用:

漏洞有一些基本要求:

  1. php version < 5.2.0 2
  2. phpmailer < 5.2.18 3
  3. php 没有安装 pcre(no default)
  4. safe_mode = false(default)

存在正则绕过之后,以及 escapeshellargescapeshellcmd一起使用造成的神奇现象之后。
只需要 phpmailer < 5.2.20

环境,poc,exp相关

这里用docker复现:

git clone https://github.com/opsxcq/exploit-CVE-2016-10033.git      //下载文件
cd exploit-CVE-2016-10033              													//进入文件
docker run --rm -it -p 8080:80 vulnerables/cve-2016-10033       		//部署环境

这里报错我以为一直有问题,环境安装不了,结果直接新开一个终端看,发现是正常运行的。
Here Insert Picture Description
Here Insert Picture Description
进入页面:
Here Insert Picture Description
直接利用下载好的exp

./exploit.sh 127.0.0.1:8080

Here Insert Picture Description
执行完成。

修复建议:

我们来看一下PHPMailer官方给出的修复代码。官方对用户传入的参数进行检测,如果当中存在被转义的字符,则不传递-f参数(-f 参数表示发邮件的人,如果不传递该参数,我们的payload就不会被带入 mail 函数,也就不会造成命令执行),所以不建议大家同时使用 escapeshellcmd()escapeshellarg() 函数对参数进行过滤,具体修复代码如下:

Here Insert Picture Description

结语

再次感谢【红日团队】

发布了35 篇原创文章 · 获赞 19 · 访问量 5199

Guess you like

Origin blog.csdn.net/zhangpen130/article/details/103933875