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