(此漏洞廠商已解決)
緣起
我在公司是做應(yīng)用安全的,平時主要關(guān)注于“防”。由于工作繁忙,少有時間來嘗試攻擊別人的系統(tǒng)。但是攻防本一家,做了那么多防治安全問題的解決方案,也該試試手,把防護(hù)經(jīng)驗(yàn)用在攻擊中,看看效果如何?
第一個問題,進(jìn)行什么類型的攻擊?時間有限,已經(jīng)是深夜了,為了節(jié)省時間,還是選跨站腳本注入吧。遍地都是XSS,哪個上規(guī)模的網(wǎng)站上沒有XSS問題反倒怪了。第二個問題是,選什么網(wǎng)站?XSS漏洞分布廣,攻擊門檻低,為了保證有一定的挑戰(zhàn)性,我選擇了淘寶。第三個問題,攻擊哪個頁面的哪個字段?簡單起見,就選擇首頁的第一個輸入框,商品搜索。
好,動手。我打開IE來到淘寶。
投石問路
首先,我在商品搜索框中輸入swx<>&"'。其中字串swx是為了方便在頁面輸出中查找注入的內(nèi)容,后面的幾個亂字符,則是HTML中可能會引起跨站腳本的常見敏感字符。點(diǎn)擊搜索,結(jié)果頁面出來了,沒有什么語法或者顯示上的錯誤。有一個現(xiàn)象,就是頁面的標(biāo)題,以及頁面最上方的搜索框里面,搜索關(guān)鍵字已經(jīng)變成了swx<>&"',除小于號之外的四個字符都被HTML編碼了。這說明開發(fā)人員有點(diǎn)心虛,或者缺乏統(tǒng)一的編碼解決方案,導(dǎo)致在不同的地方編碼了兩次,多余了,不影響安全,但是改變了用戶的搜索關(guān)鍵字。
第一步注入沒有明顯結(jié)果很正常,只是扔幾個怪字符過去,探探路而已。第二步要做的事情是分析HTML的源代碼,看看我們給的那幾個探路的字符,到了敵人那里下場如何?
蛛絲馬跡
選擇View Source,打開HTML源代碼,然后搜索swx關(guān)鍵字,UltraEdit顯示有71處發(fā)現(xiàn),還好不算太多,一個一個看。基本上,注入的那幾個特殊字符在頁面的各處都被施加了HTML編碼,包括head里面、HTML body里面、JavaScript字符串值里面等等。有些地方還確實(shí)被HTML編碼了兩次,如前面所推測。
值得注意的是,單引號則始終沒有被編碼,這是唯一保持原樣的字符。但是因?yàn)轫撁娴拈_發(fā)人員在HTML里面和JavaScript里面始終使用雙引號來包住字符,單引號無法逃出雙引號構(gòu)成的牢籠,也就不能產(chǎn)生語法意義了。
其實(shí)直接輸入HTML標(biāo)簽的跨站腳本攻擊,比如<script>alert(1)</script>,成功的可能性比較小,除非目標(biāo)網(wǎng)站太爛了。一般只要開發(fā)人員稍微考慮一下安全問題,都會把<>這樣最基本的危險字符干掉。
頁面里用的都是HTML編碼,包括JavaScript代碼里面也是。而JavaScript編碼似乎被忽略了,所以我把目光移向JavaScript里面注入的那段內(nèi)容:
var word = "swx<>&"'";
單引號無辜的呆在那里,似乎在等待援軍??磥淼脟L試更多的特殊字符,目標(biāo),JavaScript。
掩護(hù)
第二波,我只輸入了一個反斜杠字符\。反斜杠是JavaScript里面字符串常量非常重要的一個字符,也就是編碼字符。因?yàn)?span lang="EN-US">JavaScript字符串可以用被單引號或者雙引號括起來的,并且只能是單行的。但是如果字符串的內(nèi)容本身就需要包含引號或者換行符怎么辦?這時就需要進(jìn)行編碼,比如雙引號可以被編碼為\x22或者\"。其中22是雙引號的ASCII編碼值的16進(jìn)制形式。
在商品搜索欄輸入\。點(diǎn)擊搜索按鈕,出問題了。結(jié)果頁面中有JavaScript語法錯誤:Unterminated String Constant,未結(jié)束的字符串常量。
Bingo!
其實(shí)這么簡單的case淘寶沒有測試到,實(shí)在是不應(yīng)該。一個簡單有效的測試做法是,往輸入框里扔上一堆亂字符~`!@#$%^ *()_+-={}|[]\\x22:”;’<>,.?/*,如果在結(jié)果頁面中,這些字符都能原樣的顯示在界面上,沒有任何語法報錯或者頁面顯示上的異常,那么基本上可以說跨站腳本的可能性就比較小了,包括SQL注入漏洞也順帶的被驗(yàn)證了。
第二次注入嘗試后,HTML代碼里是這樣的:var word = "\";。顯然,反斜杠兄弟成功打入敵人內(nèi)部。后面緊跟著的雙引號和反斜杠變成了一個整體的編碼\",雙引號被吃掉了,從而原來用以包住字符串的一對引號只剩下了前一個,所以頁面報“未結(jié)束的字符串常量”錯誤。正因?yàn)榉葱备艿奶厥庑裕魏巫址伎梢员凰幋a。任何字符,當(dāng)然也包括大于號小于號這一對XSS攻擊最基本的字符。
現(xiàn)在,我們試一下,利用JS的這個字符編碼機(jī)制,讓反斜杠掩護(hù)剛才被干掉的大于號、小于號和雙引號字符潛入到word變量的值里面,一旦這三個字符可以潛入敵后,那么腳本執(zhí)行也就有了希望。我們想賦給word變量的值是<>",用反斜杠編碼掩護(hù)之后,變成\x3c\x3e\x22,注意,這里之所以用16進(jìn)制ASCII碼的編碼方式,是因?yàn)?strong style="mso-bidi-font-weight: normal;">\"這樣的編碼中雙引號還是以明文出現(xiàn),會被Web服務(wù)器上的編碼邏輯發(fā)現(xiàn)并干掉。而\x22看起來就非常像良民,逃過檢查的可能性比較大。
輸入\x3c\x3e\x22,點(diǎn)擊搜索,然后在結(jié)果的HTML文本里查找word =得到:
var word = "\x3c\x3e\x22";
三個特殊字符可以注進(jìn)去,這意味著,現(xiàn)在我們已經(jīng)可以在word變量的值里面任意寫HTML標(biāo)簽代碼,比如類似<script>alert(1);</script>這樣的。另一個有價值的發(fā)現(xiàn),就是把頁面卷屏到一半的位置時,屏幕右方的顯示出現(xiàn)混亂,本來應(yīng)該是有問題就打聽一下的欄目標(biāo)題,變成了" target="_blank">有問題就打聽一下,說明HTML語法已經(jīng)被破壞。JS異常和頁面顯示錯亂都很可能意味著XSS,因?yàn)檫@兩個現(xiàn)象都說明輸入的特殊字符已經(jīng)起了某些語法上的作用,而不僅僅是當(dāng)做字符顯示或者保存在HTML中。
偵查
存在跨站腳本注入漏洞的可能性看來很大,但是為了構(gòu)造有效的攻擊字串,我們需要跟蹤分析word變量的使用。通過View Source Code,查找跟蹤word變量的值的傳播和使用路徑,得到以下代碼,注釋是我加的 (網(wǎng)站開發(fā)人員可不會主動在注釋里面寫明漏洞):
// 注入內(nèi)容原樣出現(xiàn)在JS代碼的字符串常量中,但是在賦值給word時
// JS引擎會進(jìn)行解碼,所以word變量的值是<>"三個字符
var word = "\x3c\x3e\x22";
…
//word變量的值直接拼到了moreDating后面,是個URL
var moreDating="http://dating.xxx.com/search_question.htm"+"?q="+word;
…
var _hebin = function(a, b){
...
if (a != 0) {...}
_html += '...美容達(dá)人心得榜</a></div>';
else {
// moreDating URL直接作為<a>標(biāo)簽的href屬性值
// 顯然,這里只要moreDating變量的值包含一個小于號(已經(jīng)有了!)
// 就可以強(qiáng)行終止<a>標(biāo)簽,然后后面就可以自由書寫自己的腳本標(biāo)簽了
_html += '<div class="Title"><a href='+moreDating+' target="_blank">有問題就打聽一下</a></div>';
…
}
…
if(a>0||b>0){
layer.innerHTML += _html;
}
閱讀過這段代碼,基本上可以確定跨站腳本攻擊的整個流程是通的。用戶可以輸入經(jīng)過JS編碼的HTML標(biāo)簽,然后被解碼并存儲到word變量,word變量又被拼接到moreDating這個URL的后面作為參數(shù),而moreDating在滿足特定條件(a等于0而b大于0)時,會被拼裝到HTML里面去,并輸出到頁面。
_hebin這個函數(shù)在什么條件下會被觸發(fā)?粗略讀了一下代碼之后,發(fā)現(xiàn)邏輯不是很清晰。算了,先不浪費(fèi)時間來研究這個。讓事實(shí)說話吧。
發(fā)動
上一節(jié)的那段代碼轉(zhuǎn)了兩道彎,實(shí)際上可以等價簡化為下面這樣:
<a href=http://.../?q= +word+ target="_blank">。其中word變量的前后分別被拼上了一小段HTML代碼,然后被輸出到頁面。如果我們能通過適當(dāng)設(shè)置word的值,讓這一行代碼變成下面這樣:
<a href=http://xxx/?q=><img src=javascript:alert(1) target="_blank"> 那么JS腳本就會被執(zhí)行了。大家也許發(fā)現(xiàn)后面那個看起來多余的target=,這其實(shí)不要緊,只是給HTML標(biāo)簽賦予一個無意義的屬性值。上面字串的中間黑體字部分就是word變量應(yīng)當(dāng)具有的值,但是我們不能直接輸入它,那樣會被HTML編碼邏輯捕捉到。把這個值用JS編碼過后,就可以躲過HTML編碼,得到我們需要輸入的搜索關(guān)鍵字的值為:
\x3e\x3cimg src=javascript:alert(1) 我把這段文本拷貝出來,貼到商品搜索輸入框,點(diǎn)擊搜索按鈕。等了幾秒鐘后,“當(dāng)”的一聲響,警告框彈了出來。成功了。對應(yīng)上面的搜索關(guān)鍵字的URL是:http://search1.taobao.com/browse/0/n-g,lr4dgzk4pazwg2lnm4qhg4tdhvvgc5tbonrxe2lqoq5gc3dfoj2cqmjj-------2-------b--40--commend-0-all-0.htm?at_topsearch=1&ssid=s1-e
直接訪問這個URL,也會彈出警告框。用這個漏洞可以干些什么?那就看讀者的想象力和JavaScript編程的功力了。
后來經(jīng)過驗(yàn)證,這個攻擊在FireFox里沒有效果,只有在IE里可以彈出對話框。具體原因我也懶得去深究了。實(shí)際攻擊時也沒有上面寫的這么順利,我先試<script>標(biāo)簽,注入了但是沒執(zhí)行,再試<a onmouseover=,可以執(zhí)行,但是onmouseover觸發(fā)不好用,最后試<img src=才獲得了理想的結(jié)果。另外,注入的腳本要生效還有一個條件(大概是,不完全確定),就是輸入的關(guān)鍵字,在“打聽一下”里面必須要能搜到結(jié)果,并且在“美容達(dá)人心得榜”里面必須搜不到結(jié)果,只有在這兩個條件都滿足時,“打聽一下”的界面才會顯示出來,從而注入JS才會被實(shí)際插入到頁面的HTML代碼里并觸發(fā)執(zhí)行。如果想嘗試用其他的注入語法或者注入其他的JS語句,這個問題需要注意一下。
回顧
淘寶的開發(fā)人員在頁面里只使用了HTML編碼,并且只編碼了<>&"這幾個最常見的特殊字符,這是上面這個漏洞的主要原因。這似乎也是一個在Web開發(fā)安全編程中比較常見的誤解。其實(shí)要全面防治跨站腳本,僅僅對這個幾個特殊字符進(jìn)行HTML編碼是遠(yuǎn)遠(yuǎn)不夠的,JavaScript、URL、CSS、字符集、Web服務(wù)器、瀏覽器環(huán)境等等,任何一個環(huán)節(jié)的疏漏,都可能會引起跨站腳本注入問題。而需要編碼的可能有危險的字符,當(dāng)然也遠(yuǎn)遠(yuǎn)不止那幾個。
一個廣泛推薦的做法是,使用白名單進(jìn)行字符編碼。白名單的意思就是說,除了字母數(shù)字這些個無論何時也沒有特殊語法含義的普通字符之外,其他所有的特殊字符都進(jìn)行編碼。假如淘寶的開發(fā)人員采用了白名單編碼方法,就算他們沒有意識到JavaScript需要自己特有的編碼邏輯,也可以防治前面演示的攻擊。因?yàn)榉葱备軙?span lang="EN-US">HTML編碼為\,導(dǎo)致var word = "\x3c\x3e\x22";會變成var word = "\x3c\x3e\x22";。反斜杠在JS代碼里消失了,大于號等字符被偽裝,但是再也脫不下偽裝,無法還原并實(shí)施攻擊了。
對于那些邏輯簡單的網(wǎng)站,白名單方式的HTML編碼算是比較簡單有效的跨站腳本解決方法。但是要徹底解決問題,尤其是對于功能復(fù)雜,有大量混合使用JavaScript、HTML標(biāo)簽、事件處理等語法元素的Web應(yīng)用程序,如果不做好正確的、足夠的編碼,僅僅依賴于白名單方法,畢竟不是正道,總還是有缺陷的。下面就有個例子。
一點(diǎn)引申
<script>function showDetails(param) {}</script>
<body onload="showDetails('');alert('1')">Tom</body>
這是一段HTML代碼,粗體字部分是用戶輸入的一些數(shù)據(jù),已經(jīng)被進(jìn)行了白名單方式的HTML編碼,也就是說所有的特殊字符都被編碼了。但是這里面還會存在跨站腳本問題嗎?讀者可以把這段代碼另存到一個HTML文件里面,用瀏覽器執(zhí)行試試看結(jié)果,想想為什么。如果有機(jī)會,再和大家多談?wù)効缯灸_本攻防的話題。
如對本文有疑問,請?zhí)峤坏浇涣髡搲瑥V大熱心網(wǎng)友會為你解答?。?點(diǎn)擊進(jìn)入論壇