Wordpress 評論功能Xss 始末

wyzsk發表於2020-08-19
作者: Stefanie · 2015/05/11 10:03

近期Wordpress自身程式評論功能的Xss在微博上沸沸揚揚,而其中的修復過程也可謂一波三折,接下來由我為大家一一講述。

WordPress <4.1.2 xss


該問題來自於特殊字元截斷,mysql官網描述“The character set named utf8 uses a maximum of three bytes per character and contains only BMP characters”,mysql在使用utf8編碼時,單個字元大小上限為3位元組,當出現超過3個位元組的utf8字元時,則會出現由於資料庫不識別字元而產生截斷效果。示例如下,使用特殊字元:

enter image description here

#!sql
UPDATE `wp_comments` SET `comment_content` = 'stefanie[特殊字元]555555 ' WHERE `wp_comments`.`comment_ID` =12;

在資料庫中插入的結果為stefanie,特殊字元及後面的內容並未插入資料庫。

UTF-8編碼在對於不同的字元區域,編碼所佔用的位元組數各不相同。

enter image description here

mysql utf-8編碼對於佔用四個位元組的字元無法識別,使用上圖範圍內的編碼均可達到目的,如

enter image description here enter image description here enter image description here

當mysql的編碼為utf-8mb4或者latin1時,則可以對此類字元進行識別,另外,當mysql開啟strict mode時候,會更加嚴格地處理,確保在資料有效儲存之前進行阻止,也就不會產生該問題。

關於漏洞利用,Wordpress只允許評論中出現白名單內的標籤和對應的屬性,而且要保證標籤的完整性,比如尖括號的閉合。Poc如下:

#!html
<abbr title="123 onmouseover=alert(1) 特殊字元">

可以看出,onmouseover是在雙引號內作為title屬性的值出現的,在插入資料庫時由於特殊字元產生截斷,右側的引號不會插入,在輸出時,左側引號會被轉義,因而可使得onmouseover成功解析。 相關連結:

(1] http://xteam.baidu.com/?p=177 (2]https://cedricvb.be/post/wordpress-stored-xss-vulnerability-4-1-2/ (3]https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html

WordPress <=4.2 xss


mysql type=TEXT時,TEXT的最大長度為64kb,當發生資料庫的插入操作時,則會將大於64KB的部分拋棄,此時也造成了一種截斷,而wordpress儲存評論的欄位的type正是TEXT。

該漏洞於上一漏洞同理,只是截斷的方式有所不同,poc如下:

#!html
<abbr title="123 onmouseover=alert(1) 此處增加無用字元至64KB ">

截斷效果如下圖:

enter image description here

enter image description here

該poc僅限IE使用,而Klikki Oy團隊給出了相容多種瀏覽器的POC,chrome,IE,firefox測試成功。

#!html
stefanie<a title='x onmouseover=alert(unescape(/hello%20world/.source))
style=position:absolute;left:0;top:0;width:5000px;height:5000px  此處用特殊字元或者長度截斷均可'></a>

相關連結: (1] http://xteam.baidu.com/?p=177 (2] https://speakerdeck.com/mathiasbynens/hacking-with-unicode

WordPress <=4.2.1 xss


4.2.1版本主要針對長度截斷進行了修復,在wp-includes\wp-db.php get_col_length函式中增加了資料庫每種型別欄位的長度限制,並和傳入資料長度做了比較。

#!php
protected function process_field_lengths( $data, $table ) {
foreach ( $data as $field => $value ) {
    if ( '%d' === $value['format'] || '%f' === $value['format'] ) {
            // We can skip this field if we know it isn't a string.
            // This checks %d/%f versus ! %s because it's sprintf() could take more.
            $value['length'] = false;
        } else {
            $value['length'] = $this->get_col_length( $table, $field );
            if ( is_wp_error( $value['length'] ) ) {
                return false;
            }
        }
        if ( false !== $value['length'] && mb_strlen( $value['value'] ) > $value['length'] ) {
            return false;
        }
        $data[ $field ] = $value;
    }
    return $data;
}

$value['length']來自於get_col_length,text型別長度上限為65535位元組,mb_strlen( $value['value'] )是mb_strlen函式來統計字元個數的結果,兩個值的計量單位不同,一個是位元組數,一個是字元的個數。

enter image description here

在函式“mb_strlen”中,一個多位元組字元統計個數為1。在不同的編碼中,一個字元的大小可以為多個位元組(多位元組字元),所以我們可以構造一個字串,讓字元的個數小於65535而字串的位元組數大於65535位元組,從而滿足如下條件:

#!sql
if ( false !== $value['length'] && mb_strlen( $value['value'] ) > $value['length'] ) {
                return false;}

當php向資料庫中插入資料時,由於位元組數超過了text型別的長度上線65535位元組,所以字串會被截斷,導致了之前的xss可以重新被利用。 相關連結:

(1] http://xteam.baidu.com/?p=198 (2] http://php.net/manual/zh/function.mb-strlen.php

WordPress 4.2.2 中總體修復情況


在4.2.2版本中引入了兩個變數:

$truncate_by_byte_length : 是否進行位元組長度驗證 $needs_validation : 是否進行多位元組字元合規性驗證以及長度驗證 wp422/wp-includes/wp-db.php 2626行

#!sql
if ( $truncate_by_byte_length ) {
    mbstring_binary_safe_encoding();
    if ( false !== $length && strlen( $value['value'] ) > $length ) {
        $value['value'] = substr( $value['value'], 0, $length );
    }
    reset_mbstring_encoding();
    if ( ! $needs_validation ) {
        continue;
    }
}

該處解決了4.2.1中用mb_strlen來測量實際長度和規定長度上線之間的比較造成的問題,改用了strlen來測量長度,並對超長部分進行切割,因而攔截了4.2.1補丁bypass造成的xss。

#!sql
$regex = '/
    (
        (?: [\x00-\x7F]                  # single-byte sequences   0xxxxxxx
        |   [\xC2-\xDF][\x80-\xBF]       # double-byte sequences   110xxxxx 10xxxxxx
        |   \xE0[\xA0-\xBF][\x80-\xBF]   # triple-byte sequences   1110xxxx 10xxxxxx * 2
        |   [\xE1-\xEC][\x80-\xBF]{2}
        |   \xED[\x80-\x9F][\x80-\xBF]
        |   [\xEE-\xEF][\x80-\xBF]{2}';
if ( 'utf8mb4' === $charset ) {
    $regex .= '
        |    \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
        |    [\xF1-\xF3][\x80-\xBF]{3}
        |    \xF4[\x80-\x8F][\x80-\xBF]{2}
    ';
}
$regex .= '){1,40}                          # ...one or more times
    )
    | .                                  # anything else
    /x';
$value['value'] = preg_replace( $regex, '$1', $value['value'] );
if ( false !== $length && mb_strlen( $value['value'], 'UTF-8' ) > $length ) {
    $value['value'] = mb_substr( $value['value'], 0, $length, 'UTF-8' );
}
continue;
}

可以看出,對於utf8編碼時,僅僅會取範圍內的小於等於3位元組UTF8字元,取出之後,會按照當前多位元組字元的編碼長度再次確認。此處是針對小於4.2版本用四位元組utf8字元插入資料庫截斷以及其他特殊字元截斷而修補的。 相關連結:

(1] https://wordpress.org/news/2015/05/wordpress-4-2-2/

致謝:感謝evil_xi4oyu的每一次悉心指點和yaseng的幫助~

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章