点开题目只有一个文件,文本方式打开:
<script>_='function $(){e=getEleById("c").value;length==16^be0f23233ace98aa$c7be9){tfls_aie}na_h0lnrg{e_0iit\'_ns=[t,n,r,i];for(o=0;o<13;++o){ [0]);.splice(0,1)}}} \'<input id="c">< onclick=$()>Ok</>\');delete _var ","docu.)match(/"];/)!=null=[" write(s[o%4]buttonif(e.ment';for(Y in $=' ')with(_.split($[Y]))_=join(pop());eval(_)</script>
看起来像是html混着一堆不可见字符
sublime中显示的:
<script>_='function $(){<0x02>e=<0x04>getEle<0x0f>ById("c").value;<0x0e>length==16<0x05>^be0f23<0x01>233ac<0x01>e98aa$<0x01>c7be9<0x07>){<0x02>t<0x08>fl<0x03>s_a<0x03>i<0x03>e}<0x06>n<0x08>a<0x03>_h0l<0x03>n<0x06>r<0x08>g{<0x03>e<0x03>_0<0x06>i<0x08>it\'<0x03>_<0x03>n<0x06>s=[t,n,r,i];for(<0x02>o=0;o<13;++o){ <0x0b>[0]);<0x0b>.splice(0,1)}}} \'<input id="c"><<0x0c> onclick=$()>Ok</<0x0c>>\');delete _<0x01><0x07><0x05><0x02>var <0x03>","<0x04>docu<0x0f>.<0x05>)<0x0e>match(/<0x06>"];<0x02><0x07>/)!=null<0x08>=[" <0x04>write(<0x0b>s[o%4]<0x0c>button<0x0e>if(e.<0x0f>ment';for(Y in $='<0x0f><0x0e><0x0c><0x0b> <0x08><0x07><0x06><0x05><0x04><0x03><0x02><0x01>')with(_.split($[Y]))_=join(pop());eval(_)</script>
刚开始想了很多,eval函数的特性、JavaScript弱类型、编码、其他什么JavaScript奇怪的特性(bug)。
但是其实这道题跟这些屁关系都没有。
开始分析前先了解几个函数:
- with:
with 语句用于设置代码在特定对象中的作用域。
它的语法:
with (expression) statement
例如:
var sMessage = "hello";
with(sMessage) {
alert(toUpperCase()); //输出 "HELLO"
}
在这个例子中,with 语句用于字符串,所以在调用 toUpperCase() 方法时,解释程序将检查该方法是否是本地函数。如果不是,它将检查伪对象 sMessage,看它是否为该对象的方法。然后,alert 输出 “HELLO”,因为解释程序找到了字符串 “hello” 的 toUpperCase() 方法。
个人理解,也就是说with语句将()
里的内容和{}
的内容绑定在了一起。
- Array.prototype.pop()
pop()
方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。
- Array.prototype.join()
join()
方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。
join参数可选,如果没有传递参数,则默认以,
连接数组元素,如果传递了字符串,则以字符串连接数组元素。
继续看代码,先将代码美化一下:
<script>
_='function $(){e=getEleById("c").value;length==16^be0f23233ace98aa$c7be9){tfls_aie}na_h0lnrg{e_0iit\'_ns=[t,n,r,i];for(o=0;o<13;++o){ [0]);.splice(0,1)}}} \'<input id="c">< onclick=$()>Ok</>\');delete _var ","docu.)match(/"];/)!=null=[" write(s[o%4]buttonif(e.ment';
for(Y in $=' ')
with(_.split($[Y]))
_=join(pop());
eval(_)
</script>
这么一看逻辑就清晰了,_
里面是一堆带着乱码的字符串,下面开始循环,每次循环取出一个不可见字符,使用split
将_
以取出来那个不可见字符分割成数组,并用with添加到作用域链上。
接下来的_=join(pop())
操作就很好理解了,虽然没有给出是谁调用pop
、join
,但因为前面有with,所以可以理解为:
_=_.split($[Y]).join(_.split($[Y]).pop());
分割完之后的数组:
0: "function $(){e=getEle"
1: "ById("c").value;length==16^be0f23233ace98aa$c7be9){tfls_aie}na_h0lnrg{e_0iit'_ns=[t,n,r,i];for(o=0;o<13;++o){ [0]);.splice(0,1)}}} '<input id="c">< onclick=$()>Ok</>');delete _var ","docu"
2: ".)match(/"];/)!=null=[" write(s[o%4]buttonif(e."
3: "ment"
length: 4
进行一次pop()
得到'ment'
,接着join('ment')
,将数组元素连接起来,这样之前是不可见字符的地方就被替换了。
这样进行几次之后得到的源码:
function $() {
var e = document.getElementById("c").value;
if (e.length == 16) if (e.match(/^be0f23/) != null) if (e.match(/233ac/) != null) if (e.match(/e98aa$/) != null) if (e.match(/c7be9/) != null) {
var t = ["fl", "s_a", "i", "e}"];
var n = ["a", "_h0l", "n"];
var r = ["g{", "e", "_0"];
var i = ["it'", "_", "n"];
var s = [t, n, r, i];
for (var o = 0; o < 13; ++o) {
document.write(s[o % 4][0]);
s[o % 4].splice(0, 1)
}
}
}
document.write('<input id="c"><button onclick=$()>Ok</button>');
delete _
开始分析
首先获取input标签的值,过5个if判断:
- 长度是否为16位
- 字符串开头为
be0f23
- 字符串中是否含有
233ac
- 字符串中是否含有
c7be9
- 字符串末尾是
e98aa
满足这些条件后得到的字符串为:be0f233ac7be98aa
输入后,点击按钮,页面中得到flag。
但其实直接将if判断后的代码粘贴到控制台中,回车运行,直接就可以得到flag。