首頁 >運維 >安全 >利用php序列化和反序列化的語法差異繞過防護

利用php序列化和反序列化的語法差異繞過防護

王林
王林轉載
2020-01-02 16:53:082173瀏覽

利用php序列化和反序列化的語法差異繞過防護

介紹

介紹PHP序列化和反序列化在官方文件如下:

所有php裡面的值都可以使用函數serialize()來傳回一個包含位元組流的字串來表示。 unserialize()函數能夠重新把字串變回php原來的值。序列化一個物件將會保存物件的所有變量,但是不會保存物件的方法,只會保存類別的名字。為了能夠unserialize()一個對象,這個對象的類別必須已經定義過。如果序列化類別A的一個對象,將會傳回一個跟類別A相關,而且包含了對象所有變數值的字串。

簡單說序列化是物件轉換字串的過程,反序列化是字串還原物件的過程。

環境

文章中所述內容使用環境如下:

PHP7.3.1、SDKVSCodeC 與C

#在網路上公開參數反序列化執行流程已經非常詳細,但是對於一些細節地方有一些不足,其中就包括序列化和反序列化之間的語法差異問題。

差異問題

1、序列化

我們透過編譯PHP核心原始碼分析,發現PHP序列化在預設情況下在物件轉換中加入:{和}用來拼接成字串。

[var.c]
Line:882
static void php_var_serialize_intern()
Line:896
if (ce->serialize(struc, &serialized_data, &serialized_length, (zend_serialize_data *)var_hash) == SUCCESS) {
                        smart_str_appendl(buf, "C:", 2);
                        smart_str_append_unsigned(buf, ZSTR_LEN(Z_OBJCE_P(struc)->name));
                        smart_str_appendl(buf, ":\"", 2);
                        smart_str_append(buf, Z_OBJCE_P(struc)->name);
                        smart_str_appendl(buf, "\":", 2);

                        smart_str_append_unsigned(buf, serialized_length);
                        smart_str_appendl(buf, ":{", 2);
                        smart_str_appendl(buf, (char *) serialized_data, serialized_length);
                        smart_str_appendc(buf, '}');
                    }
Line:952
smart_str_appendl(buf, ":{", 2);
Line:995
smart_str_appendc(buf, '}');

咱們來看上面這段程式碼,PHP會使用smart_str_appendl為序列化字串前後拼接:{和},從var.c的第882行開始進入序列化邏輯。在第896行進行序列化字串拼接,第952行和第995行,對於內嵌方法進行拼接。

2、反序列化

反序列化是將序列化的字串,依照某一語法規則轉換還原。

[var_unserialize.c]
Line:655
static int php_var_unserialize_internal()

Line:674{
    YYCTYPE yych;    
    static const unsigned char yybm[] = {          
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
        128, 128, 128, 128, 128, 128, 128, 128, 
        128, 128,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
          0,   0,   0,   0,   0,   0,   0,   0, 
    };
    if ((YYLIMIT - YYCURSOR) < 7) YYFILL(7);
    yych = *YYCURSOR;    
    switch (yych) {    
    case &#39;C&#39;:    
    case &#39;O&#39;:    goto yy4;    
    case &#39;N&#39;:    goto yy5;    
    case &#39;R&#39;:    goto yy6;    
    case &#39;S&#39;:    goto yy7;    
    case &#39;a&#39;:    goto yy8;    
    case &#39;b&#39;:    goto yy9;    
    case &#39;d&#39;:    goto yy10;    
    case &#39;i&#39;:    goto yy11;    
    case &#39;o&#39;:    goto yy12;    
    case &#39;r&#39;:    goto yy13;    
    case &#39;s&#39;:    goto yy14;    
    case &#39;}&#39;:    goto yy15;    
    default:    goto yy2;
    }

Line:776
yy15:
    ++YYCURSOR;
    {    /* this is the case where we have less data than planned */
    php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data");    
return 0; /* not sure if it should be 0 or 1 here? */
}

透過核心程式碼能夠看到第655行進入反序列化,反序列化是利用詞法掃描,判斷各項符號轉換對應物件。能夠看到反序列化中對於}進行了處理,處理中只是對計數器加一並沒有其他操作。

實際作用

反序列化語法的差異,對於安全防護設備判斷反序列化產生很大的影響。在Snort中,有段規則如下:

alert tcp any any -> any [80,8080,443] (uricontent:".php"; pcre:"/\{\w:.+?\}/"; sid:1; 
msg:php_serialize;)

在攻擊載荷中可以使用大多數字元來代替{},從而導致規則失效。

總結

在紅隊攻擊中可以利用PHP序列化和反序列化語法差異,從而達到繞過防護的目的。

在藍隊防禦中建議考慮定義中所述不會保存物件的方法,只會保存類別的名字。 ,攔截保存類別的名字,以及語法中相同的字元例如冒號進行防禦。

相關文章教學分享:網站安全教學

#

以上是利用php序列化和反序列化的語法差異繞過防護的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:freebuf.com。如有侵權,請聯絡admin@php.cn刪除