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