转载于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)