Javascript:alert(1)可以這樣寫以繞過filter


在2011年的BlackHat DC 2011大會上Ryan Barnett給出了一段關於XSS的示例javascript代碼:

($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+($$=($_=!''+$)[_/_]+$_[+$])])()[__[_/_]+__[_+~$]+$_[_]+$$](_/_)

這是一段完全合法的javascript代碼,效果相當於alert(1)。它可以在大部分瀏覽器上運行。(雖然目前我測試過手頭的瀏覽器都能運行,但理論上不能保證所有瀏覽器都能正確運行,原因見下文) 

這段代碼的好處(對於黑客)是,它不包含任何字符或數字,可以逃過某些過濾器的檢查。比如說,如果假定一個AJAX請求將返回一個只包含數字的JSON,於是很可能會簡單判斷了一下其中不含字母就直接eval了,結果給黑客們留下了后門。上面的代碼功能很簡單,只是alert(1),但使用同樣的原理,完全可以干出更復雜的事,例如alert(document.cookie)。更重要的是,這段代碼再一次提醒我,黑客的想象力是無限的……正如Ryan Barnett的演講標題:"XSS:The only rule is no rule"。 

那么這段代碼是如何工作的呢? 

我們可以把它分為兩個部分來理解: 
第一部分:

($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+($$=($_=!''+$)[_/_]+$_[+$])])()

第二部分:

[__[_/_]+__[_+~$]+$_[_]+$$](_/_)

其中第一部分是核心,我們首先對它進行分析,先縮進一下:

($= [$=[]][  
        (__=!$+$)[_=-~-~-~$] +  
        ({}+$)[_/_] +  
        ($$= ($_=!''+$)[_/_] + $_[+$])  
    ]  
)() 

顯然,最外層是(...)()形式的函數調用,我們需要看看這里究竟調用了什么函數,返回了什么。下一步,我們把原來代碼中賦值表達式提取出來,將其改寫為以下等價形式:

$ = []; //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。

[__[_/_]+__[_+~$]+$_[_]+$$](_/_) = ["false"[1]+"false"[3+(-1)]+"true"[3]+"rt"](1) = ["a"+"l"+"e"+"rt"](1)

所以,整條式子相當於:

window["alert"](1)

最后只想再感慨一次:黑客的想象力是無限的。理解代碼並不難,問題是一開始時他們是怎么能想出來的。。。

轉自:http://www.javaeye.com/topic/947149

開源工具

使用JS開發的編碼工具

一個“神奇”的工具:把 JavaScript 代碼轉為 ()[]{}!+ 字符


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM