首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 5.3.9 遠端程式碼執行漏洞 (CVE-2012-0830) 深度剖析

     編輯:PHP 相關知識

漏洞背景與修補演進

還記得先前討論的 PHP Hash Collisions DoS 漏洞嗎?最初開發團隊的修補方案是:當輸入變數超過 max_input_vars 限制時,直接拋出 E_ERROR 導致 PHP 終止執行。後來為了採取更輕量的處理方式,機制改為發出 E_WARNING 警告並停止往目標陣列新增元素,但程式流程繼續執行。這項變更隨 PHP 5.3.9 釋出。

新修補邏輯引入的嚴重缺陷

這項善意的修改卻在 PHP 5.3.9 中引入一個嚴重問題(已在 5.3.10 修復),由 Stefan Esser 發現。請看 php_register_variable_ex 的最終修補代碼:

while (1) {
    if (zend_symtable_find(symtable1, escaped_index, index_len + 1, (void **) &gpc_element_p) == FAILURE
        || Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { //(3)
        if (zend_hash_num_elements(symtable1) <= PG(max_input_vars)) { // (4)
            if (zend_hash_num_elements(symtable1) == PG(max_input_vars)) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded %ld. ...", PG(max_input_vars)); // (1)
            }
            MAKE_STD_ZVAL(gpc_element);
            array_init(gpc_element);
            zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p);
        }
        //......
    }
    //.....
    symtable1 = Z_ARRVAL_PP(gpc_element_p); // (2)
    goto plain;
}

觸發條件分析

當註冊一個陣列變數(如 GET 中的 a[]=2),且該變數剛好是第 max_input_vars 個時,會觸發警告 (1),此時行為正常。

但若註冊的陣列變數是第 max_input_vars + 1 個,gpc_element_p 將成為未初始化指標。由於邏輯繼續執行並走到位置 (2),導致解參考未初始化指標——系統崩潰。若伺服器啟用 Core Dump,攻擊效果極為明顯,可用於 DoS 攻擊。

更嚴重的任意程式碼執行風險

外層迴圈處理類似 a[b]=2 的鍵值對時會執行兩次:第一次插入 a[],第二次往 a[] 插入 b。關鍵在判斷點 (3):若目標陣列找不到元素,或該元素非陣列,流程同樣會流向 (2),引發問題。

攻擊載荷構造範例

假設預設 max_input_vars=1000,發送以下 POST 資料:

1=1&1=2&..........&999=1&x="我是惡意的string"&x[0]=

逐步執行流程

  1. 1 到 999 正常插入
  2. x 為第 1000 個元素,觸發警告並正常插入
  3. 插入 x[0] 時,(3) 判斷發現非 Array 進入 if 區塊,但 (4) 判斷失敗,流程流向 (2)
  4. 此時 gpc_element_p 指向 x —— 我們偽造的字串

核心資料結構利用:zval 與 HashTable

PHP 內部使用 zval 結構儲存變數:

struct _zval_struct {
    zvalue_value value;  /* value */
    zend_uint refcount__gc;
    zend_uchar type;     /* active type */
    zend_uchar is_ref__gc;
};

zvalue_value 是聯合體:

typedef union _zvalue_value {
    long lval;
    double dval;
    struct { char *val; int len; } str;
    HashTable *ht;       /* hash table value */
    zend_object_value obj;
} zvalue_value;

當字串被當作 HashTable 解讀時,記憶體佈局會對應:

typedef struct _hashtable {
    uint nTableSize;
    uint nTableMask;
    uint nNumOfElements;
    ulong nNextFreeElement;
    Bucket *pInternalPointer;
    Bucket *pListHead;
    Bucket *pListTail;
    Bucket **arBuckets;
    dtor_func_t pDestructor;  // 關鍵函式指標
    zend_bool persistent;
    unsigned char nApplyCount;
    zend_bool bApplyProtection;
    #if ZEND_DEBUG
    int inconsistent;
    #endif
} HashTable;

任意程式碼執行鏈

HashTable 中的 pDestructor 是函式指標,當陣列元素被刪除時會被呼叫。攻擊者可精心構造字串內容,控制 pDestructor 指向任意位址,再誘使 PHP 刪除陣列元素,即可實現任意程式碼執行。

修補建議

  • 立即升級至 PHP 5.3.10 或更新版本
  • 若無法升級,設定 max_input_vars 為較小值並監控異常請求
  • 部署 WAF 規則過濾超大量輸入變數的請求
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved