WordPress <= 4.3.0 跨站脚本漏洞

2015-09-19T00:00:00
ID SSV:89479
Type seebug
Reporter RickGray
Modified 2015-09-19T00:00:00

Description

<p>WordPress 在编辑文章内容时允许使用简码(shorcodes)来表示资源(图片,链接等)。WordPress 中开启了白名单机制去过滤 HTML 标签,只有在白名单规则里的标签,才允许被使用,并且会使用专用脚本 "KSES" 去检测和过滤这些 HTML 标签。这里需要说明的是,WordPress 对 HTML 标签的检测和过滤发生在将内容插入数据库时,而简码的解析渲染发生在将内容输出到页面时,下面简单用例子说明一下两个处理过程的差别,编辑文章插入内容为:</p><pre class="lang-html" data-lang="html">TEST!!![caption width="1" caption='<a href="' ">]</a><a>xxxxxx</a></pre><p>因插入的内容包含完整且符合白名单规则的 HTML 标签,而简码 caption(<a href="http://codex.wordpress.org/Caption_Shortcode" target="_blank">caption简码说明</a>) 并不包含在 "KSES" 检测的内容里,最后输出内容到前台时简码解析后会被渲染为:</p><pre class="lang-html" data-lang="html"><p>TEST!!!<figure style="width: 1px;" class="wp-caption alignnone"><figcaption class="wp-caption-text"><a href="</figcaption></figure></a><a>xxxxxx</a></p></pre><p>由于在 "KSES" 过滤检测时只关 HTML 标签,对简码并不进行检测,又因简码中属性都以 KEY=VALUE 的形式出现,用单引号(')或者双引号(")包裹值 VALUE ,因此在 TEST!!![caption width="1" caption='<a href="' ">]</a><a>xxxxxx</a> 这段内容中,简码 caption 有两个属性,分别为:</p><pre class="">width: 1    caption: <a href="</pre><p>而后半部分的 <a href="' ">]</a><a>xxxxxx</a> 又为正常的 HTML 标签闭合形式,因此并不会被 "KSES" 检测过滤后丢弃掉。最终在前台输出时,简码 caption 被解析,使得最后出现 <a> 标签中 href 属性值未闭合的情况。</p><p>因此利用前后处理的差异,可以构造出有利的 payload 形成 XSS:</p><pre class="lang-html" data-lang="html">TEST!!![caption width="1" caption='<a href="' ">]</a><a href="http://onMouseOver='alert(1)'">Click me</a></pre><p>将上面 payload 作为文章内容发布,前端渲染出来的结果为:</p><pre class="lang-html" data-lang="html"><p>TEST!!!<figure style="width: 1px;" class="wp-caption alignnone"><figcaption class="wp-caption-text"><a href="</figcaption></figure></a><a href="http://onMouseOver='alert(1)'">Click me</a></p></pre><p>输出的内容在浏览器中解析成 <a> 标签部分,href 属性值为 </figcaption></figure></a><a href=,而 http:// 由于双斜杠(//)的原因与 onMouseOver='alert(1) 部分断开,因此一个 onmouseover 属性被解析出来,形成 XSS。</p><p><img alt="4.png" src="https://images.seebug.org/contribute/d838cf01-106c-4c35-a580-2b8da82721ae-4.png" data-image-size="2560,1388"><br></p><p>该漏洞(CVE-2015-5714)已经在 WordPress 新版 4.3.1 中修复,具体 patch 部分位于两处,第一处在 wp-includes/shortcodes.php 中的 shortcode_parse_atts() 函数中:</p><pre class="lang-diff" data-lang="diff">--- wp-includes/shortcodes.php +++ wp-includes/shortcodes.php @@ -462,6 +462,15 @@ elseif (isset($m[8])) $atts[] = stripcslashes($m[8]); } + + // Reject any unclosed HTML elements + foreach( $atts as &$value ) { + if ( false !== strpos( $value, '<' ) ) { + if ( 1 !== preg_match( '/^[^<]+(?:<[^>]+>[^<]+)+$/', $value ) ) { + $value = ''; + } + } + } } else { $atts = ltrim($text); }<br></pre><p>新添加的处理过程,过滤了在简码属性值中出现的未闭合 HTML 标签的值。并且解析简码时使用 wp_kses() 函数进行了过滤,确保输出的内容被编码(代码位于 wp-includes/media.php):</p><pre class="lang-diff" data-lang="diff">--- wp-includes/media.php +++ wp-includes/media.php @@ -863,6 +863,8 @@ $content = $matches[1]; $attr['caption'] = trim( $matches[2] ); } + } elseif ( strpos( $attr['caption'], '<' ) !== false ) { + $attr['caption'] = wp_kses( $attr['caption'], 'post' ); }

/**

</pre><p>这样一来就很难利用上面所说的 "KSES"和简码解析前后处理差异 成功构造出能够进行 XSS 的 HTML 标签了。</p>