WordPress.org可进行蠕虫攻击的存储型XSS漏洞披露实例教程

WP GDPR Compliance是WordPress中一个非常流行的插件,被发现存在权限提升漏洞,通过对该漏洞的利用,攻击者可以轻易的劫持成千上万的网站。虽然一个插件的漏洞仅代表了使用它的网站上的安全缺陷,然而,就WordPress生态系统的风险性而言,有一些内容比WP GDPR Compliance之类的流行插件更值得我们探讨,那就是WordPress.org的安全性。在这篇博文中,我们调查了WordPress.org网站上存储的一个关键的XSS漏洞,在今年5月我们曾向WordPress安全团队报告过这一漏洞。
介绍
WordPress.org网站上包含了所有WordPress网站使用过的插件和主题库,除此之外,它还管理了开发人员用来编辑主题和插件代码的帐户。今年5月,我们就告知过WordPress安全团队其网站上的一个关键的存储型XSS漏洞。该漏洞显示在存储库的插件版本号中。因此,只要是拥有该插件的用户都可以将任意JavaScript代码注入WordPress.org网站中。
这个漏洞是我们在开发coderisk.com网站期间检测到的,为了开发该网站,我们不得不对库中每个插件的可用版本进行排序。而由于缺乏统一的版本控制方案,这个过程比我们预期的更具挑战性。超过5万个插件向我们展示了许多“创造性的”版本控制方案,其中之一就是在插件中使用图像而不是版本号。该插件上一次更新是在10年前,当访问存储库中的插件页面时,会确认图像实际上是显示的。当然,这也给安全研究人员敲响了警钟。通过一个插件的快速验证,我们确认了在那个位置注入任意JavaScript的行为是能够实现的。在下文中,我们将概述导致该漏洞的技术细节,并解释该漏洞是如何劫持其他作者的插件的。
技术细节
WordPress.org网站的源代码可以作为WordPress元环境的一部分公开使用。因此,我们可以研究代码的哪一部分致使了漏洞的产生。
插件是如何存储的
WordPress.org网站是使用WordPress CMS构建的。插件库中显示的插件仅仅是使用特殊模板显示的专用文章类型(post-type)的文章。
更改文件、上传新版本等操作,开发人员可以不与网站交互,而是使用版本控制系统Subversion。一旦开发人员将插件插入WordPress存储库,他就能访问Subversion服务器,接着便能在Subversion服务器上对托管的插件进行更改。身份验证凭证与开发人员的WordPress.org用户帐户的凭证相同。插件名、描述、版本等数据都是从插件的readme.txt和主PHP文件中的特殊标头中检索的。WordPress.org上的插件存储库会监视Subversion服务器的变化,并在需要时更新它已保存的有关插件的数据。
注入点
此漏洞的产生,是由于插件版本号在存储库中相应的插件页上显示之前没有得到充分清理。

下面的列表显示了负责显示版本号的代码。
wordpress.org/public_html/wp-content/plugins/plugin-directory/widgets/class-meta.php
' . get_post_meta( $post->ID, 'version', true ) . '' ); >
如上所述,在存储库中,插件由专用的post-type表示。关于插件的其他信息(例如版本),将作为元数据添加到代表文章中。最后一行显示了从数据库检索版本号并将其打印到输出的过程中不需要进行任何验证或转义。此时,如果版本号在存储到数据库之前未被清理,则会给出存储的XSS。
插件的版本号是从主PHP文件的特殊头文件中检索出来的。
wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php

namespace WordPressdotorgPlugin_DirectoryCLI;

class Import {

public function import_from_svn( $plugin_slug ) {

$data = $this->export_and_parse_plugin( $plugin_slug );

$headers = $data['plugin_headers'];

update_post_meta( $plugin->ID, 'version', wp_slash( $headers->Version ) );
从上面的代码中可以看出,WordPressdotorg Plugin_Directory CLI Import:import_from_svn()方法负责将Subversion服务器中的更改与存储在插件存储库中的数据同步。它使用export_and_parse_plugin()方法从插件头部分和readme.txt文件中提取信息,并保存必要的更改。表示版本的字段将作为元值保存到表示插件的post中。只有wp_slash()函数(不阻止XSS)才会应用于检索和保存之间的版本。
如何利用
要利用该漏洞,攻击者需要在WordPress.org插件库中拥有一个插件。考虑到贡献插件的容易性,这并不难实现。
攻击者可以在插件的version字段中隐藏任意的JavaScript的payload。每当有人访问存储库中的插件页面时,此payload就会在WordPress.org的上下文中执行。那么,payload可能会造成什么损害呢?
当插件作者在WordPress.org中登录时,他可以将其他帐户作为提交者添加到他的插件中。这些帐户将被授予对插件的Subversion存储库的完全写入权限,因此可以修改插件的代码。通过这种方式授予对插件的访问权限的帐户,也可以添加和删除提交者。例如,他们可以删除主提交者,即插件的作者,而无需任何验证或警告。添加提交者的过程可以通过一些简单的JavaScript ajax请求触发。攻击者可以设计一个XSS

{C}payload,当拥有插件的登录用户访问受感染的页面时,该payload将无声的添加一个他拥有的帐户作为提交者。此时,攻击者作为提交者,可以将后门隐藏在被劫持的插件中,并将他的payload注入他们的版本号中以传播更多内容。这个扩展可以通过发布一个包含payload的插件链接来启动(例如WordPress slack,或者支持论坛)。
另外:管理仪表板中的反射型XSS
在找到第一个漏洞后,我们决定使用RIPS静态代码分析器扫描WordPress.org代码库。这揭示了WordPress.org/plugins的管理仪表板中的另一个漏洞——一个反射型XSS。
wordpress.org/public_html/wp-content/plugins/plugin-directory/admin/tools/class-stats-report.php

public function show_stats() {

if ( isset( $_POST['date'] ) && preg_match( '/[0-9]{4}-[0-9]{2}-[0-9]{2}$/', $_POST['date'] ) ) {
$args['date'] = $_POST['date'];
} else {
$args['date'] = '';
}

$stats = $this->get_stats( $args );

printf(
__( 'Displaying stats for the %1$d days preceding %2$s (and other stats for the %3$d most recent days).', 'wporg-plugins' ),
$stats['num_days'],
$stats['date'],
$stats['recentdays']
);
在上段代码的第3行中,从$ _POST ['date']收到的用户输入被验证为具有带正则表达式的特定模式。然后打印从用户输入检索的值,而不需要在列表的第11行中转义。正则表达式构造方式上的一个小缺陷使注入payload成为可能,尽管一开始它似乎验证了输入与xxxx-xx-xx模式的严格一致性。
视频连接:https://blog.ripstech.com/videos/wporg-reflected-xss.mp4
由于正则表达式中缺少起始分隔符^,它能匹配日期按所需格式结束的任何内容。因此,诸如 alert(1) 0000-00-00之类的payload将绕过检查并被执行。RIPS静态代码分析器会准确地将正则表达式识别为不完善的修复并跟踪到接收器的输入。
时间线
2018/05/11 漏洞报告给Hackerone上的WordPress安全团队。
2018/05/12 该漏洞由安全团队进行分类和验证。
2018/05/12 安全团队部署了修补程序https://meta.trac.wordpress.org/changeset/7195。
总结
在这篇博文中,我们介绍了我们在WordPress.org网站上检测到的两个漏洞。第一个是插件存储库中的关键存储型XSS,任何在存储库中都有插件的用户都可以利用它。如果不幸被恶意攻击者利用,可能会造成巨大的损害。第二个是WordPress.org/plugins管理仪表板中反射型XSS,它展示了正则表达式中的一个小缺陷是如何影响代码的。