轉載於http://www.iteye.com/topic/947149javascript
在2011年的BlackHat DC 2011大會上Ryan Barnett給出了一段關於XSS的示例javascript代碼:
java
Javascript代碼
數組
($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+($$=($_=!''+$)[_/_]+$_[+$])])()[__[_/_]+__[_+~$]+$_[_]+$$](_/_)
這是一段徹底合法的javascript代碼,效果至關於alert(1)。它能夠在大部分瀏覽器上運行。(雖然目前我測試過手頭的瀏覽器都能運行,但理論上不能保證全部瀏覽器都能正確運行,緣由見下文)瀏覽器
這段代碼的好處(對於***)是,它不包含任何字符或數字,能夠逃過某些過濾器的檢查。好比說,若是假定一個AJAX請求將返回一個只包含數字的 JSON,因而極可能會簡單判斷了一下其中不含字母就直接eval了,結果給***們留下了後門。上面的代碼功能很簡單,只是alert(1),但使用一樣 的原理,徹底能夠幹出更復雜的事,例如alert(document.cookie)。更重要的是,這段代碼再一次提醒我,***的想象力是無限的……正如 Ryan Barnett的演講標題:"XSS:The only rule is no rule"。cookie
那麼這段代碼是如何工做的呢?ide
咱們能夠把它分爲兩個部分來理解:函數
第一部分:測試
($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+($$=($_=!''+$)[_/_]+$_[+$])])()
第二部分:
spa
[__[_/_]+__[_+~$]+$_[_]+$$](_/_)
其中第一部分是核心,咱們首先對它進行分析,先縮進一下:
($= [$=[]][ (__=!$+$)[_=-~-~-~$] + ({}+$)[_/_] + ($$= ($_=!''+$)[_/_] + $_[+$]) ] )()
顯然,最外層是(...)()形式的函數調用,咱們須要看看這裏究竟調用了什麼函數,返回了什麼。下一步,咱們把原來代碼中賦值表達式提取出來,將其改寫爲如下等價形式:
$ = []; //1 __ = !$+$; //2 _ = -~-~-~$; //3 $_=!''+$; //4 $$ = $_[_/_] + $_[+$]; //5 $= [$][ __[_] + //6 ({}+$)[_/_] + //7 $$ //8 ]; //9 $(); //10
如今來一行行看:
1. $先賦值爲一個空數組 (後面會被覆蓋)
2.__ = ![] + [] = false + [] = "false"這裏利用了javascript運算的強制類型轉換特性。首先空數組是一個非null值,所以![]的結果是false(布爾型)。在計算false + []時,因爲數組對象沒法與其餘值相加,在加法以前會先作一個toString的轉換,空數組的toString就是"",所以事實上在計算false + ""。這時false被自動轉換爲字符串。最終結果是"false"+"" = "false"。 **
換句話說,在$爲空數組時,使用 「+$」的方式能夠將任何一個值轉爲字符串**
3. 在計算~[]時,~須要一個數字操做數,空數組沒法直接轉換爲數字,則做爲0處理。所以~[] = ~0 = -1
參考: ~3 = -4 ~[3] = -4 ~[3,2] = -1 (沒法轉爲數字) ~"3" = -4 ~"abc" = -1
所以:
_ = -~-~-~[] = -~-~-(-1) = -~-~1 = -~-(-2) = -~2 = -(-3) = 3
理論上,能夠用這種方式得出1-9全部數字
4. !''是true,使用+$將其變爲字符串 "true"
5. 這裏須要注意的是,以前一直用「值+[]」來得到「值」的字符串形式。而「+[]」則是0(正號致使[]被自動轉換爲數值0)。所以:$$ = "true"[3/3] + "true"[+[]] = "true"[1] + "true"[0] = "rt"
6.__[_] = "false"[3] = "s"
7. ({} + [])致使空對象{}被轉換爲字符串"[object Object]", 所以({}+$)[_/_] = "[object Object]"[1] = "o"
9. 這裏把$覆蓋爲[[]]["s"+"o"+"rt"]。注意這裏[[]]自己是一個包含空數組的數組,其實對這一步來講,任何一個數組都沒有關係(不必定要是嵌套數組),但做者巧妙地把$的首次賦值式放在了數組內部,使代碼更爲緊湊。最終結果是$ = [[]]["sort"] = [[]].sort = Array.prototype.sort
10. 調用$(),做爲整個表達式最終的取值。須要注意,$是全局範圍的,是window的一個屬性,至關於window.$。而Array.prototype.sort會返回this。對於window.$來講,this就是window。所以,整個第一部分的值,就是window自己!固然,這個過程的正確運做依賴於當前瀏覽器Array.prototype.sort實現能對this爲window的狀況容錯。
經過第一部分,咱們已經得到將任何值轉換爲字符串的簡單方法,並能產生任意的數值,理論上就能夠從javascript的取值系統中提取出大部分 字母(不知道是否是所有,須要考證)。而且,咱們獲取到了window的引用。下面就能夠開始上下其手,隨心所欲了。木哈哈哈哈哈!
能夠看出,上面的第10步是與瀏覽器的具體實現相關的,所以也存在着某些瀏覽器下須要對代碼做出修改的可能。
如今看第二部分,事實上已經很是明朗了,惟一須要注意的是,如今$是一個函數,所以~$ = ~0 (沒法直接轉換爲數字則做爲0處理) = -1。
Javascript代碼
[__[_/_]+__[_+~$]+$_[_]+$$](_/_) = ["false"[1]+"false"[3+(-1)]+"true"[3]+"rt"](1) = ["a"+"l"+"e"+"rt"](1)
因此,整條式子至關於:
Javascript代碼
window["alert"](1)