你不知道的JavaScript用法 Hacker是這樣寫JS的

Web開發者發表於2014-01-21

  作者:garethheyes  翻譯:Tianyi_Ting 校核:myownghost

  注* 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字元轉義時,用圓括號是不太可能的,但是我們可以對正在被呼叫的函式名進行轉義。例如:

\u0061\u006c\u0065\u0072\u0074(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操作符組合在一起會發生什麼?

throw delete~typeof~alert(1)

  你可能認為這句程式碼不能執行,但是使用函式呼叫delete卻是可以的,仍舊能夠執行:

delete alert(1)

  這兒有一些更多的例子:

delete~[a=alert]/delete a(1)
delete [a=alert],delete a(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函式以這樣的形式顯示:

\u0061\u006c\u0065\u0072\u0074

  注* a 的ASCII編碼為0x61

  這篇文章遵循 Creative Commons Attribution, Non Commercial - Share Alike 2.5 許可。

  原文 dev.opera.com

相關文章