作者:
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字元時,則會出現由於資料庫不識別字元而產生截斷效果。示例如下,使用特殊字元:
#!sql
UPDATE `wp_comments` SET `comment_content` = 'stefanie[特殊字元]555555 ' WHERE `wp_comments`.`comment_ID` =12;
在資料庫中插入的結果為stefanie,特殊字元及後面的內容並未插入資料庫。
UTF-8編碼在對於不同的字元區域,編碼所佔用的位元組數各不相同。
mysql utf-8編碼對於佔用四個位元組的字元無法識別,使用上圖範圍內的編碼均可達到目的,如
當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 ">
截斷效果如下圖:
該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函式來統計字元個數的結果,兩個值的計量單位不同,一個是位元組數,一個是字元的個數。
在函式“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的幫助~
本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!