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

Date: 2017-03-10

0x00 漏洞概述

漏洞簡介

近日 Wordpress 發布了 4.7.3,在此次更新中修復了一個利用惡意 MP3 文件的 XSS 漏洞,該漏洞觸發點有兩處,一個是在服務端輸出數據時,另外一個在前端渲染數據時。觸發該漏洞有兩個前提條件:攻擊者需要具有上傳 MP3 文件的權限或者能夠誘導管理員進行上傳,并且還需具有發表文章并添加播放列表的權限。成功觸發該漏洞后攻擊者可以利用 XSS 進行獲取用戶信息等敏感操作。

漏洞影響

利用 XSS 進行獲取用戶信息等敏感操作

觸發前提:

  1. 具有上傳 MP3 文件的權限或者能夠誘導管理員進行上傳
  2. 具有發表文章并添加播放列表的權限

影響版本: < 4.7.3

0x01 漏洞復現

環境搭建

只需下載安裝相應版本 Wordpress 即可

復現

在看具體的漏洞之前,我們先簡單了解一下 ID3,它是一個元數據容器,用來存儲 MP3 文件的一些相關信息,例如名稱、作者和專輯等等,我們可以用一個Python 的第三方庫 eyeD3 來對 MP3 文件進行解析和修改,首先我們來解析一下漏洞作者給出的 PoC

id3_xss.png)

可以看到在 title 中存在著 XSS 代碼。

接下來我們執行以下幾個步驟來觸發漏洞:

  1. 登陸后臺上傳該 MP3 文件
  2. 創建文章
  3. 創建播放列表,把惡意 MP3 文件加入到播放列表中
  4. 發布文章

然后我們點開新發布的文章,發現會彈窗兩次:

xss1.png)

xss2.png)

我們先看第一次彈框,刷新頁面,開啟動態調試,我們看wp-includes/media.phpwp_playlist_shortcode函數,該函數用于創建播放列表,在第2112-2118行中有這樣一段代碼:

<noscript>
    <ol><?php
    foreach ( $attachments as $att_id => $attachment ) {
        printf( '<li>%s</li>', wp_get_attachment_link( $att_id ) );
    }
    ?></ol>
</noscript>

這里將 wp_get_attachment_link的值直接輸出到了頁面上,跟進這個函數:

function wp_get_attachment_link( $id = 0, $size = 'thumbnail', $permalink = false, $icon = false, $text = false, $attr = '' ) {
    $_post = get_post( $id );

    if ( empty( $_post ) || ( 'attachment' !== $_post->post_type ) || ! $url = wp_get_attachment_url( $_post->ID ) ) {
        return __( 'Missing Attachment' );
    }

    if ( $permalink ) {
        $url = get_attachment_link( $_post->ID );
    }

    if ( $text ) {
        $link_text = $text;
    } elseif ( $size && 'none' != $size ) {
        $link_text = wp_get_attachment_image( $_post->ID, $size, $icon, $attr );
    } else {
        $link_text = '';
    }

    if ( '' === trim( $link_text ) ) {
        $link_text = $_post->post_title;
    }

    if ( '' === trim( $link_text ) ) {
        $link_text = esc_html( pathinfo( get_attached_file( $_post->ID ), PATHINFO_FILENAME ) );
    }

    ... 

    return apply_filters( 'wp_get_attachment_link', "<a href='" . esc_url( $url ) . "'>$link_text</a>", $id, $size, $permalink, $icon, $text );
}

最終的返回值與$link_text有關,根據調試來看,$link_text最終取的是 $_post->post_title$_post->post_title的值如下:

post_title.png)

可見$link_text即為我們構造的 payload (圖中由于 payload 過長并沒有顯示完全),接下來繼續跟進apply_filters( 'wp_get_attachment_link', "<a href='" . esc_url( $url ) . "'>$link_text</a>", $id, $size, $permalink, $icon, $text );函數直接返回了$value

apply_filters.png)

value.png)

所以最終輸出到兩個<li>之間的內容是<a >Summer of Pwnage </noscript><script>alert(document.cookie);</script></a> ,其中</noscript>對前面的標簽進行了閉合,整個過程中并沒有進行過濾,從而使 payload 得以被執行:

view_source.png)

接下來我們來看第二個彈框,在/wp-includes/js/mediaelement/wp-playlist.js第91-105行:

renderTracks : function () {
        var self = this, i = 1, tracklist = $( '<div class="wp-playlist-tracks"></div>' );
        this.tracks.each(function (model) {
            if ( ! self.data.images ) {
                model.set( 'image', false );
            }
            model.set( 'artists', self.data.artists );
            model.set( 'index', self.data.tracknumbers ? i : false );
            tracklist.append( self.itemTemplate( model.toJSON() ) );
            i += 1;
        });
        this.$el.append( tracklist );

        this.$( '.wp-playlist-item' ).eq(0).addClass( this.playingClass );
    },

renderTrack函數的作用是對播放列表進行填充,這里使用 jQuery this.$el.appendtracklist的內容輸出的頁面中, tracklistself.itemTemplate(model.toJSON())有關,我們通過console.log來看一下self.itemTemplate(model.toJSON())的值:

console_log.png)

可以看到wp-playlist-item-title中有我們的 payload,這個過程也并沒有進行過濾或轉義,從而導致了 XSS 的產生。

總的來說,這個漏洞在利用上雖然不用擔心 payload 的構造問題,但是由于需要特殊權限以及文件上傳,筆者覺得還是略顯雞肋的。

0x02 補丁分析

其實不難看出漏洞的根源在于上傳文件后沒有對元數據的合法性進行檢測,所以 Wordpress 官方做了如下補丁:

patch.png)

在讀取文件元數據的地方對元數據進行過濾。

0x03 參考


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