前言
上个月我们公开了WordPress 5.0中需要身份验证的远程代码执行漏洞。这篇博文将揭露另WordPress 5.1的另一个高危漏洞链,允许未经身份验证的攻击者在WordPress 5.1.1之前的任何版本获取远程代码执行。
影响
通过诱导该站点管理员访问攻击者设置的网站,攻击者可以接管任何启用评论的WordPress站点。只要受害管理员访问恶意网站,一个针对WordPress博客的跨站请求伪造(CSRF)漏洞就会在后台运行,并且受害者浑然不觉。这个CSRF漏洞利用了多个逻辑缺陷和过滤的错误,当组合起来时导致远程代码执行从而接管目标站点。
这些漏洞存在于WordPress 5.1.1以前的所有版本,并且可以通过默认设置进行攻击。
WordPress被互联网上超过33%的网站使用,它的下载页面如是说。考虑到评论是博客的一项核心功能并且默认开启,此项漏洞将影响数百万个站点。
技术分析
评论形式的CSRF导致HTML注入
用户发表新评论时WordPress没有进行CSRF验证。这是因为如果评论处存在验证,一些WordPress功能(比如说trackbacks and pingbacks)将无法使用。因此,攻击者通过CSRF攻击能够使用WordPress博客的某个管理员用户的身份来创建评论。
这能够演变成一个安全事故,因为WordPress博客的管理员们可以在评论处使用任意HTML标签,甚至是<script>
。从理论上说,攻击者能滥用该CSRF漏洞来创建一个含有恶意JS代码的评论。
WordPress尝试通过在评论表单处为管理员创建一个额外的nonce
来解决这个问题。管理员携带有效的nonce
提交评论时,这个评论将不会进行任何过略。如果nonce
是无效的,那么这个评论仍然可以创建但是会被过滤处理。
下面是WordPress核心中的部分解决代码:
⋮
if ( current_user_can( 'unfiltered_html' ) ) {
if (! wp_verify_nonce( $_POST['_wp_unfiltered_html_comment'], 'unfiltered-html-comment' )) {
$_POST['comment'] = wp_filter_post_kses($_POST['comment']);
}
} else {
$_POST['comment'] = wp_filter_kses($_POST['comment']);
}
⋮
事实上早在2009年,我们都知道评论表单中已经不存在CSRF保护了。
然而,我们在管理员消毒程序中发现了一个逻辑缺陷。正如你在代码片段所见,评论一直使用wp_filter_kses()
来消毒,除非评论者是管理员时才会使用unfiltered_html
。如果是这种情况并且管理员没有提供有效的nonce
,那么将使用wp_filter_post_kses()
代替消毒(上述代码块的第四行)。
wp_filter_post_kses()
和wp_filter_kses()
的不同之处是过滤程度。这两个函数都允许未消毒的评论,但是字符串只能是属于一个确定的HTML标签和属性列表。通常wp_filter_kses()
函数对评论消毒时只允许一些基本的HTML标签和属性,例如<a>
标签结合href
属性使用。
另一个函数允许包含更多的HTML标签和属性。但是尽管wp_filter_post_kses()
权限更宽松,一些可以导致跨站脚本漏洞的标签仍然会被删除。
HTML注入到储存型XSS
事实上我们仍然可以注入额外的HTML标签和属性来造成WordPress核心的储存型XSS漏洞。这是因为一些属性即使不允许在评论处设置,但是仍然可以被错误解析和操作,从而导致任意属性注入。
WordPress完成评论消毒后,它将会修改<a>
标签以实现搜索引擎优化。
这是通过解析<a>
标签的属性字符串(比如href="#" title="some link" rel="nofollow"
)到一个关联数组来实现的(下面代码第三行),其中key
为属性名value
为属性值。
function wp_rel_nofollow_callback( $matches ) {
$text = $matches[1];
$atts = shortcode_parse_atts($matches[1]);
⋮
WordPress然后回检查rel
属性是否设置。只有评论是以wp_filter_post_kses()
函数过滤的,该属性才可以被设置。如果存在,那么rel
标签将会和<a>
标签放在一起。
if (!empty($atts['rel'])) {
// the processing of the 'rel' attribute happens here
⋮
$text = '';
foreach ($atts as $name => $value) {
$text .= $name . '="' . $value . '" ';
}
}
return '<a ' . $text . ' rel="' . $rel . '">';
}
此缺陷对映上述代码块中的第5,6行,其属性值被连接在一起而没有被转义。
攻击者可以创建一个含有<a>
的评论,比如说设置title
属性锚点为title='XSS " onmouseover=alert(1) id="'
。该属性是有效的HTML并且可以通过消毒步骤。然而,构造的title
标签使用单引号时才有效。
当这些标签组合在一起,title
属性将被双引号包装起来(第六行)。因此,攻击者能够通过注入额外的双引号关闭title
属性来注入额外的HTML属性。
举个例子:<a title='XSS " onmouseover=evilCode() id=" '>
经过处理后会变成<a title="XSS " onmouseover=evilCode() id=" ">
。
因为此时评论已经经过消毒处理,所以注入的onmouseover
事件程序已经储存到数据库中。这允许攻击者通过CSRF漏洞和过略缺陷注入储存型XSS有效载荷到目标网站。
通过iframe执行XSS
只要管理员查看注入JS代码的恶意评论,攻击者就能够获取远程代码执行。此评论展示在目标WordPress博客的前端。对于WordPress本身来说,前端并不受X-Frame-Options
保护。因此,恶意评论可以储存在一个不可见的<iframe>
中,并且此<iframe>
储存在攻击者控制的网站中。因为注入的属性是一个onmouseover
事件程序,攻击者可以制作<iframe>
标签结合受害者鼠标移动来触发XSS有效载荷。
这允许攻击者加载目标网站触发CSRF漏洞的管理员会话执行任意JS代码。所有的JS代码都在后台执行,受害者浑然不觉。
从XSS到远程代码执行
现在我们可使用管理员会话执行任意JS代码,因此实现远程代码执行变得简单起来了。WordpRress在后台管理页面默认允许博客管理员直接编辑主题和插件的.php
文件。只需简单地插入一个PHP后门,攻击者就能实现任意PHP代码执行。
补救措施
默认情况下,WordPress会自动安装补丁,你应该已经是5.1.1版本了。但是如果你由于某种原因不能更新,那么在升级前你应该禁用评论功能。最重要的一点,在浏览其他网站前确保注销管理员会话。
总结
本文详细阐述了一个CSRF漏洞起始的漏洞链。此漏洞链允许任何WordPress站点在默认设置情况下被攻击者接管,只需目标网站管理员访问一个恶意网站,并且受害者在攻击者控制的网站上不会发现任何异常。除了访问攻击者设置的网站以外,不需要任何形式的交互。
时间线
- 2018/10/24 报告WordPress,通过CSRF可以注入更多的HTML标签
- 2018/10/25 Hackone上,WordPress团队确认了此漏洞
- 2019/02/05 WordPress准备发布补丁,请求我们的建议
- 2019/03/01 报告WordPress,HTML注入可导致存储型XSS漏洞
- 2019/03/01 WordPress反馈:安全团队已了解并准备发布补丁
- 2019/03/13 发布WordPress 5.1.1