注* XSS攻擊即Cross Site Scripting,通常在網(wǎng)頁鏈接地址Url中注入JS代碼來達(dá)到攻擊手段,很多大廠都中過招,如:Twitter,新浪微博,類似于:http://www.demo.cn/=<script>alert(document.cookie)</script> 其實(shí)此代碼并不能在所有瀏覽器上執(zhí)行,但僅需要一部分瀏覽器(如IE6)可用,即可達(dá)到攻擊效果。目前很多網(wǎng)站都有自動(dòng)過濾XSS代碼的功能,此文即介紹了一些如何屏蔽XSS過濾器的手段,其實(shí)我們可以發(fā)現(xiàn),大多數(shù)在前端執(zhí)行的XSS過濾都是不安全的,這對(duì)于我們?cè)诜婪禭SS攻擊的時(shí)必有一定的借鑒意思。
引言
我喜歡以一種意想不到的方式使用JavaScript,寫出一些看起來奇怪但其實(shí)很管用的代碼,這些代碼常常能夠執(zhí)行一些出人意料功能。這聽起來似 乎有些微不足道,但是基于這點(diǎn)發(fā)現(xiàn)足以總結(jié)出一些非常有用的編程技巧。下面寫到的每一個(gè)小技巧都可以屏蔽掉XSS過濾器,這也是我寫這些代碼的初衷。然而,學(xué)習(xí)這樣的JavaScript代碼可以明顯加強(qiáng)你對(duì)語言本身的掌握,幫助你更好地處理輸入,并且提高Web應(yīng)用程序的安全性。
下面就看看這些令人驚異的JavaScript代碼吧!
正則表達(dá)式替換可執(zhí)行代碼
當(dāng)用到帶有replace的正則表達(dá)式時(shí),第二個(gè)參數(shù)支持函數(shù)賦值。在Opera中,可以利用這個(gè)參量執(zhí)行代碼。例如,下面這個(gè)代碼片段:
'XSS'.replace(/XSS/g,alert)
這個(gè)執(zhí)行的結(jié)果將會(huì)等價(jià)于:alert(‘XSS’); 產(chǎn)生這種現(xiàn)象的原因是正則表達(dá)式的匹配項(xiàng)被被當(dāng)成一個(gè)參數(shù),傳遞到了alert函數(shù)。一般情況下,在匹配文本上你會(huì)用一個(gè)函數(shù)調(diào)用另一段代碼,像這樣:
'somestring'.replace(/some/,function($1){
//do something with some
})
但是,正如在第一個(gè)例子中所看到的,我們執(zhí)行了一個(gè)本地alert調(diào)用,而不是用戶自定義函數(shù),并且參數(shù)由正則表達(dá)式傳遞到了本地調(diào)用。這是個(gè)很酷的技巧,可以屏蔽掉一些XSS過濾器。例如,先寫一個(gè)字符串,再跟一個(gè)“卯點(diǎn)”,接著就可以調(diào)用任何你想調(diào)用的函數(shù)啦。
為了看一看這個(gè)在XSS環(huán)境中是怎么使用的,想象一下:我們?cè)谧址杏卸挝催^濾的攻擊代碼,可能是JavaScript事件或者是script標(biāo) 簽,即這個(gè)字符串中出現(xiàn)了一個(gè)注入。首先,我們注入一個(gè)有效的函數(shù)alert(1),接著我們突破這個(gè)引號(hào)的限制,最后再寫我們的正則表達(dá)式。
.replace(/.+/,eval)//
注意我在這里用了eval函數(shù)執(zhí)行我想執(zhí)行的任何代碼,并且為了使攻擊代碼傳遞給eval,正則表達(dá)式必須匹配所有項(xiàng)。
如果我把所有的代碼放在一起,展示這個(gè)頁的輸出,這樣的話就會(huì)更容易理解這個(gè)過程:
頁輸出:
<script>somevariableUnfiltered="YOUR INPUT"</script>
上面的代碼在分析腳本中很常見,你上網(wǎng)搜索的所有字符串都被一些廣告公司儲(chǔ)存在這樣的分析腳本中。你可能沒有注意到這些腳本,但是如果你觀察一個(gè) Web頁面的源,你會(huì)發(fā)現(xiàn)這是經(jīng)常出現(xiàn)的。另外,論壇也是一個(gè)經(jīng)常會(huì)用到這些腳本的地方。“YOUR INPUT”是你所控制的字符串。如果輸入沒有被正確過濾時(shí),這也將被稱為基于DOM的XSS注入。(注:DOM,將 HTML 文檔表達(dá)為樹結(jié)構(gòu),通常指HTML結(jié)構(gòu))
輸入:
alert(1)".replace(/.+/,eval)//
輸出結(jié)果:
<script>somevariableUnfiltered="alert(1)".replace(/.+/,eval)//"</script>
注意這里”//”用于清除后面引用的單行注釋。
Unicode 轉(zhuǎn)義
盡管在對(duì)Unicode字符轉(zhuǎn)義時(shí),用圓括號(hào)是不太可能的,但是我們可以對(duì)正在被調(diào)用的函數(shù)名進(jìn)行轉(zhuǎn)義。例如:
u0061u006cu0065u0072u0074(1)
這句代碼調(diào)用了alert(1); u表明這是個(gè)轉(zhuǎn)義字符,并且在u0061后面的十六進(jìn)制數(shù)是“a”。
另外,常規(guī)字符可以和轉(zhuǎn)義字符混合或匹配使用,下面的例子就展示了這一點(diǎn):
u0061lert(1)
你也可以將它們包含在字符串中,甚至用eval對(duì)它們求值。Unicode轉(zhuǎn)義和常規(guī)的16進(jìn)制或8進(jìn)制轉(zhuǎn)義有些不同,因?yàn)閁nicode轉(zhuǎn)義可以包含在一個(gè)字符串中,或者是引用函數(shù)、變量或?qū)ο笾小?/p>
下面的例子展示了如何使用被求值并且被分成兩部分的Unicode轉(zhuǎn)義。
eval('\u'+'0061'+'lert(1)')
通過避免像命名為alert這樣的常規(guī)函數(shù),我們就可以愚弄XSS過濾器注入我們的代碼。這個(gè)例子就是用來繞過PHPIDS(一個(gè)開源的IDS系 統(tǒng)),最終導(dǎo)致規(guī)則變得更健壯。如果為了分析可能運(yùn)行的惡意代碼,你需要在解碼JavaScript時(shí),需要考慮過濾盡可能多的編碼方法。就像在這個(gè)例子中 看到的,這不是個(gè)容易的工作。
JavaScript解析器引擎
JavaScript是一個(gè)非常動(dòng)態(tài)的語言??梢詧?zhí)行很大量的代碼。這些代碼第一眼看起來似乎不能執(zhí)行,然而一旦理解了解析器工作的原理,你就能夠逐漸理解它背后的邏輯。
JavaScript在函數(shù)執(zhí)行之前是不知道函數(shù)結(jié)果的,并且很明顯它必須通過調(diào)用函數(shù)返回變量的類型。這點(diǎn)很有趣,舉個(gè)例子:如果返回函數(shù)不能返回代碼塊的一個(gè)有效值,就會(huì)在函數(shù)執(zhí)行之后出現(xiàn)語法錯(cuò)誤。
說的到底是什么意思呢?好吧!代碼總比空談更有說服力,看下面的例子:
+alert(1)–
alert函數(shù)執(zhí)行后,返回一個(gè)未定義的量,然而已經(jīng)有些太晚了,語法錯(cuò)誤立刻就會(huì)出現(xiàn),這是因?yàn)樽詼p操作符的操作數(shù)應(yīng)該是一個(gè)數(shù)字。
下面是一些不會(huì)產(chǎn)生錯(cuò)誤的例子:
+alert(1)
1/alert(1)
alert(1)>>>/abc/
你可能認(rèn)為上面的例子沒有什么意義,但是實(shí)際上它們深刻體現(xiàn)了JavaScript的工作過程。一旦你理解了這些細(xì)節(jié),JavaScript這個(gè)大 家伙就變得清晰,了解代碼的執(zhí)行方式可以幫助你理解解析器是怎么工作的。我覺得這類例子在追蹤語法錯(cuò)誤,檢測(cè)基于DOM的XSS攻擊和檢測(cè)XSS過濾器的時(shí)候很有用。
Throw,Delete還有什么?
你可以用想不到的方式進(jìn)行刪除操作,這會(huì)產(chǎn)生一些很古怪的語法。讓我們看看將throw, delete, not和typeof操作符組合在一起會(huì)發(fā)生什么?
throw delete~typeof~alert(1)
你可能認(rèn)為這句代碼不能運(yùn)行,但是使用函數(shù)調(diào)用delete卻是可以的,仍舊能夠執(zhí)行:
delete alert(1)
這兒有一些更多的例子:
delete~[a=alert]/delete a(1)
delete [a=alert],delete a(1)
第一眼看過去,你會(huì)認(rèn)為這樣的代碼有語法錯(cuò)誤,但是當(dāng)你仔細(xì)分析后,你覺得會(huì)有幾分道理。解析器先發(fā)現(xiàn)一個(gè)數(shù)組內(nèi)部的變量賦值,執(zhí)行賦值操作后刪除 數(shù)組。同樣地,刪除操作是在一個(gè)函數(shù)(注* [a=alert])調(diào)用之后,因?yàn)閯h除操作需要在知道函數(shù)執(zhí)行結(jié)果的情況下,才能刪除返回的對(duì)象,即使返回的是NULL。
同時(shí),這些代碼可以用來屏蔽XSS過濾器,因?yàn)樗鼈兘?jīng)常會(huì)嘗試著匹配有效的語法,不希望代碼太晦澀。當(dāng)你的應(yīng)用程序進(jìn)行數(shù)據(jù)驗(yàn)證的時(shí)候,你應(yīng)該考慮這樣的例子。
聲明全局對(duì)象
在屏蔽XSS過濾器的特定實(shí)例中,攻擊代碼經(jīng)常隱藏在一個(gè)類似英語文本中的變量中。聰明的系統(tǒng)如PHPIDS,可以使用語法分析去比較判斷訪問請(qǐng)求是否是惡意攻擊,所以這是測(cè)試這些系統(tǒng)很有用的方法。
僅使用全局對(duì)象或函數(shù)時(shí),能夠產(chǎn)生類似英文的代碼塊。事實(shí)上,在sla.ckers安全論壇上,我們可以玩?zhèn)€小游戲,用JavaScript形式產(chǎn)生類似英語的句子。為了了解這是怎么一回事,請(qǐng)看下面的例子:
stop, open, print && alert(1)
我自己杜撰了個(gè)名字,叫作Javascriptlish, 因?yàn)樗梢援a(chǎn)生一些看起來很不可思議的代碼:
javascript : /is/^{ a : ' weird ' }[' & wonderful ']/" language "
the_fun: ['never '] + stop['s']
我們使用正則表達(dá)式/is/跟上一個(gè)操作符^,接著創(chuàng)造一個(gè)對(duì)象{ a : ‘weird’}(擁有a屬性和賦值weird)。在我們剛剛創(chuàng)造的對(duì)象中,尋找’ & wonderful ‘屬性,這個(gè)屬性接著被一串字符分開。
接下來我們用一個(gè)命名為the_fun 的標(biāo)識(shí)和一個(gè)帶有never的數(shù)組,用一個(gè)命名為stop的全局函數(shù)檢查s… 的屬性,所有這些都是正確的語法。
Getters/Setters函數(shù)
當(dāng)火狐增加 custom syntax for setters后,屏蔽了一些不使用圓括弧的有趣XSS注入。Opera還不支持自定義語法—從安全角度來說,這是個(gè)優(yōu)點(diǎn),但對(duì)JavaScript黑客來說卻不是個(gè)好消息。然而Opera支持標(biāo)準(zhǔn)的defineSetter語法。這使我們能夠通過賦值以達(dá)到調(diào)用函數(shù)的 目的,說起來這對(duì)屏蔽XSS過濾器來說也有些作用。
defineSetter('x',alert); x=1;
假如你不了解setters/getters,那么上面的例子就是為全局變量x創(chuàng)造了一個(gè)設(shè)值函數(shù)。當(dāng)一個(gè)變量被設(shè)定時(shí)就會(huì)調(diào)用設(shè)值函數(shù)。第二個(gè)參數(shù)alert是函數(shù)調(diào)用賦值。這樣,當(dāng)x被賦值成1時(shí),就會(huì)調(diào)用alert函數(shù),并把1作為參數(shù)。
Location允許url編碼
location對(duì)象允許url用JavaScript編碼。這允許你通過雙重編碼進(jìn)一步掩飾XSS注入。
location='javascript:%61%6c%65%72%74%28%31%29'
將它們與轉(zhuǎn)義字符結(jié)合能夠很好地隱藏字符串。
location='javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c %75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(1)'
第一個(gè)例子是可行的,因?yàn)镺pera的地址欄可以識(shí)別編碼的地址串。通過用URL編碼,你可以隱藏JavaScript代碼。這點(diǎn)很有用,特別是當(dāng)傳遞XSS攻擊代碼的時(shí)候,我們?yōu)榱烁M(jìn)一步地屏蔽過濾,可以進(jìn)行雙重URL編碼。
第二個(gè)例子結(jié)合了第一個(gè)例子利用轉(zhuǎn)義字符的技巧。所以,當(dāng)你對(duì)字符串解碼時(shí),就會(huì)導(dǎo)致alert函數(shù)以這樣的形式顯示:
u0061u006cu0065u0072u0074
注* a 的ASCII編碼為0×61