点开题目只有一个文件,文本方式打开:

    1. <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中显示的:

    1. <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 语句用于设置代码在特定对象中的作用域。
    它的语法:

    1. with (expression) statement

    例如:

    1. var sMessage = "hello";
    2. with(sMessage) {
    3. alert(toUpperCase()); //输出 "HELLO"
    4. }

    在这个例子中,with 语句用于字符串,所以在调用 toUpperCase() 方法时,解释程序将检查该方法是否是本地函数。如果不是,它将检查伪对象 sMessage,看它是否为该对象的方法。然后,alert 输出 “HELLO”,因为解释程序找到了字符串 “hello” 的 toUpperCase() 方法。

    个人理解,也就是说with语句将()里的内容和{}的内容绑定在了一起。

    • Array.prototype.pop()

    pop()方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。

    • Array.prototype.join()

    join()方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。

    join参数可选,如果没有传递参数,则默认以,连接数组元素,如果传递了字符串,则以字符串连接数组元素。


    继续看代码,先将代码美化一下:

    1. <script>
    2. _='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';
    3. for(Y in $=' ')
    4. with(_.split($[Y]))
    5. _=join(pop());
    6. eval(_)
    7. </script>

    这么一看逻辑就清晰了,_里面是一堆带着乱码的字符串,下面开始循环,每次循环取出一个不可见字符,使用split_以取出来那个不可见字符分割成数组,并用with添加到作用域链上。
    接下来的_=join(pop())操作就很好理解了,虽然没有给出是谁调用popjoin,但因为前面有with,所以可以理解为:

    1. _=_.split($[Y]).join(_.split($[Y]).pop());

    分割完之后的数组:

    1. 0: "function $(){e=getEle"
    2. 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"
    3. 2: ".)match(/"];/)!=null=[" write( s[o%4] buttonif(e."
    4. 3: "ment"
    5. length: 4

    进行一次pop()得到'ment',接着join('ment'),将数组元素连接起来,这样之前是不可见字符的地方就被替换了。
    这样进行几次之后得到的源码:

    1. function $() {
    2. var e = document.getElementById("c").value;
    3. if (e.length == 16) if (e.match(/^be0f23/) != null) if (e.match(/233ac/) != null) if (e.match(/e98aa$/) != null) if (e.match(/c7be9/) != null) {
    4. var t = ["fl", "s_a", "i", "e}"];
    5. var n = ["a", "_h0l", "n"];
    6. var r = ["g{", "e", "_0"];
    7. var i = ["it'", "_", "n"];
    8. var s = [t, n, r, i];
    9. for (var o = 0; o < 13; ++o) {
    10. document.write(s[o % 4][0]);
    11. s[o % 4].splice(0, 1)
    12. }
    13. }
    14. }
    15. document.write('<input id="c"><button onclick=$()>Ok</button>');
    16. delete _

    开始分析
    首先获取input标签的值,过5个if判断:

    1. 长度是否为16位
    2. 字符串开头为be0f23
    3. 字符串中是否含有233ac
    4. 字符串中是否含有c7be9
    5. 字符串末尾是e98aa

    满足这些条件后得到的字符串为:be0f233ac7be98aa
    输入后,点击按钮,页面中得到flag。

    但其实直接将if判断后的代码粘贴到控制台中,回车运行,直接就可以得到flag。