<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/papers/8988

            By: RickGray (知道創宇404安全實驗室)

            近日,WordPress 發布了新版本4.3.1,其中修復了幾個嚴重的安全問題,其中包含了由 Check Point 所提交的一個跨站腳本漏洞(CVE-2015-5714)和一個權限提升漏洞(CVE-2015-5715)。

            8月初,Check Point 在其官方博客上發表了一篇關于 《WordPress漏洞三部曲》 系列文章的第一部,在這篇文章中,提及了 WordPress 在 4.2.3 版本中修復的一個越權漏洞,這里對此就不再做具體分析和說明,相關細節詳情可參考原文和 phithon 所寫的 《Wordpress4.2.3提權與SQL注入漏洞(CVE-2015-5623)分析》

            這里主要說明的是 "三部曲" 中的第三部,也就是 Check Point 在其博客上公開的關于 WordPress 4.3.1 版本中所修復的另一個越權漏洞和一個跨站腳本漏洞(原文在這里)。

            0x01 "KSES"與簡碼過濾差異化導致的 XSS


            首先來看看跨站腳本漏洞。WordPress 在編輯文章內容時允許使用簡碼(shorcodes)來表示資源(圖片,鏈接等)。WordPress 中開啟了白名單機制去過濾 HTML 標簽,只有在白名單規則里的標簽,才允許被使用,并且會使用專用腳本 "KSES" 去檢測和過濾這些 HTML 標簽。這里需要說明的是,WordPress 對 HTML 標簽的檢測和過濾發生在將內容插入數據庫時,而簡碼的解析渲染發生在將內容輸出到頁面時,下面簡單用例子說明一下兩個處理過程的差別,編輯文章插入內容為:

            #!html
            TEST!!![caption width="1" caption='<a href="' ">]</a><a>xxxxxx</a>
            

            因插入的內容包含完整且符合白名單規則的 HTML 標簽,而簡碼 [caption]caption簡碼說明) 并不包含在 "KSES" 檢測的內容里,最后輸出內容到前臺時簡碼解析后會被渲染為:

            #!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>
            

            由于在 "KSES" 過濾檢測時只關 HTML 標簽,對簡碼并不進行檢測,又因簡碼中屬性都以 KEY=VALUE 的形式出現,用單引號'或者雙引號"包裹值Value,因此在 TEST!!![caption width="1" caption='<a href="' ">]</a><a>xxxxxx</a> 這段內容中,簡碼 caption 有兩個屬性,分別為:

            width: 1
            caption: <a href="
            

            而后半部分的 <a href="' ">]</a><a>xxxxxx</a> 又為正常的 HTML 標簽閉合形式,因此并不會被 "KSES" 檢測過濾后丟棄掉。最終在前臺輸出時,簡碼 caption 被解析,使得最后出現 <a> 標簽中 href 屬性值未閉合的情況。

            因此利用前后處理的差異,可以構造出有利的 payload 形成 XSS:

            #! html
            TEST!!![caption width="1" caption='<a href="' ">]</a><a href="http://onMouseOver='alert(1)'">Click me</a>
            

            將上面 payload 作為文章內容發布,前端渲染出來的結果為:

            #!html
            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>
            

            輸出的內容在瀏覽器中解析成 <a> 標簽部分,href 屬性值為 </figcaption></figure></a><a href=,而 http:// 由于雙斜杠(//)的原因與 onMouseOver='alert(1) 部分斷開,因此一個 onmouseover 屬性被解析出來,形成 XSS。

            該漏洞(CVE-2015-5714)已經在 WordPress 新版 4.3.1 中修復,具體 patch 部分位于兩處,第一處在 wp-includes/shortcodes.php 中的 shortcode_parse_atts() 函數中:

            --- 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);
                }
            

            新添加的處理過程,過濾了在簡碼屬性值中出現的未閉合 HTML 標簽的值。并且解析簡碼時使用 wp_kses() 函數進行了過濾,確保輸出的內容被編碼(代碼位于 wp-includes/media.php):

            --- 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' );
                }
            
                /**
            

            這樣一來就很難利用上面所說的 "KSES"和簡碼解析前后處理差異 成功構造出能夠進行 XSS 的 HTML 標簽了。

            0x02 權限檢查遺漏導致越權操作


            Check Point 在文章中還提到了另一個越權操作(與 part1 的越權不同),可以使得不具有文章發布權限的用戶通過 XMLRPC 操作將自己的文章狀態修改為 private,并可將其置頂 (WordPress 4.3.0版本中已將其修復,未設密碼的私有文章不可置頂)。

            越權操作位于 XMLRPC 文章編輯操作中,涉及文件 /wp-includes/class-wp-xmlrpc-server.php (5042-5327) 其中關鍵代碼分析:

            #!php
                public function mw_editPost( $args ) {
                    $this->escape( $args );    
            
                    $post_ID        = (int) $args[0];  // 獲取需要編輯的文章ID (用戶所屬)
                    $username       = $args[1];  // 從請求的xml中獲取用戶名
                    $password       = $args[2];  // 從請求的xml中獲取用戶密碼
                    $content_struct = $args[3];  // 從請求的xml中獲取結構
                    $publish        = isset( $args[4] ) ? $args[4] : 0;    
            
                    (...省略)
                    if ( isset( $content_struct["{$post_type}_status"] ) ) {
                        switch( $content_struct["{$post_type}_status"] ) {
                            case 'draft':
                            case 'pending':
                            case 'private':
                            case 'publish':
                                $post_status = $content_struct["{$post_type}_status"];  // 數據庫中存儲的文章類型為post,所以取的是xml中 post_status 參數的值
                                break;
                            default:
                                $post_status = $publish ? 'publish' : 'draft';
                                break;
                        }
                    }   
            

            首先處理程序獲取提交參數并驗證當前用戶權限,對于草稿或者未審核的文章,其數據庫中存儲的文章類型為 post,所以在取值 $content_struct["{$post_type}_status"] 時,獲取的是提交參數中 post_status 的值。

            #!php
                    (...省略)
                    // 當用戶不具有文章發布權限時,`publish` 操作會被禁止
                    // 但是這里并沒有限制 `private` 的情況
                    // 所以若xml中 post_status 參數值為 private 則跳過檢查
                    if ( ('publish' == $post_status) ) {
                        if ( ( 'page' == $post_type ) && ! current_user_can( 'publish_pages' ) ) {
                            return new IXR_Error( 401, __( 'Sorry, you do not have the right to publish this page.' ) );
                        } elseif ( ! current_user_can( 'publish_posts' ) ) {
                            return new IXR_Error( 401, __( 'Sorry, you do not have the right to publish this post.' ) );
                        }
                    }
            

            接著,程序會驗證其提交的需要更新的文章狀態。當用戶通過 XMLRPC 進行文章編輯時,若想發布一篇未發布的文章時,會檢查用戶是否具有文章發布的權限。但是該檢查判斷了將文章狀態變為發布狀態的情況下(post_status == publish),而針對將文章狀態變為私有狀態的情況代碼中并沒有進行檢查。程序上的判斷疏忽,致使我們可以使用一個不具有文章發布權限的帳號將自己一篇 未通過審核 或者 存于垃圾箱 的文章的轉臺通過該過程將其改為私有(private),讓該文章以私文的形式在前臺顯示出來,管理員以及其他具有權限的用戶都能瀏覽到。

            另一個需要說明的點就是,通過 XMLRPC 操作編輯文章時,可以將文章進行置頂,具體代碼為:

            #!php
                    (...省略)
            
                    // 將文章置頂(4.3.0 版本后不能將未設密碼的私有文章置頂)
                    // Only posts can be sticky
                    if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {
                        $data = $newpost;
                        $data['sticky'] = $content_struct['sticky'];
                        $data['post_type'] = 'post';
                        $error = $this->_toggle_sticky( $data, true );
                        if ( $error ) {
                            return $error;
                        }
                    }
            

            但是該問題在 WordPress 4.3.0 版本中已經得到了限制:

            #!php
                private function _toggle_sticky( $post_data, $update = false ) {
                    $post_type = get_post_type_object( $post_data['post_type'] );    
            
                    // Private and password-protected posts cannot be stickied.
                    if ( 'private' === $post_data['post_status'] || ! empty( $post_data['post_password'] ) ) {
                        // 如果需要置頂的文章為私有狀態,并且未設訪問密碼,不能將其置頂,并自動取消之前的置頂狀態
                        // Error if the client tried to stick the post, otherwise, silently unstick.
                        if ( ! empty( $post_data['sticky'] ) ) {
                            return new IXR_Error( 401, __( 'Sorry, you cannot stick a private post.' ) );
                        }    
            
                        if ( $update ) {
                            unstick_post( $post_data['ID'] );
                        }
                    } elseif ( isset( $post_data['sticky'] ) )  {
                        // 如果需要置頂的文章為私有狀態,并且設有訪問密碼,且具有編輯其他文章的權限,則將其所置頂的文章置頂
                        if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) {
                            return new IXR_Error( 401, __( 'Sorry, you are not allowed to stick this post.' ) );
                        }    
            
                        $sticky = wp_validate_boolean( $post_data['sticky'] );
                        if ( $sticky ) {
                            stick_post( $post_data['ID'] );
                        } else {
                            unstick_post( $post_data['ID'] );
                        }
                    }
                }
            

            未設密碼訪問的私有文章已經無法再通過 XMLRPC 編輯文章操作將文章置頂。

            下面通過示例來說明如何通過 XMLRPC 編輯文章操作將文章狀態修改為 私有。為了方便演示,這里事先注冊好一個用戶(投稿者權限,其投稿的文章狀態為 pending),并提交一篇文章投遞申請:

            查看一下待審文章在數據庫中的狀態:

            然后根據上面所分析的權限提升細節,構造 payload ,將此待審文章狀態更改為 private

            可以看到返回消息中提示置頂私有文章失敗,這是因為測試時使用的 WordPress 4.3.0 版本,該版本中已經修復了私有文章任意置頂的問題。

            然后查看一下通過 XMLRPC 編輯文章后數據庫中待審核文章的狀態:

            數據庫中文章狀態已經變為私有,再到前臺查看首頁:

            由投稿用戶提交的待審核文章已經變為私有狀態顯示在前臺頁面中,并且管理員能看到所有的私有文章。

            0x03 結合跨站腳本和越權操作


            本文一開時已經分析過了如何通過利用 "KSES"與簡碼過濾差異化造成一個存儲型的前臺 XSS,加上第二節所演示越權編輯文章狀態的過程,結合這兩個漏洞,可以使得站點上具有一點權限的用戶(投稿即可),投遞包含惡意內容的文章,然后利用越權操作將文章顯示到前臺,對能夠瀏覽到該文章的用戶(包括管理員)進行 XSS 攻擊。

            下面我們模擬一下上面敘述的流程。首先投遞一篇包含 XSS payload 的文章,利用 "KSES"與簡碼渲染操作的差異使得內容在前臺渲染后能夠形成 XSS,將文章內容設置為:

            XSS LOL!!![caption width='1' caption='<a href="' ">]</a><a href="http://onMouseOver='alert(/xss/)' style='display:block;position:absolute;top:0px;left:0px;margin-left:-1000px;margin-top:-1000px;width:99999px;height:99999px;'"></a>
            

            然后利用 XMLRPC 遍歷文章得到提交的待審核文章的 id,這里得到待審核文章 id 為:28,在構造 payload 將其未發布狀態改為私有:

            利用 XMLRPC 文章編輯成功修改文章狀態為私有后,訪問前臺查看結果:

            0x04 三部曲之歌


            回顧 Check Point 所發布的《WordPress漏洞三部曲》,可以知道 WordPress 在 4.2.2 版本中含有其提交的所有漏洞,包括了 競爭條件下權限提升文章恢復導致SQL注入"KSES"與簡碼過濾差異化導致的XSS權限檢查遺漏導致越權操作 等。通看起來,如果在 WordPress 4.2.2 版本下,這些漏洞都能在以 競爭條件下權限提升 作為起始,完成后面的攻擊,實現一個超低權限用戶下進行 SQL 注入、XSS 攻擊的操作。我將 Check Point 在 part1 和 part3 中所提到的漏洞利用方法綜合到一起,寫出了 all in one 的 PoC,其中 競爭條件下權限提升 的過程使用 phithon 文章中所提及的使用兩個訂閱用戶來解決 7 天攻擊周期的限制。

            為了達到 all in one 的演示結果,將 WordPress 測試環境更換為 4.2.2 版本,并事先準備兩個訂閱用戶 guest:guest888test:test888,然后運行 PoC:

            PoC 提示成功后,管理員訪問前臺,文章成功置頂并包含惡意代碼:

            0x05 總結


            這里不得不佩服洞主對 WordPress 熟悉程度和漏洞挖掘的思路。

            雖然 WordPress 在幾個連續的版本中修復了這些漏洞,但在非最新版本中(< 4.3.1)中,這些漏洞還是能夠在特定場景下得以利用。尤其是在 4.2.2 版本中,能夠利用 "三部曲" 中所提及的漏洞進行一系列的攻擊操作。

            這些看似雞肋的漏洞在我看來并不雞肋,雞肋只是因為還未找到合適的應用場景而已。

            0x06 參考鏈接


            原文出處:http://blog.knownsec.com/2015/09/wordpress-vulnerability-analysis-cve-2015-5714-cve-2015-5715/

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线