5

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

 2 years ago
source link: https://exp-blog.com/safe/ctf/prompt/level-b-in-exception/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【prompt(1) to win】 Level B



javascript
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 会把 () 前面的字符串识别是 函数名,但由于函数不存在,所以抛出异常。

01.png

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

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

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

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

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

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

02.png

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


JS 异常利用

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

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

03.png
04.png

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

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

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

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

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

05.png

JS 操作符 in

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

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

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

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

06.png

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

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

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

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

07.png

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

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK