WordPress_4.6_RCE

WordPress<=4.6_RCE分析

0x01 概述

CVE-2016-10033

主要原因wordpress使用phpmailer组件进行重置密码邮件的发送(所以漏洞存在于所有使用phpmailer组件的cms),漏洞其实是存在于phpmailer,加上wordpress的过滤不严。

漏洞文件:wordpress/wp-includes/class.phpmailer.php and wordpress/wp-includes/pluggable.php

0x02 代码分析

1
2
3
4
5
6
7
8
9
10
11
12
/**来自wordpress/wp-includes/class.phpmailer.php
* Which method to use to send mail.
* Options: "mail", "sendmail", or "smtp".
* @var string
*/
public $Mailer = 'mail';

/**
* The path to the sendmail program.
* @var string
*/
public $Sendmail = '/usr/sbin/sendmail';

既然知道是因为发送邮件引发的rce,那么我们就直接看邮件部分,由最后一句可知,利用系统命令发送邮件,我直接就想到了命令注入。那我们来看参数的获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	 //这一段来自wordpress/wp-includes/pluggable.php
if ( !isset( $from_email ) ) {
// Get the site domain and get rid of www.
$sitename = strtolower( $_SERVER['SERVER_NAME'] );
if ( substr( $sitename, 0, 4 ) == 'www.' ) {
$sitename = substr( $sitename, 4 );
}

$from_email = 'wordpress@' . $sitename;
}


/**
* Filters the name to associate with the "from" email address.
*
* @since 2.3.0
*
* @param string $from_name Name associated with the "from" email address.
*/
$from_name = apply_filters( 'wp_mail_from_name', $from_name );


$phpmailer->setFrom( $from_email, $from_name );

当调用WordPress wp_mail()函数发送电子邮件时(例如,在用户注册,忘记密码等情况下),WordPress会根据SERVER_NAME服务器标头设置电子邮件域,即$sitename=SERVER_NAME。
发件人地址的格式如下:$ from_email =’wordpress@’.$ sitename;
然后将其简单检测并传递给易受攻击的PHPMailer的setFrom()函数。

然后再看看SERVER_NAME这个参数哪里来的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**来自wordpress/wp-includes/class.phpmailer.php
* Get the server hostname.
* Returns 'localhost.localdomain' if unknown.
* @access protected
* @return string
*/
protected function serverHostname()
{
$result = 'localhost.localdomain';
if (!empty($this->Hostname)) {
$result = $this->Hostname;
} elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
$result = $_SERVER['SERVER_NAME'];
} elseif (function_exists('gethostname') && gethostname() !== false) {
$result = gethostname();
} elseif (php_uname('n') !== false) {
$result = php_uname('n');
}
return $result;
}

serverHostname函数用于获取SERVER_NAME(主机名),就是请求报文中的HOST,可以看出除了几个判断,明显没有做任何的过滤,所以这里存在命令注入是必然的,但是另一个问题出现了,参数是从HOST头获取的这里不能使用特殊字符,所以直接的命令执行行不通。

0x03 Poc实现

所以既然存在命令注入,如何利用就是我们需要考虑的问题了。既然最后是利用系统命令执行,那我们就来看看,能怎么操盘。sendmail 提供了-O和-X参数,-X参数用于写入日志文件, 可以使用-O QueueDirectory=/tmp/ -X /tmp/test.php命令组合,它会将发送的邮件保存到/tmp/test.php中.但是实际上还需要解决一些问题,且sendmail比我们想象的更加强大.

  • wordpress方面以及PHPMailer库方面都会防止攻击者注入空字符(空格或TAB)到sendmail命令中。并且,添加括号引入向sendmail中注入参数的方法已经行不通了.
  • 比如我们想要调用/bin/touch的时候也会出问题,因为host字段中如果出现/,服务器会拒绝我们的请求。

但是在ubuntu/debain系统中,已经使用exim4替代了sendmail的功能,现在sendmail文件就是一个链向exim4的链接文件,而exim4又多了-be参数用于读取部分变量的数据,exim4还提供函数来执行一些命令,如字符串截取函数$substr、系统命令调用函数$run。说到字符串截取,应该想到了,曾经使用字符串截取实现无空格的命令执行.说到系统命令调用,就应该命令执行了。字符串截取+命令执行=计划通

于是

  • ${substr{0}{1}{$spool_directory}} =/
  • ${substr{10}{1}{$tod_log}}=space
  • target(any -froot@localhost -be ${command}} null) //payload

tip:command不能出现特殊字符。

avatar