信息安全技术大赛——Console
题目标题:Console 题目描述: 题目入口:http://code2.myclover.org/
这道题没有描述,打开网页,只看到一个人在不停地走动:
网页源码里只提示要用Chrome打开网页,就没其它提示了,唯一可疑的就是题目Console。 打开Chrome的开发人员工具,刷新网页,切换到Console标签,看到了提示信息:
按照提示输入syc,然后回车,出现了以下内容:
“你尝试过F5了么?如果没有,试试看!” “注意到URL了么?好像很有趣” “尝试着点击浏览器的后退看看,你什么时候访问的这些地址呀?” “看看你的浏览器历史记录” “试着访问这些地址,你会找到线索”
这时浏览器的地址栏里的地址是:http://code2.myclover.org/Sunday
。 点击浏览器的后退按钮,地址栏里的地址变成:http://code2.myclover.org/Saturday
。 于是我打开浏览器的历史记录看了一下:
可以看到,浏览器产生了7个访问历史,从Monday到Sunday。 但是这是怎么产生的呢?我查看了开发人员工具的Network标签,发现了一个名为msg的js文件:
再打开这个js文件,里面有一段代码:
1 2 3 4 var a = ["Monday" , "Tuesday" , "Wednesday" , "Thursday" , "Friday" , "Saturday" , "Sunday" ];for (var b = 0 ; b < 7 ; b++) { history.pushState(null , null , a[b]) };
history.pushState是HTML5里的API,作用是将指定的URL添加到浏览器历史记录里。
再根据提示访问历史记录里的地址,出现了提示:
一开始以为网页里有陷阱,但查看源码并没发现什么奇怪的地方。 于是继续访问其它地址,当访问到了星期三时,提示变了:
每周三都是 [三叶草] 集会的日子,每次集会都可以得到三个字符。 据说收集到所有的字符,就可以找到神龙,神龙就会告诉你key。 搜集到所有的字符,将这些字符按照 [大写字母+小写字母+数字] 的顺序排列起来, 再访问这个序列构造的URL就可以找到神龙。 例如:ABFGJbcdzy456,则访问 http://[你懂的]/ABFGJbcdzy456。 不过传说所有的字符大概有四十多个。
获取字符的地址为:http://code2.myclover.org/randomstr
。 下面为获取所有字符并排序的python代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import urllib2def get_three_chars (): try : url = 'http://code2.myclover.org/randomstr' page = urllib2.urlopen(url, timeout=10 ) return page.read() except : return '' if __name__ == '__main__' : chars = '' .join([get_three_chars() for i in range (100 )]) words = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' print ('' .join(sorted (set (chars), key=lambda x:words.index(x))))
因为提示说字符大概有40多个,那么获取100次得到300个字符应该覆盖了全部字符了。 执行后得到了传说中的字符:ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678。 (好吧,居然有48个字符,也算是40多个吧!!!)
打开网址http://code2.myclover.org/ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678
,看到了新提示:
1 2 神龙说: ({}+[])[!![]-~!![]-~!![]]+(!![]+[])[-~!![]]+([][[]]+[])[!![]-~!![]-~!![]]+(!![]+[])[+[]]+(![]+[])[!![]-~!![]]+'y' +({}+[])[!![]-~!![]-~!![]]
是一段精心构造的js代码,js代码中的加号是连接字符串的操作符,所以代码可以写成:
1 2 3 4 5 6 7 8 9 10 11 12 13 ({}+[])[!![]-~!![]-~!![]] + (!![]+[])[-~!![]] + ([][[]]+[])[!![]-~!![]-~!![]] + (!![]+[])[+[]] + (![]+[])[!![]-~!![]] + 'y' + ({}+[])[!![]-~!![]-~!![]]
下面是分步骤分析过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1、({}+[])[!![]-~!![]-~!![]]。 可以拆分成两个部分:({}+[]) 和 [!![]-~!![]-~!![]]。 ({}+[])在计算{}+[]时,由于对象无法相加,所以在加法之前会先做一个toString的转换, 即({}.toString()+[].toString()) = "[object Object]",是一个字符串。 [!![]-~!![]-~!![]]里面都包含有!![],因为[]是一个object,为非Null值, !是逻辑取反,所以![]的结果是false,因此!![] = true。 也就是说[!![]-~!![]-~!![]]可转化为[true - ~true - ~true]。 在计算~true时,~是按位取反操作,需要一个数字操作数,所以true当作数字1处理。 1在计算机的原码是00000001,按位取反后~1 = 11111110,是一个负数。 因为负数在计算机里是以补码显示的,转成原码的方法是非符号位减1再取反, 即11111110 => 11111101 => 10000010,结果为-2,因此~true = ~1 = -2。 简单的计算方法是操作数取负数再减1,~1 = -1-1 = -2。 所以[true - ~true - ~true] = [1 - (-2) - (-2)] = [5]。 "[object Object]"[5] 表示取字符串第5个元素的值,所以"[object Object]"[5] = 'c'。
1 2 3 4 5 6 2、(!![]+[])[-~!![]]。 同上,可以拆分成两个部分:(!![]+[]) 和 [-~!![]]。 (!![]+[]) = (true + []) = (true + "") = "true"。 [-~!![]] = [-~1] = [-(-2)] = [2]。 (!![]+[])[-~!![]] = "true"[2] = 'u'。
1 2 3 4 5 6 7 8 3、([][[]]+[])[!![]-~!![]-~!![]]。 同上,可以拆分成两个部分:([][[]]+[]) 和 [!![]-~!![]-~!![]]。 [][[]]可拆分为[] 和 [[]],[]是空数组,[[]]里面的[]是一个object, 而取空数组元素时下标只能用数字,不能用object,所以返回未定义undefined, 因此([][[]]+[]) = (undefined + []) = "undefined"。 [!![]-~!![]-~!![]] = [true - ~true - ~true] = [1-(-2)-(-2)] = [5]。 ([][[]]+[])[!![]-~!![]-~!![]] = "undefined"[5] = 'i'。
1 4、(!![]+[])[+[]] = (true+[])[+0] = "true"[0] = 't'。
1 5、(![]+[])[!![]-~!![]] = (false+[])[true-~true] = "false"[3] = 's'。
把上面7个字符拼起来就得到了过关的key: "cuitsyc"
。
最简单的方法是把js代码复制到开发人员工具的Console里执行: