加载中...

【prompt(1) to win】 Level B - In Exception



题目

function escape(input) {
    // name should not contain special characters
    var memberName = input.replace(/[[|\s+*/\\<>&^:;=~!%-]/g, '');

    // data to be parsed as JSON
    var dataString = '{"action":"login","message":"Welcome back, ' + memberName + '."}';

    // directly "parse" data in script context
    return '                                \n\
<script>                                    \n\
    var data = ' + dataString + ';          \n\
    if (data.action === "login")            \n\
        document.write(data.message)        \n\
</script> ';
}

解题思路

题目看上去似乎很复杂,其实可以简化成:

<script>
    document.write('"Welcome back, ' + input + '."');
</script>

其中 input 是我们输入的内容,允许输入的字符只有 a-zA-Z0-9"'()


JS 异常测试

这题其实很蹊跷,不清楚 Javascript 的异常机制很难成功解题。

不妨打开浏览器的控制台,输入这行 JS 代码:document.write("fun"());

留意异常信息为:"fun" is not a function

也就是说, JS 会把 () 前面的字符串识别是 函数名,但由于函数不存在,所以抛出异常。

换言之, () 里面可能会被识别为函数的参数表,可以不妨再测试下。

先构造无效的参数,在控制台输入这行 JS 代码:document.write("fun"(arg1, arg2));

这次异常信息为:arg1 is not defined ,说明 () 里面确实被识别成参数表。

而且因为抛出的异常信息变成了参数异常,说明参数表优先于函数名被解析

再构造有效的参数表测试一下:document.write("fun"(1, "2", 3+4));

这次异常信息又重新变成为: "fun" is not a function

利用这个特性,如果参数表是函数调用、或表达式计算,那么就可以在抛出 "fun" is not a function 异常之前就先被执行了。


JS 异常利用

不妨构造这样的 JS 代码测试一下:document.write("fun"(alert(1)));

很明显,参数 alert(1) 会先被解析执行触发,在关闭 alert 窗口后,才抛出异常。

这个特性或许可以用于解决这题。

在这题里,函数名 fun 就是 Welcome back, ,而参数表是我们控制的,那么目标就是构造成这样的 JS 代码 :

document.write("Welcome back, "(prompt(1)));

从运行结果上看,触发了 prompt(1) 执行,即这个方向是正确的。

据此我们就可以反推出 payload 应该为 "(prompt(1))" (注意要闭合双引号)


JS 操作符 in

但是这个 payload 还不足以解决问题,需要注意到,在我们注入点的后面,还有一个小尾巴 .

换言之,其实我们注入 "(prompt(1))" 这个 payload 后,得到的 JS 代码其实是这样的:

document.write("Welcome back, "(prompt(1))".");

而这个小尾巴最致命的地方,就是它先于参数表的 prompt(1) 被解析,导致先抛出了一个 SyntaxError 语法错误的异常, prompt(1) 则无法被执行。

那么接下来就需要处理掉这个语法错误的问题,使得参数表可以被解析。

但是由于 + 被过滤了,无法利用它拼接函数返回值和字符串去解决这个尾巴。

不过 JS 还有一个 in 操作符同样可以达到拼接目的,其使用方法是 [a_object] in [b_object] ,用于判断一个对象 a 是否被对象 b 包含。

虽然 in 对 object 类型有要求,但是即使是类型错误,也只会在运行时抛出,而不会在最开始解析时就直接报语法错误,从而可以解决前面语法错误导致参数表的 prompt(1) 没有被解析的问题。

所以最终的 payload 为 "(prompt(1))in" ,构造成的 JS 代码为:

document.write("Welcome back, "(prompt(1))in".");

此 payload 运行时会依次触发 3 个事件:

  • 解析并执行参数表的 prompt(1)已经足以完成挑战
  • 抛出 Welcome back, 函数未定义异常
  • 抛出 in 操作符的 TypeError 异常

答案下载


文章作者: EXP
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 EXP !
  目录