前段时间看到了px1624的10题XSS案例,一直留着留着,终于拿出来看看了。还好网站没关。(然后突然增加了2题变成了12题了)
觉得是一个JS基础不是很好的菜鸡补充了解一些JS基础和奇怪姿势的很好的案例,此文包含7-12题(共9k3字),知识点大概包括:
当在字符串过滤中使用
\
的绕过方式<!-- <js>
可以突破</js>
标签的限制浏览器对于
<js/>
不解析为标签,但是<js />
一类的可以正常解析浏览器对于JS无分号换行的处理方式
<!-- <js>
可以多次突破</js>
标签的限制<js>
标签之间互相不管死活模板字符串的嵌套
闭合大括号的方式:对象等
使用%2f让浏览器不会吞掉多余路径
JS语言声明变量时变量提升的特性
var的另一种赋值方式:解构赋值
对于以上列出知识点如果只有个别不清楚的,可以直接在各题知识点中找到它,然后看那一题就行了。
后6题跟前6题有点不一样,前六题可能是你看了知识点,就会恍然大悟:“原来是这样!我会了!”,但是后六题可能就是看了知识点:“原来是这样!”,但是还是做不来。
此外还提出了第十题比已有解法更短的payload,以及官方WP暂未发布的11、12题的WP。
px1624的官方WP,写的很好。但是以菜鸡的角度去看题目总会讲述到一些默认被跳过的但是又有点东西的细节。写文章一向往细了写,结合了看可能也会有所帮助吧?
有错误喷就完事了。
感谢px1624老哥、11/12题原作renwax23的案例分享以及huuu老哥的十二题最短解
<js>
var px='123';
</js>
give me xss by pass~7
<div style="display:none">123</div>
<!--px-->
<js>
'px'
</js>
知识点:
当在字符串过滤中使用
\
的绕过方式<!-- <js>
可以突破</js>
标签的限制浏览器对于
<js/>
不解析为标签,但是<js />
一类的可以正常解析浏览器对于JS无分号换行的处理方式
题目的尿性,提示px,肯定是输入px参数,也就直接加上啦,就会发现有两个回显点:js标签中一个,div标签中一个。
老样子使用一个字符集去看看两个地方是怎么处理的:123"'/\<>()'%3b+%2b-*/%26|~^%25!%3f%3d%60%3b
<js>
var px='123px_xss\'\/\\<\>()\'\; -*\/&|~^%!?=`\;';
</js>
give me xss by pass~7
<div style="display:none">123"'/\<>()'; +-*/&|~^%!?=`;</div>
<!--px-->
<js>
'px'
</js>
第一处插入点,在js标签中:对于
' / < > ;
做了\
转义,使用转义可以防止'
作为符号解析闭合字符串第二处插入点,在div标签中:对于
' < > &
做了html转义,防止插入标签。
看似挺好的防护,第二处< >
被转义插入不了标签是完全没有机会的,直接掠过,看第一处插入点
字符串中的\
转义绕过
如果我们简单的插入一个单引号确实是会被转义从而闭合无效:var px='123\''
但是我们知道字符串中的\一个使用的使用是对后面的字符做转义处理,即作为字符串的一部分解析,同样我们想要在字符串中使用\符号的时候需要\\这样书写即为一个\
回顾转义列表,并没有对我们输入的\进行转义,于是我们就可以输入一个\来反过来转义掉服务端添加的用于转义'的\
正儿八经闭合,构造正确的JS语法,可以尝试
//
:JS单行注释,注释掉后面需要闭合的东西,可以尝试。/* */
:JS跨行注释,在当前环境上不能使用。突破不了js标签,只能在js标签内部进行多行注释<!--
:HTML跨行注释,机制上不能使用。HTML跨行注释,在js标签中,起不到注释作用模板字符串注释—反引号:跨行注释,没试过,可以尝试。
再结合给出两条payload插入思路进行判断:
JS中
;
闭合前面的语句然后写入新的JS语句使用运算符拼接字符串的形式,即使用
-
、in
之类的符号
第一个payload插入思路使用;明显不行因为;
被/转义了形成/;
会语法报错;只能使用第二个思路使用运算符。
尝试第一个闭合思路:正儿八经闭合,后面是
';
建立该测试页面,访问
<js>
var px='123';/*
</js>
give me xss by pass~7
*/
文字不会被注释,代表/* */
JS跨行注释无法突破js标签进行跨行注释。
证明第四个思路不可用:
<!--
:HTML跨行注释
建立测试页面,访问
<js>
<!--
var px='123';
</js>
give me xss by pass~7
-->
文字不会被注释,代表<!-- -->
HTML跨行注释无法突破js标签进行跨行注释。
尝试第五个思路:模板字符串进行跨行注释
尝试在命令行中执行这段JS,逻辑是没有问题的:
但是直接访问,让谷歌浏览器解析,仍然无法跨越js标签,同时其他浏览器也不行。
看来模板字符串也被封死了,关键是无法突破</js>
标签,陷入僵局。
<!-- 标签突破js标签
参考此篇文章得知<!--
标签会改变js标签配对规则,即:在<!--
之后的<js>
会优先匹配一个</js>
我们可以引入一个js去闭合掉原生的</js>
,这样就可以突破js把后面也作为JS解析。
但是这个特性在测试中,并不是那么万能,具有局限性。
按照字面解释的预期,这样的页面是可以正常执行JS的
<js>
alert(1);
<!--
<js>
</js>
-->
</js>
但是实际并不可以,会报错。
我们先通过以下例子来证明在<!--
之后的<js>
的确会抢先匹配,影响标签解析:
<js>
alert(1);
<!-- <js> //删这行
if(1</js>/){
alert(1)
}
-->
</js>
单行的<!--
在JS中具有单行注释的作用,所以按照正常的理解,理论上第三行加了和没加应该是一样的,但是实际结果却不是
不删第三行:会弹框1(说明JS正常执行)
删第三行:之后的alert(1)会被当作html解析,即js标签结束了(说明js标签被
1</js>/
这边的js标签提前闭合了)
情况出现了变化,证明我们的<!-- <js>
虽然是注释了,但是却改变了执行情况,即优先闭合下一个js后标签。说明我们注释的<js>
把1</js>/
这边的标签给闭合了从而完成了外部标签的正常闭合。
再看例子:
<js>
if(console.log("<!--var a;<js>")</js>/)
alert("js data state");
else
alert("js data state too");
-->
</js>
此处我们的<!--
出现在了字符串中,执行结果是 console.log 打印并且弹框 js data state
先得出最终使用结论,再来对上述例子进行解释:
JS在引入的
<!--
跟js标签
后形成的代码要满足JS语法正确。//在js标签对中,突然出现一个js标签,这是不符合JS语法的。这就解释了我们之前第一个预期成功然而失败的案例,是因为JS语法错误而失败
满足JS语法后的解析,会开始跳出JS语法逻辑寻找
<!--
。只是单纯的字符串匹配,所以即使是字符串内的<!--
或者注释的<!--
都满足。会开始寻找 js标签,同样寻找的过程同样跟JS语法无关,会单纯的寻找标签字符
对之前的例子进行解释:
加入
<!--<js>
去闭合第一个</js>
:需要防止JS语法错误(可以加入到字符串中)突破js标签后,这部分give me xss什么的会作为JS的一部分解析,我们需要引入一个跨行字符串或注释一类的包括住他们,确保其语法正确。(可以选用模板字符串或者
/* */
JS多行注释(之后测试由于环境,单独使用JS多行注释无法完成))由于第一个js标签会跟最后一个js后标签闭合,
</div><!--px
之后都会被认为是JS语句,还需要处理这边的JS语法错误问题。
选择加入到前方字符串中,选择模板字符串,先暂时使用//
注释掉明显不对劲的</div>
,给出一个初步的payload:
实验,利用浏览器的特性,加个空格%20:
但是回想到刚才其实我们没有明确解决的后面尾巴的问题:我们只是想先简单暂时解决下</div>
,但是后面的<js>'px'
都没有处理为啥就可以成功了?这关乎到JS的换行逻辑
JS其实也是一个比较随便的语言,他的换行会根据当前运算环境分析是要结束当前语句或者是延续下一行拼接,看一个例子:
<js>
var px=1 - 0
// 4>1
<4>1
<'px'
console.log(px)
</js>
如果我们不注释第三行,那么px的值为1,说明赋值语句已经结束了,之后的运算会作为一个新的运算语句解析;
如果我们注释的第三行,结果就会为false,浏览器会把之后的运算认为和赋值语句是一个语句,从而进行比较运算。
我们把答案的环境简化再合成一行:
<js>
var px='1' - alert(2) - `3` < 4 > 'px'
//var px='1' - alert(2) - `3` < a > 'px'
console.log(px)
</js>
该例子语法正确,只是在语句执行的时候从左向右执行,发现找不到a(也就是js)这个变量时报错,但是这不是JS语法错误而是运行的时候报错,所以会正常执行前面的弹框语句。
再给出其他人的一些payload:
<!-- <js>
可以多次突破</js>
标签限制
<js>
标签之间互相不管死活
基础检测payload一丢,四处输出点,确认可以构造payload的输出点:
第四处对双引号做了黑名单,没戏
第三处对长度做了限制,同时对<>做了转义,没戏
同时注意这几处输出的输出处理规则都不太一样
还是看第一处第二处,突破单引号:
尝试注释符处理后面的尾巴
<!--
:过滤器变为\<!--
,多了一个\无法处理//
:过滤器变为\/\/
,失去注释作用/*
:过滤器变为\/*
,多了一个\无法处理,同时即使没有\,单单/*的语法也不行尝试单引号闭合
'
:过滤器变为/'
。多了一个\无法处理
已有知识无解,换条路,尝试用<!--<js>
突破js后标签把下面两个输入点也拉入战局,寻求机会。
思来想去,怼到极致还是死在/上,同时满足不了语法。
多次突破js标签
换个思路,我们既然可以逃出一个js标签,那是不是可以把标签全部逃光,然后自己写标签?
那要求我们最后一个输出点不过滤,即:可以自己构造一个被解析的js标签,但是这里最后一个输出点就恰好只过滤双引号不过滤其他的。
Writeup:
但实际上可以调换位置,把<js >
直接作为外面的js标签解析,可以短一点:
内容包括一些敏感的危险标签和属性就会执行过滤,拒绝执行。
那么绕过思路也对应着在使输出内容跟输入内容不一致,绕过黑名单的检测,历史上也有很多针对谷歌浏览器的绕过方式。
回到这题本身,除了用一些通用的绕过方式,还可以根据当前过滤环境给出如下香草的payload:
使用模板字符串的思路
请做完第九题,第十题再回来这边
做完第十题,再回过头会发现这题跟第十题的情况是相似的,都是第三个输出点存在截断。
那么这题应该也是可以使用模板字符串的思路,直接使用第十题的payload看看:
那么问题肯定是出在其他地方,对比下和第10题的返回页面,就会发现区别在于缺少</js>
。
在不修改结构的情况下,想办法引入一个即可。
其他老哥的payload:
模板字符串的嵌套:
`abc${`def`}`
//abcdef
闭合大括号的方式:对象等
基础payload一样一砸,会发现这题跟第八题输出点、过滤规则大致都一致。回顾第八题我们思考了两种思路去解决:
突破第一个
</js>
标签,把所有输入点拉入战局,尝试在JS中构造一个合法的语句。但是无法因为总是多出\
无法解决突破全部
</js>
标签,自己重新起一个js标签,然后写入语句,成功
再看这里的输出点过滤,对于之前利用处的第四处输出同样进行了html编码,这样我们就无法引入一个<js>
标签,进行弹框。
同时还在第三处第四处进行了长度限制,只允许20个字符显示,那就要硬怼我们刚才对不过去的第一条路。
回顾我们之所以做不过去的原因:我们所有列出的注释符号都会引入\
进行注释;我们尝试闭合也会因为\
。那么有没有不被注释的可以利用的多行注释呢,其实是有的:模板字符串。
但是我们在利用的时候会发现有问题,先梳理payload需要满足的基本格式:
必须拥有闭合单引号的
\'
用于多行注释的
反引号
必须拥有的突破js标签的:
<!--<js >
:可以放在后面反引号中,也可以放前面单引号的闭合里面。同时需要提前注意在第三行第四行存在长度限制,我们是可以利用长度限制,去吃掉我们payload后面不想要的字符的。
接下来来根据当前环境确定payload中需要拥有的反引号
数量,这很关键,其实我们可以直接从闭合角度去确认数量,而不是无脑尝试,根据第一个跟第二个闭合,第三个跟第四个闭合的语法,可以分成两种情况:
payload中引入偶数个反引号:这明显不可能,因为偶数个的话在输出点就会行程,本行跟本行的闭合跟我们设想的跨行闭合背道而驰,直接PASS
payload中引入奇数个反引号:1,3,5,奇数个反引号会现在内部闭合,留下一个单引号到下一行闭合,这样就形成了跨行。但是我们会发现奇+奇=偶,这样前两行就会完成全部的闭合,第二行到第三行中间乱七八糟的字符就只能暴露出来,形成报错。相当于能跨行,但是幅度还是不够。
其实可以使用:模板字符串的嵌套
`abc${`def`}`
//abcdef
发现所需要用到的反引号、$、{、**}**恰好均不会被注释,很棒,进行尝试,老样子先定闭合,我们有4行输出,想到两种闭合形式:
递归闭合,即第一行的第一个前反引号,到最后一行的最后一个后引号才完成闭合,整个行程一个整体
前后行闭合,即这行的前反引号,在后一行的开头即闭合,上下行行程闭环。
我们先看一下递归分析:
`abc${ `d${ `e${ `f` }` }` }`
//abcdef
//左反引号4个,右反引号4个
//左开口3个,右闭口3个
根据各行的特性,大致拟定下左侧开口和闭合数量
问题在第二行直接暴露,我们只考虑左右闭合的话,中间的语法问题根本没法解决。
同时也可以知道因为反引号数量是比开头闭口数量要多的,上一行的开头左反引号进入到下一行会打乱布局。
我们换前后行闭合的思路尝试解决这个问题:
前后行闭合:
出现了最后一行的问题最后的语法没法闭合,尝试用长度去截断:
闭合了,但是出现了我们没法在大括号这里写入语句,简而言之就是多出了一个大括号。
其实我们在梳理闭合反引号和闭合大括号数量的时候就发现了这个数量不一致的问题,最后会导致语法错误。
所以我们必须要引入什么其他语法结构来闭合大括号。
函数、语法、对象闭合大括号
function(){}
、if(){}
、{a:123}
,出题者WP给出了JS语法中三种闭合大括号的方式。由于前两者会引入一些语法结构问题,而对象会比较简单,也可以作为表达式的一部分。所以选取对象结构来进行括号填补。
至此 构建payload的所有要素就齐全了,引入对象,把右边的模板字符串写成对象的形式好了:
var px=''-alert(1)<js>'px'
拉过去浏览器!失败…没弹框…
仔细看看代码会发现存在一个闭合问题:
那么在这个位置加上闭合,那么考虑第一行的语法情况,在前面也需要把大括号闭合了,再引入一个对象:
看了下发现跟官方WP的答案如出一辙…不亏虽然没看答案但是一路偷看思路的我…
看看其他老哥的WP
<js>
var px='12345';
var px1624='12345';
</js>
give me xss by pass~10
<div style="display:none">12</div>
<div style="display:none">12345</div>
<!--px-->
<js>
"px"
</js>
知识点:
延续之前知识点,知识构造方法不同
跟之前的应该变化不大,先用之前的payload探探路:
第三处限制输出2个字符
第四处不限制输出字符数
仔细观察第九题的payload输出结果,会发现当前payload输出实际上变成了同一个payload在三个地方输出,知识第三个地方的过滤规则跟之前两处不同,前面是转义,后面是html编码
这种情景就陷入了我们之前说的三行之间前后行闭合的情况:
''-{a:``}-{a:`${`
`}`}-{a:`${`
`}`}-{a:`${`
之前的前后行闭合还可以使用长度截断来改变最后语法,从而突破。但是这里我们没法动,因为我们只要改动payload,想在第三行正常结束,那么他就会在第二行结束。因为两者输出是一样的。
也不是完全一样的,因为这两者之间过滤规则不同。
那么我们是否可以找到一个payload,利用过滤规则,让JS第二行正常闭合,开启,但是在第三行完成语法修正,比如…注释符?
让截断处起到改变语法的作用
换条思路,我们的目的就是为了让第四行改变语法,想办法利用起第三行的被截取的两个字符来改变语法。
比如提前闭合对象,从而释放出之后的第四行的原来被关在模板字符串里面的注释符//。
那么我们的payload就要以反引号}
开头,根据这个要求修改payload再把弹框和占位都去掉,看一下:
看看其他师傅payload
11、12题应参考国外老哥题目
payload参考原题解
最短payload参考huuu
<h3>alert(11) to win</h3>
<br>
<js>
eval(location.pathname)
</js>
知识点:
使用%2f让浏览器不会吞掉多余路径
eval执行location.pathname
,直接输出看下location的内容:
%2f突破浏览器作妖
根据浏览器不URL解码%2f,而NGINX服务器会把URL编码%2f
解码为/
的特性差来进行绕过。
只有NGINX服务器会解码URL,IIS/APACHE服务器不会解码。
只需要把/
编码成%2f
即可绕过(第一次做的时候,直接这个地方就趴下死了)
然后根据报错慢慢修改,这里..%2f
语法报错了,注释掉,再修改payload:
会发现如果把//
理解为我们理想中的原目录原地跳就会发现多了一个..%2f
,什么地方不对劲。
假设服务器把//
理解成一个子目录,需要往上层跳,会发现也不对劲,2个//
一个/alert(1)/
一共是多了三个子目录,但是我们只有2个..%2f
测试:
在明文路径(如/xsstest11/)左侧的
//
解释为原地跳在最后的明文路径(如/alert(1))右侧的
//
理解为子目录,非常的神奇
但是规律应该不是通用的。
来到了变量没定义这一关,看似死局,因为我们没法修改前面的路由
…???????
好像完全可以修改前面的路由,然后注释后面的全部代码,利用..%2f
再构造正常路由就可以了:
<h3>alert(12) to win</h3>
<br>
<js>
if(/\/xsstest12\//.test(location.pathname.substr(0,17))){
eval(location.pathname)
}
</js>
知识点:
JS语言声明变量时变量提升的特性
var的另一种赋值方式
新增了一个正则,要求我们的路由前17位必须包含/xsstest12/
回到了解决xsstest12变量不存在的问题
变量提升特性
通常而言我们知道,一个变量应该先声明,后使用,假如不这样就会被教做人,比如python:
而JS却不是,参考说明:
JavaScript 变量的另一个不同寻常的地方是,你可以先使用变量稍后再声明变量而不会引发异常。这一概念称为变量提升;JavaScript 变量感觉上是被“提升”或移到了函数或语句的最前面。但是,提升后的变量将返回 undefined 值。因此在使用或引用某个变量之后进行声明和初始化操作,这个被提升的变量仍将返回 undefined 值。
那么实际上只需要在后面继续声明变量就可以了:
/test//xsstest11/alert(23);var xsstest11=1;//..%2f..%2f..%2f..%2f
但是var xsstest12
这个地方的空格在URL中会被编码成%20,然后传入JS中。JS是无法解析的。
JS中替代空格,换一下变成注释符/**/
,然后补齐路由即可:
进一步简短,就要从赋值入手了。
var[a]=[1] 解构赋值
非常神奇的赋值方式:
尝试过在各个网站领域去搜索这种赋值方式,但是没有收获,决定厚着脸皮去问下原题作者:
emmm…
又跟px1642老哥交流了下,发现官方其实有这种赋值方式的说明…叫做解构赋值:
进一步缩短,虽然会运行报错,但是不影响
噢…好像是弹12…但是这不重要emmm
✋热门推荐