故事源于某一天,笔者的一位后台同事突然在 RTX 找到了我,然后抛出了一道一看就要跪的题,
以下是他给我的原题:
1 2 3 4 5 6 7 8 |
(function escape(input) { input = input.replace(/[;\\\/<>a-zA-Z]/g, "_").slice(0,1500); return "<script>[][([![]]+[])[++[++[++[[]][+[]]][+[]]][+[]]]+([]+{})[++[[]][+[]]]+([!![]]+[])[++[[]][+[]]]+([!![]]+[])[+[]]]"+input+"</script> "; })('?') 要求可以弹出alert框 |
初略看了一下,感觉这是在逗我么。。。这么搞,js 都 Brainfuck 化了啊。
再仔细一看,好像在哪见过类似的题目啊。
于是乎笔者脑海不断地往前翻看记录,终于想起之前在 wtfjs 里面看到的 obfuscated fibonacci,一道求斐波那契数列的题,
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var fib = function (_) { for(_=[+[],++[[]][+[]],+[],_],_[++[++[++[[]][+[]]][+[]]][+[]]]=(((_[++[++[++[[]][+[]]][+[]]][+[]]]-(++[[]][+[]]))&(((--[[]][+[]])>>>(++[[]][+[]]))))===(_[++[++[++[[]][+[]]][+[]]][+[]]]-(++[[]][+[]])))?(_[++[++[[]][+[]]][+[]]]=++[[]][+[]],_[++[++[++[[]][+[]]][+[]]][+[]]]-(++[[]][+[]])):+[];_[++[++[++[[]][+[]]][+[]]][+[]]]--;_[+[]]=(_[++[[]][+[]]]=_[++[++[[]][+[]]][+[]]]=_[+[]]+_[++[[]][+[]]])-_[+[]]); return _[++[++[[]][+[]]][+[]]]; } console.assert(fib(-1) === 0); console.assert(fib(0) === 0); console.assert(fib(1) === 1); console.assert(fib(2) === 1); console.assert(fib(3) === 2); console.assert(fib(4) === 3); console.assert(fib(5) === 5); console.assert(fib(6) === 8); console.assert(fib(7) === 13); console.assert(fib(32) === 2178309); console.assert(fib(46) === 1836311903); console.assert(fib(47) === 2971215073); console.assert(fib(63) === 6557470319842); console.log('done!'); |
这两者非常类似,充斥了大量"[]()+"的符号。
再想想好久之前看过有些人的博客介绍里面加上一大堆这种符号,
控制台里一打印出来是他的名字,逼格顿时蹭蹭蹭上去了。
到了这里,一切仿佛是那么滴清晰了。
然后让我们再来回顾他给的题目的特点进行 360 度全方位剖析:
第一步是对输入的内容进行正则的替换,主要是字母还有;、<> 这些将被替换掉,并且限制了长度在 1500 之内;
第二步才是主菜,前面给的一大段符号,其实也算是一种提示,让我们把它打印出来看看:
额,这里怎么变成了个 sort 函数呢?不科学吧。。。
亲,不急哈,根据一门语言的词法分析,如果要不报错,那么上面的代码应该是下面这种形式:
1 2 3 4 |
[][([![]]+[])[++[++[++[[]][+[]]][+[]]][+[]]]+([]+{})[++[[]][+[]]]+([!![]]+[])[++[[]][+[]]]+([!![]]+[])[+[]]] //上面代码需等于形式如下面一样的 Array[索引或属性] |
因为前面是个空的数组,那么后面应该就是它的属性值了,
也就是后面中括号中可以形成字符串的”sort“,
接下来让我们来验证一下:
到这里已经基本清晰明朗,接着我们进行最后一步的字符拼接剖析。
要形成"sort"这个字符串,我们需要的材料可以是"s"+"o"+"r"+"t"
继续看代码,前面好长,括号好多,说到这里,又想起 lisp 那些坑到哭的括号,又一阵忧伤。。。
既然前面那么坑,只要基于 js 的词法分析,我们从后面看也行啊:
1 2 |
//截取 + 以及 后面一个符合词法的代码段 进行分析 +([!![]]+[])[+[]] |
很明显,其实这个"+"只是用于字符串的拼接,这里先忽略掉,
对后面进行分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
([!![]]+[])[+[]] // 等于"t" //下面进行分解 ([!![]]+[]) // 这里等于"true", //笔者感觉这里面用多了一个没用的中括号,如下也是可以的 (!![]+[]) //这是只是要把true转为字符串的"true" //后面的[+[]] +[] === 0 // true [+[]] // 等于[0]; //于是上面代码等于如下代码 "true"[0]//取字符串的第一位 |
于是我们得到了"t", 接下来只需要对我们需要的字符进行类似的操作就可以了,
这里利用的是 js 中一些返回值如”true“,”false“,”undefined“等等,然后再通过索引拿到对应的字母进行拼接。
于是到了这里,我们已对该题分析完毕,剩下这么做就靠你们了哈。
这里再提供个在线转换的网站。
网上也有对应的源码映射表,感兴趣的也可以搜索一下,
基本上通过 []()+! 就可以模拟出绝大部分字符出来。
由此延伸,我们来到了前段时间举行的 xss writeup 挑战赛。
大赛地址:
http://prompt.ml/0
比赛要求:
1. 能执行 prompt(1) 则为成功
2. 代码在不需要用户交互的情况下也能被执行
3. 可以在 Chrome,Firefox,IE 这些浏览器其中之一的最新版本中可以运行
4. 每个级别至少有两种不同浏览器的解决方案
5. 代码越短越好
至于怎么玩,建议可以先到官网上试试身手,
解决方案与分析网上已有一些文章,这里就不展开讨论了,
附解决方案地址:
https://github.com/cure53/xss-challenge-wiki/wiki/prompt.ml
其中涵盖了各种的 XSS 玩法,值得一读。
祝玩得愉快!
杨威 2017 年 3 月 9 日
不错
mason wu 2015 年 1 月 8 日
冒昧的问一下,这样做的意义在哪里呢? 表现高大上的情怀?
花生 2015 年 1 月 10 日
我感觉是高级的防攻击手段吧,所谓知己知彼,要知道黑客的攻击手段,才能更好地防黑客
小强 2014 年 12 月 17 日
([!![]]+[])[+[]]] // 等于”t”
应该是:
([!![]]+[])[+[]]
TAT.gctang 2014 年 12 月 29 日
感谢指出,写多了个]
CHB丶Young 2014 年 12 月 1 日
不错的想法
Ben 2014 年 12 月 1 日
http://codetank.alloyteam.com/
你知道吗?这个网址访问不了了
出啥状况啦?
TAT.gctang 2014 年 12 月 1 日
服务器问题,正在修复中,给您带来的不便,深表抱歉
TAT.Jdo 2014 年 12 月 1 日
好玩
Barret Lee 2014 年 12 月 1 日
淘宝 UED 内部近期也搞了一个类似的 xss 攻击比赛,很有趣,很有收获!
TAT.gctang 2014 年 12 月 1 日
赞!欢迎分享这些类型的技术竞赛,如果做出对外的挑战赛也不错呀
yuanoook 2014 年 12 月 1 日
确诊为 Jsfuck -> http://www.jsfuck.com/
TAT.gctang 2014 年 12 月 1 日
赞!这个网站也不错